Skip to content

Commit

Permalink
Added KotzUtilities helper class
Browse files Browse the repository at this point in the history
  • Loading branch information
Kaoticz committed May 16, 2024
1 parent 2af3fb7 commit cdb70e0
Show file tree
Hide file tree
Showing 12 changed files with 734 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<VersionPrefix>2.2.2</VersionPrefix>
<VersionPrefix>2.3.0</VersionPrefix>
<Authors>Kotz</Authors>
<Copyright>Copyright © Kotz 2022</Copyright>
<PackageProjectUrl>https://github.com/Kaoticz/Kotz.Utilities</PackageProjectUrl>
Expand Down
404 changes: 404 additions & 0 deletions Kotz.Extensions/KotzUtilities.cs

Large diffs are not rendered by default.

17 changes: 16 additions & 1 deletion Kotz.Extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,19 @@ Defines the following extension methods:
- ToTitleCase: Converts the current string to the *Title Case* format.
- **StringBuilder Extensions**
- ReplaceAll: Replaces all instances of a substring with another substring, even if the new substring is a substring of the old substring.
- ToStringAndClear: Returns the `string` value of the current builder and clears the builder.
- ToStringAndClear: Returns the `string` value of the current builder and clears the builder.

Defines the following types:

- **KotzUtilities**: static class with a wide range of helper methods.
- AddPathToPATHEnvar: Adds a directory path to the PATH environment variable.
- HasWritePermissionAt: Checks if this application can write to the specified directory.
- ProgramExists: Checks if a program exists at the specified absolute path or the PATH environment variable.
- StartProcess: Starts the specified program in the background.
- TryCreate: Safely creates an object with the specified factory method.
- TryDeleteFSO: Safely deletes a file or directory.
- TryDeleteFile: Safely deletes a file.
- TryDeleteDirectory: Safely deletes a directory.
- TryMoveFSO: Safely moves a file or directory.
- TryMoveFile: Safely moves a file.
- TryMoveDirectory: Safely moves a directory.
4 changes: 2 additions & 2 deletions Kotz.Tests/Events/EventAsyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Kotz.Tests.Events;

public sealed class EventAyncTests
public sealed class EventAsyncTests
{
private readonly AsyncEvent<EventAyncTests, EventArgs> _asyncEvent = new();
private readonly AsyncEvent<EventAsyncTests, EventArgs> _asyncEvent = new();

internal int Count { get; private set; }

Expand Down
25 changes: 25 additions & 0 deletions Kotz.Tests/Extensions/Utilities/HasWritePermissionAtTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class HasWritePermissionAtTests
{
[Fact]
internal void HasWritePermissionAtTrueTest()
=> Assert.True(KotzUtilities.HasWritePermissionAt(AppContext.BaseDirectory));

[Fact]
internal void HasWritePermissionAtFalseTest()
{
var directoryUri = OperatingSystem.IsWindows()
? Environment.GetFolderPath(Environment.SpecialFolder.System)
: "/";

Assert.False(KotzUtilities.HasWritePermissionAt(directoryUri));
}

[Fact]
internal void HasWritePermissionAtFailTest()
{
var fakeUri = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.Personal), Guid.NewGuid().ToString());
Assert.Throws<DirectoryNotFoundException>(() => KotzUtilities.HasWritePermissionAt(fakeUri));
}
}
16 changes: 16 additions & 0 deletions Kotz.Tests/Extensions/Utilities/ProgramExistsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class ProgramExistsTests
{
[Theory]
[InlineData(true, "echo")]
[InlineData(false, "abcde")]
internal void ProgramExistsSuccessTest(bool expected, string command)
=> Assert.Equal(expected, KotzUtilities.ProgramExists(command));

[Theory]
[InlineData("")]
[InlineData(null)]
internal void ProgramExistsFailTest(string? command)
=> Assert.ThrowsAny<ArgumentException>(() => KotzUtilities.ProgramExists(command!));
}
29 changes: 29 additions & 0 deletions Kotz.Tests/Extensions/Utilities/StartProgramTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.ComponentModel;

namespace Kotz.Tests.Extensions.Utilities;

public sealed class StartProgramTests
{
[Fact]
internal void StartProgramSuccessTest()
{
using var process1 = KotzUtilities.StartProcess("echo", "Hello from xUnit!");
using var process2 = KotzUtilities.StartProcess("echo", ["Hello from xUnit!"]);

Assert.NotNull(process1);
Assert.NotNull(process2);
}

[Fact]
internal void StartProgramFailTest()
{
Assert.Throws<Win32Exception>(() => KotzUtilities.StartProcess("idonotexist"));
Assert.Throws<Win32Exception>(() => KotzUtilities.StartProcess("idonotexist", ["args"]));
Assert.Throws<ArgumentException>(() => KotzUtilities.StartProcess("", (string?)null!));
Assert.Throws<ArgumentException>(() => KotzUtilities.StartProcess("", Enumerable.Empty<string>()));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.StartProcess(null!, ""));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.StartProcess(null!, Enumerable.Empty<string>()));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.StartProcess("idonotexist", (string?)null!));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.StartProcess("idonotexist", (IEnumerable<string>?)null!));
}
}
22 changes: 22 additions & 0 deletions Kotz.Tests/Extensions/Utilities/TryCreateTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text;

namespace Kotz.Tests.Extensions.Utilities;

public sealed class TryCreateTests
{
[Fact]
internal void TryCreateSuccessTest()
{
Assert.True(KotzUtilities.TryCreate(() => new StringBuilder(), out var actualResult, out var exception));
Assert.IsType<StringBuilder>(actualResult);
Assert.Null(exception);
}

[Fact]
internal void TryCreateFailTest()
{
Assert.False(KotzUtilities.TryCreate<StringBuilder>(() => throw new InvalidOperationException(), out var actualResult, out var exception));
Assert.IsType<InvalidOperationException>(exception);
Assert.Null(actualResult);
}
}
40 changes: 40 additions & 0 deletions Kotz.Tests/Extensions/Utilities/TryDeleteDirectoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class TryDeleteDirectoryTests
{
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryDeleteDirectorySuccessTest(bool createDirectory, bool expected)
{
var directoryPath = CreateDirectoryPath(createDirectory);

Assert.Equal(createDirectory, Directory.Exists(directoryPath));
Assert.Equal(expected, KotzUtilities.TryDeleteDirectory(directoryPath));

if (createDirectory)
Assert.Equal(!createDirectory, Directory.Exists(directoryPath));
}

[Fact]
internal void TryDeleteDirectoryFailTest()
{
Assert.Throws<ArgumentException>(() => KotzUtilities.TryDeleteDirectory(string.Empty));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryDeleteDirectory(null!));
}

/// <summary>
/// Creates a random directory path.
/// </summary>
/// <param name="createDirectory"><see langword="true"/> if the directory should be created, <see langword="false"/> otherwise.</param>
/// <returns>The path to the directory.</returns>
internal static string CreateDirectoryPath(bool createDirectory)
{
var directoryPath = Path.Join(Path.GetTempPath(), Path.GetRandomFileName());

if (createDirectory)
Directory.CreateDirectory(directoryPath).Create();

return directoryPath;
}
}
40 changes: 40 additions & 0 deletions Kotz.Tests/Extensions/Utilities/TryDeleteFileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class TryDeleteFileTests
{
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryDeleteFileSuccessTest(bool createFile, bool expected)
{
var filePath = CreateFilePath(createFile);

Assert.Equal(createFile, File.Exists(filePath));
Assert.Equal(expected, KotzUtilities.TryDeleteFile(filePath));

if (createFile)
Assert.Equal(!createFile, File.Exists(filePath));
}

[Fact]
internal void TryDeleteFileFailTest()
{
Assert.Throws<ArgumentException>(() => KotzUtilities.TryDeleteFile(string.Empty));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryDeleteFile(null!));
}

/// <summary>
/// Creates a random file path.
/// </summary>
/// <param name="createFile"><see langword="true"/> if the file should be created, <see langword="false"/> otherwise.</param>
/// <returns>The path to the file.</returns>
internal static string CreateFilePath(bool createFile)
{
var filePath = Path.Join(Path.GetTempPath(), Path.GetRandomFileName() + ".tmp");

if (createFile)
File.Create(filePath).Dispose();

return filePath;
}
}
86 changes: 86 additions & 0 deletions Kotz.Tests/Extensions/Utilities/TryMoveDirectoryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class TryMoveDirectoryTests
{
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryMoveDirectoryRenameSuccessTests(bool createdirectory, bool expected)
{
var oldPath = TryDeleteDirectoryTests.CreateDirectoryPath(createdirectory);
var newPath = TryDeleteDirectoryTests.CreateDirectoryPath(false);

Assert.Equal(createdirectory, Directory.Exists(oldPath));
Assert.Equal(expected, KotzUtilities.TryMoveDirectory(oldPath, newPath));

if (!createdirectory)
return;

Assert.Equal(!createdirectory, Directory.Exists(oldPath));
Assert.False(KotzUtilities.TryDeleteDirectory(oldPath));
Assert.True(KotzUtilities.TryDeleteDirectory(newPath));
Assert.False(Directory.Exists(newPath));
}

[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryMoveDirectorySuccessTests(bool createDirectory, bool expected)
{
var oldPath = TryDeleteDirectoryTests.CreateDirectoryPath(createDirectory);
var newPath = Path.Join(TryDeleteDirectoryTests.CreateDirectoryPath(false), Path.GetFileName(oldPath));

Assert.Equal(createDirectory, Directory.Exists(oldPath));
Assert.Equal(expected, KotzUtilities.TryMoveDirectory(oldPath, newPath));

if (!createDirectory)
return;

Assert.Equal(!createDirectory, Directory.Exists(oldPath));
Assert.False(KotzUtilities.TryDeleteDirectory(oldPath));
Assert.True(KotzUtilities.TryDeleteDirectory(newPath));
Assert.False(File.Exists(newPath));
}

[Fact]
internal void TryMoveDirectoryVolumeTest()
{
// Define the directory tree
var oldRootDirPath = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetRandomFileName()); // Root directory
var oldDir1Path = Path.Join(oldRootDirPath, Path.GetRandomFileName()); // In: oldRootDirPath > oldDir1Path
var oldFile1Path = Path.Join(oldRootDirPath, Path.GetRandomFileName() + ".tmp"); // Inside: oldRootDirPath
var oldDir2Path = Path.Join(oldRootDirPath, Path.GetRandomFileName()); // In: oldRootDirPath > oldDir2Path
var oldFile2Path = Path.Join(oldDir2Path, Path.GetRandomFileName() + ".tmp"); // Inside: oldRootDirPath > oldDir2Path

// Create the directory tree
Directory.CreateDirectory(oldDir1Path);
Directory.CreateDirectory(oldDir2Path);
File.Create(oldFile1Path).Dispose();
File.Create(oldFile2Path).Dispose();

// Move to a different volume (ie. "/tmp")
var newRootDirPath = Path.Join(Path.GetTempPath(), Path.GetFileName(oldRootDirPath));
Assert.True(KotzUtilities.TryMoveDirectory(oldRootDirPath, newRootDirPath));

// Check if move was successful
Assert.False(Directory.Exists(oldRootDirPath));
Assert.True(Directory.Exists(newRootDirPath));
Assert.True(Directory.Exists(Path.Join(newRootDirPath, Path.GetFileName(oldDir1Path))));
Assert.True(Directory.Exists(Path.Join(newRootDirPath, Path.GetFileName(oldDir2Path))));
Assert.True(File.Exists(Path.Join(newRootDirPath, Path.GetFileName(oldFile1Path))));
Assert.True(File.Exists(Path.Join(newRootDirPath, Path.GetFileName(oldDir2Path), Path.GetFileName(oldFile2Path))));

// Cleanup
Directory.Delete(newRootDirPath, true);
Assert.False(Directory.Exists(newRootDirPath));
}

[Fact]
internal void TryMoveDirectoryFailTests()
{
Assert.Throws<ArgumentException>(() => KotzUtilities.TryMoveDirectory(string.Empty, "not empty"));
Assert.Throws<ArgumentException>(() => KotzUtilities.TryMoveDirectory("not empty", string.Empty));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryMoveDirectory(null!, "not empty"));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryMoveDirectory("not empty", null!));
}
}
53 changes: 53 additions & 0 deletions Kotz.Tests/Extensions/Utilities/TryMoveFileTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace Kotz.Tests.Extensions.Utilities;

public sealed class TryMoveFileTests
{
[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryMoveFileRenameSuccessTests(bool createFile, bool expected)
{
var oldPath = TryDeleteFileTests.CreateFilePath(createFile);
var newPath = TryDeleteFileTests.CreateFilePath(false);

Assert.Equal(createFile, File.Exists(oldPath));
Assert.Equal(expected, KotzUtilities.TryMoveFile(oldPath, newPath));

if (!createFile)
return;

Assert.Equal(!createFile, File.Exists(oldPath));
Assert.False(KotzUtilities.TryDeleteFile(oldPath));
Assert.True(KotzUtilities.TryDeleteFile(newPath));
Assert.False(File.Exists(newPath));
}

[Theory]
[InlineData(true, true)]
[InlineData(false, false)]
internal void TryMoveFileSuccessTests(bool createFile, bool expected)
{
var oldPath = TryDeleteFileTests.CreateFilePath(createFile);
var newPath = Path.Join(TryDeleteDirectoryTests.CreateDirectoryPath(false), Path.GetFileName(oldPath));

Assert.Equal(createFile, File.Exists(oldPath));
Assert.Equal(expected, KotzUtilities.TryMoveFile(oldPath, newPath));

if (!createFile)
return;

Assert.Equal(!createFile, File.Exists(oldPath));
Assert.False(KotzUtilities.TryDeleteFile(oldPath));
Assert.True(KotzUtilities.TryDeleteFile(newPath));
Assert.False(File.Exists(newPath));
}

[Fact]
internal void TryMoveFileFailTests()
{
Assert.Throws<ArgumentException>(() => KotzUtilities.TryMoveFile(string.Empty, "not empty"));
Assert.Throws<ArgumentException>(() => KotzUtilities.TryMoveFile("not empty", string.Empty));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryMoveFile(null!, "not empty"));
Assert.Throws<ArgumentNullException>(() => KotzUtilities.TryMoveFile("not empty", null!));
}
}

0 comments on commit cdb70e0

Please # to comment.