From 6cf3bdfb7e173d98933be0ae75a4a85b0968c84e Mon Sep 17 00:00:00 2001 From: Dom Slee Date: Mon, 20 May 2024 18:45:49 +1000 Subject: [PATCH] Improve test reliability and fix workstation domain trust issue --- .github/workflows/release.yaml | 19 ++++++-- Directory.Build.props | 1 + ForceOps.Lib/ForceOps.Lib.csproj | 2 +- ForceOps.Test/ForceOps.Test.csproj | 1 - .../src/ListFileOrDirectoryLocksTest.cs | 4 +- ForceOps.Test/src/ProgramTest.cs | 4 +- ForceOps.Test/src/TestUtil.cs | 46 ++----------------- ForceOps.Test/src/TestUtilStdout.cs | 26 +++++++++++ ForceOps.Test/src/WrappedProcess.cs | 2 +- ForceOps/src/ForceOps.cs | 2 +- 10 files changed, 54 insertions(+), 53 deletions(-) create mode 100644 ForceOps.Test/src/TestUtilStdout.cs diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0cbbd7c..3b10c60 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -43,10 +43,21 @@ jobs: run: dotnet --info - name: Pack - run: dotnet pack -c release /p:ContinuousIntegrationBuild=true + run: | + dotnet pack -c release /p:ContinuousIntegrationBuild=true + cp -r nupkg nupkg_pack + dotnet clean - name: Build (AOT) - run: dotnet publish -c release /p:UseAot=1 /p:ContinuousIntegrationBuild=true + run: | + dotnet publish -c release /p:UseAot=1 /p:ContinuousIntegrationBuild=true + cp -r bin bin_aot + dotnet clean + + - name: Build (release) + run: | + dotnet publish -c release /p:ContinuousIntegrationBuild=true + cp -r nupkg nupkg_release - name: Create release if: ${{ github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)') }} @@ -54,10 +65,10 @@ jobs: with: draft: true name: "${{ steps.get_version.outputs.version }}" - files: ForceOps/bin/Release/net8.0/win-x64/publish/ForceOps.exe + files: ForceOps/bin_aot/Release/net8.0/win-x64/publish/ForceOps.exe tag_name: "${{ steps.get_version.outputs.version }}" - name: Publish NuGet if: ${{ github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'chore(release)') }} run: | - dotnet nuget push -k ${{ secrets.NUGET_AUTH_TOKEN }} -s https://api.nuget.org/v3/index.json ForceOps/nupkg/ForceOps.${{ steps.get_version.outputs.version }}.nupkg ForceOps.Lib/nupkg/ForceOps.Lib.${{ steps.get_version.outputs.version }}.nupkg --skip-duplicate + dotnet nuget push -k ${{ secrets.NUGET_AUTH_TOKEN }} -s https://api.nuget.org/v3/index.json ForceOps/nupkg_pack/ForceOps.${{ steps.get_version.outputs.version }}.nupkg ForceOps.Lib/nupkg_release/ForceOps.Lib.${{ steps.get_version.outputs.version }}.nupkg --skip-duplicate diff --git a/Directory.Build.props b/Directory.Build.props index 4b144dd..07f4cea 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,6 +9,7 @@ Dom Slee lock file directory force delete https://github.com/domsleee/forceops/blob/main/CHANGELOG.md + false net8.0 LatestMajor diff --git a/ForceOps.Lib/ForceOps.Lib.csproj b/ForceOps.Lib/ForceOps.Lib.csproj index f3a460f..70c9b51 100644 --- a/ForceOps.Lib/ForceOps.Lib.csproj +++ b/ForceOps.Lib/ForceOps.Lib.csproj @@ -11,7 +11,7 @@ - + diff --git a/ForceOps.Test/ForceOps.Test.csproj b/ForceOps.Test/ForceOps.Test.csproj index f3d07c9..ea8c938 100644 --- a/ForceOps.Test/ForceOps.Test.csproj +++ b/ForceOps.Test/ForceOps.Test.csproj @@ -1,7 +1,6 @@  - false true diff --git a/ForceOps.Test/src/ListFileOrDirectoryLocksTest.cs b/ForceOps.Test/src/ListFileOrDirectoryLocksTest.cs index 011519d..dc64429 100644 --- a/ForceOps.Test/src/ListFileOrDirectoryLocksTest.cs +++ b/ForceOps.Test/src/ListFileOrDirectoryLocksTest.cs @@ -1,6 +1,7 @@ using System.Text; using ForceOps.Lib; using static ForceOps.Test.TestUtil; +using static ForceOps.Test.TestUtilStdout; namespace ForceOps.Test; @@ -14,7 +15,7 @@ public class ListFileOrDirectoryLocksTest : IDisposable public void WorksForDirectory() { var testContext = new TestContext(); - using var launchedProcess = LaunchPowershellWithCommand(workingDirectory: tempDirectoryPath); + using var launchedProcess = LaunchProcessInDirectory(workingDirectory: tempDirectoryPath); new ListFileOrDirectoryLocks(testContext.forceOpsContext).PrintLocks(tempDirectoryPath); var stdoutString = GetStdoutString(stdoutStringBuilder); @@ -30,6 +31,7 @@ public void WorksForFile() new ListFileOrDirectoryLocks(testContext.forceOpsContext).PrintLocks(tempFilePath); var stdoutString = GetStdoutString(stdoutStringBuilder); + Assert.False(launchedProcess.process.HasExited); Assert.Equal($"ProcessId,ExecutableName,ApplicationName\r\n{launchedProcess.process.Id},powershell.exe,powershell.exe\r\n", stdoutString); } diff --git a/ForceOps.Test/src/ProgramTest.cs b/ForceOps.Test/src/ProgramTest.cs index e2b1ca3..1f07ce2 100644 --- a/ForceOps.Test/src/ProgramTest.cs +++ b/ForceOps.Test/src/ProgramTest.cs @@ -4,6 +4,7 @@ using ForceOps.Lib; using Moq; using static ForceOps.Test.TestUtil; +using static ForceOps.Test.TestUtilStdout; namespace ForceOps.Test; @@ -146,7 +147,8 @@ public void RelaunchedProgramUsesForceDelete() testContext.forceOpsContext.relaunchAsElevated = new RelaunchAsElevated() { verb = "", exeNameOverride = exeNameOverride }; var forceOps = new ForceOps(new[] { "delete", pathThatCanBeDeleted, tempDirectoryPath }, testContext.forceOpsContext); forceOps.extraRelaunchArgs = new List() { "--disable-elevate" }; - Assert.True(0 == forceOps.Run(), forceOps.caughtException?.ToString()); + var stdoutString = GetStdoutString(stdoutStringBuilder); + Assert.True(0 == forceOps.Run(), BuildFailMessage(testContext, forceOps, stdoutString)); Assert.True(!Directory.Exists(tempDirectoryPath), "Deleted by relaunch"); Assert.Contains("Unable to perform operation as an unelevated process. Retrying as elevated and logging to", testContext.fakeLoggerFactory.GetAllLogsString()); } diff --git a/ForceOps.Test/src/TestUtil.cs b/ForceOps.Test/src/TestUtil.cs index bc9c64b..5e77592 100644 --- a/ForceOps.Test/src/TestUtil.cs +++ b/ForceOps.Test/src/TestUtil.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Reactive.Disposables; -using System.Text; namespace ForceOps.Test; @@ -13,10 +12,10 @@ public static WrappedProcess LaunchProcessInDirectory(string workingDirectory) public static WrappedProcess HoldLockOnFileUsingPowershell(string filePath) { - return LaunchPowershellWithCommand(command: $"[System.IO.File]::Open('{filePath}', 'OpenOrCreate')"); + return LaunchPowershellWithCommand(command: $"$file = [System.IO.File]::Open('{filePath}', 'CreateNew')"); } - public static WrappedProcess LaunchPowershellWithCommand(string command = "", string workingDirectory = "") + static WrappedProcess LaunchPowershellWithCommand(string command = "", string workingDirectory = "") { var process = new Process { @@ -24,27 +23,7 @@ public static WrappedProcess LaunchPowershellWithCommand(string command = "", st { FileName = "powershell", WorkingDirectory = workingDirectory, - Arguments = $"-NoProfile -Command \"{command}; echo 'process has been loaded'; sleep 10000\"", - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; - - StartProcessUntilProcessHasBeenLoadedMessage(process); - - return new WrappedProcess(process); - } - - public static WrappedProcess LaunchCmdWithCommand(string command = "", string workingDirectory = "") - { - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "cmd", - WorkingDirectory = workingDirectory, - Arguments = $"/c \"{command}; echo process has been loaded && timeout /t 10 /nobreak\"", + Arguments = $"-NoProfile -Command \"$ErrorActionPreference='stop'; {command}; echo 'process has been loaded'; sleep 10000\"", RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true @@ -95,12 +74,6 @@ public static string GetTemporaryFileName() return Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); } - public static string GetStdoutString(StringBuilder stdoutStringBuilder) - { - Console.Out.Flush(); - return stdoutStringBuilder.ToString(); - } - public static IDisposable CreateTemporaryDirectory(string directory) { Directory.CreateDirectory(directory); @@ -113,17 +86,4 @@ public static IDisposable CreateTemporaryDirectory(string directory) catch { } }); } - - public static IDisposable RedirectStdout(StringBuilder stringBuilder) - { - var originalConsoleOut = Console.Out; - Console.Out.Flush(); - Console.SetOut(new StringWriter(stringBuilder)); - - return Disposable.Create(() => - { - Console.SetOut(originalConsoleOut); - Console.Out.Flush(); - }); - } } diff --git a/ForceOps.Test/src/TestUtilStdout.cs b/ForceOps.Test/src/TestUtilStdout.cs new file mode 100644 index 0000000..c64e95e --- /dev/null +++ b/ForceOps.Test/src/TestUtilStdout.cs @@ -0,0 +1,26 @@ +using System.Reactive.Disposables; +using System.Text; + +namespace ForceOps.Test; + +public static class TestUtilStdout +{ + public static string GetStdoutString(StringBuilder stdoutStringBuilder) + { + Console.Out.Flush(); + return stdoutStringBuilder.ToString(); + } + + public static IDisposable RedirectStdout(StringBuilder stringBuilder) + { + var originalConsoleOut = Console.Out; + Console.Out.Flush(); + Console.SetOut(new StringWriter(stringBuilder)); + + return Disposable.Create(() => + { + Console.SetOut(originalConsoleOut); + Console.Out.Flush(); + }); + } +} diff --git a/ForceOps.Test/src/WrappedProcess.cs b/ForceOps.Test/src/WrappedProcess.cs index 0044172..dc0c9e7 100644 --- a/ForceOps.Test/src/WrappedProcess.cs +++ b/ForceOps.Test/src/WrappedProcess.cs @@ -14,6 +14,6 @@ public WrappedProcess(Process process) void IDisposable.Dispose() { process.Kill(); - process.WaitForExit(1); + process.WaitForExit(); } } diff --git a/ForceOps/src/ForceOps.cs b/ForceOps/src/ForceOps.cs index ae6d7ec..9ec7e98 100644 --- a/ForceOps/src/ForceOps.cs +++ b/ForceOps/src/ForceOps.cs @@ -141,7 +141,7 @@ void RunWithRelaunchAsElevated(Action action, Func> buildArgsForRel var childProcessExitCode = forceOpsContext.relaunchAsElevated.RelaunchAsElevated(args, childOutputFile); if (childProcessExitCode != 0) { - throw new AggregateException($"Child process failed with {childProcessExitCode}."); + throw new AggregateException($"Child process failed with exit code {childProcessExitCode}."); } else {