diff --git a/src/Runtime/XSharp.VFP.Tests/DirectoryTests.prg b/src/Runtime/XSharp.VFP.Tests/DirectoryTests.prg new file mode 100644 index 0000000000..c3580ae9f4 --- /dev/null +++ b/src/Runtime/XSharp.VFP.Tests/DirectoryTests.prg @@ -0,0 +1,137 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// +USING System +USING System.IO +USING XUnit + +BEGIN NAMESPACE XSharp.VFP.Tests + + CLASS DirectoryTests + STATIC CONSTRUCTOR + XSharp.RuntimeState.Dialect := XSharpDialect.FoxPro + END CONSTRUCTOR + + STATIC PRIVATE METHOD NewTempDir() AS STRING + LOCAL cDir AS STRING + cDir := System.IO.Path.Combine(System.IO.Path.GetTempPath(), "VfpDirectoryTest_" + Guid.NewGuid():ToString("N")) + System.IO.Directory.CreateDirectory(cDir) + RETURN cDir + END METHOD + + STATIC PRIVATE METHOD Hide(cDir AS STRING) AS VOID + LOCAL oInfo AS System.IO.DirectoryInfo + oInfo := System.IO.DirectoryInfo{cDir} + oInfo:Attributes := (System.IO.FileAttributes) _OR((DWORD) System.IO.FileAttributes.Directory, ; + (DWORD) System.IO.FileAttributes.Hidden) + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryFindsAnExistingDirectory() AS VOID + LOCAL cDir AS STRING + cDir := NewTempDir() + TRY + Assert.True(Directory(cDir)) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryAcceptsATrailingSeparator() AS VOID + LOCAL cDir AS STRING + cDir := NewTempDir() + TRY + Assert.True(Directory(cDir + System.IO.Path.DirectorySeparatorChar:ToString())) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryReturnsFalseForAMissingDirectory() AS VOID + Assert.False(Directory(System.IO.Path.Combine(System.IO.Path.GetTempPath(), "VfpDirectoryTest_" + Guid.NewGuid():ToString("N")))) + END METHOD + + // a file is not a directory, VFP reports .F. for it + [Fact, Trait("Category", "File")]; + METHOD DirectoryReturnsFalseForAFile() AS VOID + LOCAL cDir AS STRING + LOCAL cFile AS STRING + cDir := NewTempDir() + cFile := System.IO.Path.Combine(cDir, "afile.txt") + System.IO.File.WriteAllText(cFile, "x") + TRY + Assert.False(Directory(cFile)) + FINALLY + System.IO.File.Delete(cFile) + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryReturnsFalseForAnEmptyName() AS VOID + Assert.False(Directory("")) + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryHidesAHiddenDirectoryByDefault() AS VOID + LOCAL cDir AS STRING + cDir := NewTempDir() + Hide(cDir) + TRY + Assert.False(Directory(cDir)) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryFindsAHiddenDirectoryWithFlagOne() AS VOID + LOCAL cDir AS STRING + cDir := NewTempDir() + Hide(cDir) + TRY + Assert.True(Directory(cDir, 1)) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + // only nFlags 1 ignores the attributes, 2 behaves like 0 + [Fact, Trait("Category", "File")]; + METHOD DirectoryTreatsFlagsOtherThanOneAsZero() AS VOID + LOCAL cDir AS STRING + cDir := NewTempDir() + Hide(cDir) + TRY + Assert.False(Directory(cDir, 2)) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + // Windows marks a drive root Hidden and System, VFP still reports it + [Fact, Trait("Category", "File")]; + METHOD DirectoryFindsADriveRoot() AS VOID + Assert.True(Directory(System.IO.Path.GetPathRoot(Environment.SystemDirectory))) + END METHOD + + [Fact, Trait("Category", "File")]; + METHOD DirectoryResolvesARelativeName() AS VOID + LOCAL cName AS STRING + LOCAL cDir AS STRING + cName := "VfpDirectoryTest_" + Guid.NewGuid():ToString("N") + cDir := System.IO.Path.Combine(Environment.CurrentDirectory, cName) + System.IO.Directory.CreateDirectory(cDir) + TRY + Assert.True(Directory(cName)) + FINALLY + System.IO.Directory.Delete(cDir) + END TRY + END METHOD + + END CLASS +END NAMESPACE diff --git a/src/Runtime/XSharp.VFP/RuntimeCoreWrappers.prg b/src/Runtime/XSharp.VFP/RuntimeCoreWrappers.prg index 1240700d3c..b0ad52b09f 100644 --- a/src/Runtime/XSharp.VFP/RuntimeCoreWrappers.prg +++ b/src/Runtime/XSharp.VFP/RuntimeCoreWrappers.prg @@ -23,7 +23,34 @@ FUNCTION File(cFileSpec AS STRING) AS LOGIC /// [FoxProFunction("DIRECTORY", FoxFunctionCategory.FileAndIO, FoxEngine.RuntimeCore, FoxFunctionStatus.Full, FoxCriticality.High)]; FUNCTION Directory(cDirectoryName AS STRING, nFlags := 0 AS INT) AS LOGIC - RETURN XSharp.RT.Functions.ALen(XSharp.RT.Functions.Directory(cDirectoryName)) > 0 + LOCAL lFound := FALSE AS LOGIC + LOCAL cFullPath AS STRING + LOCAL oAttributes AS System.IO.FileAttributes + + IF String.IsNullOrWhiteSpace(cDirectoryName) + RETURN FALSE + ENDIF + + TRY + // SET DEFAULT syncs the process directory in the FoxPro dialect, so a + // relative name resolves against the VFP default directory + cFullPath := System.IO.Path.GetFullPath(cDirectoryName) + IF System.IO.Directory.Exists(cFullPath) + // only nFlags 1 ignores the attributes, and Windows marks a drive + // root Hidden and System while VFP still reports it + IF nFlags == 1 .OR. String.Compare(cFullPath, System.IO.Path.GetPathRoot(cFullPath), TRUE) == 0 + lFound := TRUE + ELSE + oAttributes := System.IO.File.GetAttributes(cFullPath) + lFound := ! (oAttributes:HasFlag(System.IO.FileAttributes.Hidden) .OR. ; + oAttributes:HasFlag(System.IO.FileAttributes.System)) + ENDIF + ENDIF + CATCH + lFound := FALSE + END TRY + + RETURN lFound /// [FoxProFunction("GETENV", FoxFunctionCategory.EnvironmentAndSystem, FoxEngine.RuntimeCore, FoxFunctionStatus.Full, FoxCriticality.Medium)];