From 8b9b69ec917c6c363f37cd1513f1bc98114cc359 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 20 Sep 2020 19:46:16 +1000 Subject: [PATCH] Add sync API (#95) --- docs/diff-tool.custom.md | 6 +- readme.md | 6 +- src/DiffEngine.Tests/DiffRunnerTests.cs | 90 +++++- src/DiffEngine.Tests/DiffToolsTest.cs | 6 +- src/DiffEngine/DiffRunner.cs | 259 +++++++++++------- src/DiffEngine/DiffRunner_Kill.cs | 35 +++ src/DiffEngine/DisabledChecker.cs | 13 + src/DiffEngine/MaxInstance.cs | 36 +++ src/DiffEngine/ResolvedTool.cs | 6 + src/DiffEngine/Tray/DiffEngineTray.cs | 34 ++- .../OptionsFormTests.Default.verified.png | Bin 14772 -> 14766 bytes .../OptionsFormTests.WithKeys.verified.png | Bin 14263 -> 14257 bytes src/DiffEngineTray.Tests/PiperTest.cs | 8 +- src/DiffEngineTray/PiperClient.cs | 87 +++++- src/Directory.Build.props | 2 +- 15 files changed, 453 insertions(+), 135 deletions(-) create mode 100644 src/DiffEngine/DiffRunner_Kill.cs create mode 100644 src/DiffEngine/DisabledChecker.cs create mode 100644 src/DiffEngine/MaxInstance.cs diff --git a/docs/diff-tool.custom.md b/docs/diff-tool.custom.md index b9ca2f27..0446331c 100644 --- a/docs/diff-tool.custom.md +++ b/docs/diff-tool.custom.md @@ -46,9 +46,9 @@ New tools are added to the top of the order, the last tool added will resolve be ```cs -await DiffRunner.Launch(tempFile, targetFile); +await DiffRunner.LaunchAsync(tempFile, targetFile); ``` -snippet source | anchor +snippet source | anchor Alternatively the instance returned from `AddTool*` can be used to explicitly launch that tool. @@ -61,7 +61,7 @@ var resolvedTool = DiffTools.AddToolBasedOn( name: "MyCustomDiffTool", arguments: (temp, target) => $"\"custom args {temp}\" \"{target}\""); -await DiffRunner.Launch(resolvedTool!, "PathToTempFile", "PathToTargetFile"); +await DiffRunner.LaunchAsync(resolvedTool!, "PathToTempFile", "PathToTargetFile"); ``` snippet source | anchor diff --git a/readme.md b/readme.md index a43e99eb..2b0f9791 100644 --- a/readme.md +++ b/readme.md @@ -82,9 +82,9 @@ A tool can be launched using the following: ```cs -await DiffRunner.Launch(tempFile, targetFile); +await DiffRunner.LaunchAsync(tempFile, targetFile); ``` -snippet source | anchor +snippet source | anchor Note that this method will respect the above [difference behavior](/docs/diff-tool.md#detected-difference-behavior) in terms of Auto refresh and MDI behaviors. @@ -99,7 +99,7 @@ A tool can be closed using the following: ```cs DiffRunner.Kill(file1, file2); ``` -snippet source | anchor +snippet source | anchor Note that this method will respect the above [difference behavior](/docs/diff-tool.md#detected-difference-behavior) in terms of MDI behavior. diff --git a/src/DiffEngine.Tests/DiffRunnerTests.cs b/src/DiffEngine.Tests/DiffRunnerTests.cs index a1649253..c6025c2b 100644 --- a/src/DiffEngine.Tests/DiffRunnerTests.cs +++ b/src/DiffEngine.Tests/DiffRunnerTests.cs @@ -14,6 +14,7 @@ public class DiffRunnerTests : string file2; string file1; string command; + [Fact(Skip = "Explicit")] public async Task MaxInstancesToLaunch() { @@ -22,11 +23,35 @@ public async Task MaxInstancesToLaunch() { await Task.Delay(500); ProcessCleanup.Refresh(); - var result = await DiffRunner.Launch(file1, "fake.txt"); + var result = DiffRunner.Launch(file1, "fake.txt"); await Task.Delay(300); Assert.Equal(LaunchResult.StartedNewInstance, result); ProcessCleanup.Refresh(); - result = await DiffRunner.Launch(file2, "fake.txt"); + result = DiffRunner.Launch(file2, "fake.txt"); + Assert.Equal(LaunchResult.TooManyRunningDiffTools, result); + ProcessCleanup.Refresh(); + DiffRunner.Kill(file1, "fake.txt"); + DiffRunner.Kill(file2, "fake.txt"); + } + finally + { + DiffRunner.MaxInstancesToLaunch(5); + } + } + + [Fact(Skip = "Explicit")] + public async Task MaxInstancesToLaunchAsync() + { + DiffRunner.MaxInstancesToLaunch(1); + try + { + await Task.Delay(500); + ProcessCleanup.Refresh(); + var result = await DiffRunner.LaunchAsync(file1, "fake.txt"); + await Task.Delay(300); + Assert.Equal(LaunchResult.StartedNewInstance, result); + ProcessCleanup.Refresh(); + result = await DiffRunner.LaunchAsync(file2, "fake.txt"); Assert.Equal(LaunchResult.TooManyRunningDiffTools, result); ProcessCleanup.Refresh(); DiffRunner.Kill(file1, "fake.txt"); @@ -45,30 +70,33 @@ async Task Launch() #region DiffRunnerLaunch - await DiffRunner.Launch(tempFile, targetFile); + await DiffRunner.LaunchAsync(tempFile, targetFile); #endregion } [Fact(Skip = "Explicit")] - public async Task Kill() + public async Task KillAsync() { - await DiffRunner.Launch(file1, file2); + await DiffRunner.LaunchAsync(file1, file2); ProcessCleanup.Refresh(); + #region DiffRunnerKill + DiffRunner.Kill(file1, file2); + #endregion } [Fact] - public async Task LaunchAndKillDisabled() + public void LaunchAndKillDisabled() { DiffRunner.Disabled = true; try { Assert.False(IsRunning()); Assert.False(ProcessCleanup.IsRunning(command)); - var result = await DiffRunner.Launch(file1, file2); + var result = DiffRunner.Launch(file1, file2); Assert.Equal(LaunchResult.Disabled, result); Thread.Sleep(500); ProcessCleanup.Refresh(); @@ -87,11 +115,55 @@ public async Task LaunchAndKillDisabled() } [Fact] - public async Task LaunchAndKill() + public async Task LaunchAndKillDisabledAsync() + { + DiffRunner.Disabled = true; + try + { + Assert.False(IsRunning()); + Assert.False(ProcessCleanup.IsRunning(command)); + var result = await DiffRunner.LaunchAsync(file1, file2); + Assert.Equal(LaunchResult.Disabled, result); + Thread.Sleep(500); + ProcessCleanup.Refresh(); + Assert.False(IsRunning()); + Assert.False(ProcessCleanup.IsRunning(command)); + DiffRunner.Kill(file1, file2); + Thread.Sleep(500); + ProcessCleanup.Refresh(); + Assert.False(IsRunning()); + Assert.False(ProcessCleanup.IsRunning(command)); + } + finally + { + DiffRunner.Disabled = false; + } + } + + [Fact] + public void LaunchAndKill() + { + Assert.False(IsRunning()); + Assert.False(ProcessCleanup.IsRunning(command)); + var result = DiffRunner.Launch(file1, file2); + Assert.Equal(LaunchResult.StartedNewInstance, result); + Thread.Sleep(500); + ProcessCleanup.Refresh(); + Assert.True(IsRunning()); + Assert.True(ProcessCleanup.IsRunning(command)); + DiffRunner.Kill(file1, file2); + Thread.Sleep(500); + ProcessCleanup.Refresh(); + Assert.False(IsRunning()); + Assert.False(ProcessCleanup.IsRunning(command)); + } + + [Fact] + public async Task LaunchAndKillAsync() { Assert.False(IsRunning()); Assert.False(ProcessCleanup.IsRunning(command)); - var result = await DiffRunner.Launch(file1, file2); + var result = await DiffRunner.LaunchAsync(file1, file2); Assert.Equal(LaunchResult.StartedNewInstance, result); Thread.Sleep(500); ProcessCleanup.Refresh(); diff --git a/src/DiffEngine.Tests/DiffToolsTest.cs b/src/DiffEngine.Tests/DiffToolsTest.cs index 37ade6a3..b7585797 100644 --- a/src/DiffEngine.Tests/DiffToolsTest.cs +++ b/src/DiffEngine.Tests/DiffToolsTest.cs @@ -20,7 +20,7 @@ public void MaxInstancesToLaunch() [Fact] public void AddTool() { - string diffToolPath = FakeDiffTool.Exe; + var diffToolPath = FakeDiffTool.Exe; #region AddTool var resolvedTool = DiffTools.AddTool( name: "MyCustomDiffTool", @@ -40,7 +40,7 @@ public void AddTool() [Fact] public void OrderShouldNotMessWithAddTool() { - string diffToolPath = FakeDiffTool.Exe; + var diffToolPath = FakeDiffTool.Exe; var resolvedTool = DiffTools.AddTool( name: "MyCustomDiffTool", autoRefresh: true, @@ -80,7 +80,7 @@ async Task AddToolAndLaunch() name: "MyCustomDiffTool", arguments: (temp, target) => $"\"custom args {temp}\" \"{target}\""); - await DiffRunner.Launch(resolvedTool!, "PathToTempFile", "PathToTargetFile"); + await DiffRunner.LaunchAsync(resolvedTool!, "PathToTempFile", "PathToTargetFile"); #endregion } diff --git a/src/DiffEngine/DiffRunner.cs b/src/DiffEngine/DiffRunner.cs index 2199d635..34cdab07 100644 --- a/src/DiffEngine/DiffRunner.cs +++ b/src/DiffEngine/DiffRunner.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Threading; using System.Threading.Tasks; using EmptyFiles; @@ -10,173 +10,226 @@ namespace DiffEngine /// /// Manages diff tools processes. /// - public static class DiffRunner + public static partial class DiffRunner { - static int maxInstancesToLaunch = GetMaxInstances(); - static int launchedInstances; + public static bool Disabled { get; set; } = DisabledChecker.IsDisable(); - public static bool Disabled { get; set; } = IsDisable(); - - static int GetMaxInstances() + public static void MaxInstancesToLaunch(int value) { - var variable = EnvironmentEx.GetEnvironmentVariable("DiffEngine_MaxInstances"); - if (string.IsNullOrEmpty(variable)) - { - return 5; - } - - if (ushort.TryParse(variable, out var result)) - { - return result; - } + Guard.AgainstNegativeAndZero(value, nameof(value)); + MaxInstance.Set(value); + } - throw new Exception($"Could not parse the DiffEngine_MaxInstances environment variable: {variable}"); + public static LaunchResult Launch(DiffTool tool, string tempFile, string targetFile) + { + GuardFiles(tempFile, targetFile); + return InnerLaunch( + (out ResolvedTool? resolved) => DiffTools.TryFind(tool, out resolved), + tempFile, + targetFile); } - static bool IsDisable() + public static Task LaunchAsync(DiffTool tool, string tempFile, string targetFile) { - var variable = EnvironmentEx.GetEnvironmentVariable("DiffEngine_Disabled"); - return string.Equals(variable, "true", StringComparison.OrdinalIgnoreCase) || - BuildServerDetector.Detected || - ContinuousTestingDetector.Detected; + GuardFiles(tempFile, targetFile); + + return InnerLaunchAsync( + (out ResolvedTool? resolved) => DiffTools.TryFind(tool, out resolved), + tempFile, + targetFile); } - public static void MaxInstancesToLaunch(int value) + /// + /// Launch a diff tool for the given paths. + /// + public static LaunchResult Launch(string tempFile, string targetFile) { - Guard.AgainstNegativeAndZero(value, nameof(value)); - maxInstancesToLaunch = value; + GuardFiles(tempFile, targetFile); + + return InnerLaunch( + (out ResolvedTool? tool) => + { + var extension = Extensions.GetExtension(tempFile); + return DiffTools.TryFind(extension, out tool); + }, + tempFile, + targetFile); } /// - /// Find and kill a diff tool process. + /// Launch a diff tool for the given paths. /// - public static void Kill(string tempFile, string targetFile) + public static Task LaunchAsync(string tempFile, string targetFile) { - if (Disabled) + GuardFiles(tempFile, targetFile); + + return InnerLaunchAsync( + (out ResolvedTool? tool) => + { + var extension = Extensions.GetExtension(tempFile); + return DiffTools.TryFind(extension, out tool); + }, + tempFile, + targetFile); + } + + public static LaunchResult Launch(ResolvedTool tool, string tempFile, string targetFile) + { + GuardFiles(tempFile, targetFile); + Guard.AgainstNull(tool, nameof(tool)); + + return InnerLaunch( + (out ResolvedTool? resolvedTool) => + { + resolvedTool = tool; + return true; + }, + tempFile, + targetFile); + } + + public static Task LaunchAsync(ResolvedTool tool, string tempFile, string targetFile) + { + GuardFiles(tempFile, targetFile); + Guard.AgainstNull(tool, nameof(tool)); + + return InnerLaunchAsync( + (out ResolvedTool? resolvedTool) => + { + resolvedTool = tool; + return true; + }, + tempFile, + targetFile); + } + + static LaunchResult InnerLaunch(TryResolveTool tryResolveTool, string tempFile, string targetFile) + { + if (ShouldExitLaunch(tryResolveTool, targetFile, out var tool, out var result)) { - return; + return result.Value; } - var extension = Extensions.GetExtension(tempFile); - if (!DiffTools.TryFind(extension, out var diffTool)) + tool.CommandAndArguments(tempFile, targetFile, out var arguments, out var command); + + if (ProcessCleanup.TryGetProcessInfo(command, out var processCommand)) { - Logging.Write($"Extension not found. {extension}"); - return; - } + if (tool.AutoRefresh) + { + DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, processCommand.Process); + return LaunchResult.AlreadyRunningAndSupportsRefresh; + } - var command = diffTool.BuildCommand(tempFile, targetFile); + KillIfMdi(tool, command); + } - if (diffTool.IsMdi) + if (MaxInstance.Reached()) { - Logging.Write($"DiffTool is Mdi so not killing. diffTool: {diffTool.ExePath}"); - return; + DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, null); + return LaunchResult.TooManyRunningDiffTools; } - ProcessCleanup.Kill(command); + var processId = LaunchProcess(tool, arguments); + + DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, !tool.IsMdi, processId); + + return LaunchResult.StartedNewInstance; } - public static Task Launch(DiffTool tool, string tempFile, string targetFile) + static async Task InnerLaunchAsync(TryResolveTool tryResolveTool, string tempFile, string targetFile) { - GuardFiles(tempFile, targetFile); + if (ShouldExitLaunch(tryResolveTool, targetFile, out var tool, out var result)) + { + return result.Value; + } - if (Disabled) + tool.CommandAndArguments(tempFile, targetFile, out var arguments, out var command); + + if (ProcessCleanup.TryGetProcessInfo(command, out var processCommand)) { - return Task.FromResult(LaunchResult.Disabled); + if (tool.AutoRefresh) + { + await DiffEngineTray.AddMoveAsync(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, processCommand.Process); + return LaunchResult.AlreadyRunningAndSupportsRefresh; + } + + KillIfMdi(tool, command); } - if (!DiffTools.TryFind(tool, out var resolvedTool)) + if (MaxInstance.Reached()) { - return Task.FromResult(LaunchResult.NoDiffToolFound); + await DiffEngineTray.AddMoveAsync(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, null); + return LaunchResult.TooManyRunningDiffTools; } - return Launch(resolvedTool, tempFile, targetFile); + var processId = LaunchProcess(tool, arguments); + + await DiffEngineTray.AddMoveAsync(tempFile, targetFile, tool.ExePath, arguments, !tool.IsMdi, processId); + + return LaunchResult.StartedNewInstance; } - /// - /// Launch a diff tool for the given paths. - /// - public static Task Launch(string tempFile, string targetFile) + static bool ShouldExitLaunch( + TryResolveTool tryResolveTool, + string targetFile, + [NotNullWhen(false)] out ResolvedTool? tool, + [NotNullWhen(true)] out LaunchResult? result) { - GuardFiles(tempFile, targetFile); if (Disabled) { - return Task.FromResult(LaunchResult.Disabled); + result = LaunchResult.Disabled; + tool = null; + return true; } - var extension = Extensions.GetExtension(tempFile); + if (!tryResolveTool(out tool)) + { + result = LaunchResult.NoDiffToolFound; + return true; + } - if (!DiffTools.TryFind(extension, out var diffTool)) + if (!TryCreate(tool, targetFile)) { - return Task.FromResult(LaunchResult.NoDiffToolFound); + result = LaunchResult.NoEmptyFileForExtension; + return true; } - return Launch(diffTool, tempFile, targetFile); + result = null; + return false; } - public static Task Launch(ResolvedTool tool, string tempFile, string targetFile) + static bool TryCreate(ResolvedTool tool, string targetFile) { - GuardFiles(tempFile, targetFile); - Guard.AgainstNull(tool, nameof(tool)); - if (Disabled) - { - return Task.FromResult(LaunchResult.Disabled); - } - var targetExists = File.Exists(targetFile); if (tool.RequiresTarget && !targetExists) { if (!AllFiles.TryCreateFile(targetFile, useEmptyStringForTextFiles: true)) { - return Task.FromResult(LaunchResult.NoEmptyFileForExtension); + return false; } } - return InnerLaunch(tool, tempFile, targetFile); + return true; } - static async Task InnerLaunch(ResolvedTool tool, string tempFile, string targetFile) + static int LaunchProcess(ResolvedTool tool, string arguments) { - var arguments = tool.Arguments(tempFile, targetFile); - var command = tool.BuildCommand(tempFile, targetFile); - if (ProcessCleanup.TryGetProcessInfo(command, out var processCommand)) - { - if (tool.AutoRefresh) - { - await DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, processCommand.Process); - return LaunchResult.AlreadyRunningAndSupportsRefresh; - } - - if (!tool.IsMdi) - { - ProcessCleanup.Kill(command); - } - } - - var instanceCount = Interlocked.Increment(ref launchedInstances); - if (instanceCount > maxInstancesToLaunch) - { - await DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, tool.IsMdi!, null); - return LaunchResult.TooManyRunningDiffTools; - } - try { var startInfo = new ProcessStartInfo(tool.ExePath, arguments) { UseShellExecute = true }; - using var process = Process.Start(startInfo); - if (process == null) + if (process != null) { - var message = $@"Failed to launch diff tool. -{tool.ExePath} {arguments}"; - throw new Exception(message); + return process.Id; } - await DiffEngineTray.AddMove(tempFile, targetFile, tool.ExePath, arguments, !tool.IsMdi, process.Id); - return LaunchResult.StartedNewInstance; + var message = $@"Failed to launch diff tool. +{tool.ExePath} {arguments}"; + throw new Exception(message); } catch (Exception exception) { @@ -186,10 +239,20 @@ static async Task InnerLaunch(ResolvedTool tool, string tempFile, } } + static void KillIfMdi(ResolvedTool tool, string command) + { + if (!tool.IsMdi) + { + ProcessCleanup.Kill(command); + } + } + static void GuardFiles(string tempFile, string targetFile) { Guard.FileExists(tempFile, nameof(tempFile)); Guard.AgainstNullOrEmpty(targetFile, nameof(targetFile)); } + + delegate bool TryResolveTool([NotNullWhen(true)] out ResolvedTool? resolved); } } \ No newline at end of file diff --git a/src/DiffEngine/DiffRunner_Kill.cs b/src/DiffEngine/DiffRunner_Kill.cs new file mode 100644 index 00000000..4b0916bb --- /dev/null +++ b/src/DiffEngine/DiffRunner_Kill.cs @@ -0,0 +1,35 @@ +using EmptyFiles; + +namespace DiffEngine +{ + public static partial class DiffRunner + { + /// + /// Find and kill a diff tool process. + /// + public static void Kill(string tempFile, string targetFile) + { + if (Disabled) + { + return; + } + + var extension = Extensions.GetExtension(tempFile); + if (!DiffTools.TryFind(extension, out var diffTool)) + { + Logging.Write($"Extension not found. {extension}"); + return; + } + + var command = diffTool.BuildCommand(tempFile, targetFile); + + if (diffTool.IsMdi) + { + Logging.Write($"DiffTool is Mdi so not killing. diffTool: {diffTool.ExePath}"); + return; + } + + ProcessCleanup.Kill(command); + } + } +} \ No newline at end of file diff --git a/src/DiffEngine/DisabledChecker.cs b/src/DiffEngine/DisabledChecker.cs new file mode 100644 index 00000000..20fbbbd1 --- /dev/null +++ b/src/DiffEngine/DisabledChecker.cs @@ -0,0 +1,13 @@ +using System; +using DiffEngine; + +static class DisabledChecker +{ + public static bool IsDisable() + { + var variable = EnvironmentEx.GetEnvironmentVariable("DiffEngine_Disabled"); + return string.Equals(variable, "true", StringComparison.OrdinalIgnoreCase) || + BuildServerDetector.Detected || + ContinuousTestingDetector.Detected; + } +} \ No newline at end of file diff --git a/src/DiffEngine/MaxInstance.cs b/src/DiffEngine/MaxInstance.cs new file mode 100644 index 00000000..c8500850 --- /dev/null +++ b/src/DiffEngine/MaxInstance.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; + +static class MaxInstance +{ + static int maxInstancesToLaunch = GetMaxInstances(); + static int launchedInstances; + + static int GetMaxInstances() + { + var variable = EnvironmentEx.GetEnvironmentVariable("DiffEngine_MaxInstances"); + if (string.IsNullOrEmpty(variable)) + { + return 5; + } + + if (ushort.TryParse(variable, out var result)) + { + return result; + } + + throw new Exception($"Could not parse the DiffEngine_MaxInstances environment variable: {variable}"); + } + + public static void Set(int value) + { + Guard.AgainstNegativeAndZero(value, nameof(value)); + maxInstancesToLaunch = value; + } + + public static bool Reached() + { + var instanceCount = Interlocked.Increment(ref launchedInstances); + return instanceCount > maxInstancesToLaunch; + } +} \ No newline at end of file diff --git a/src/DiffEngine/ResolvedTool.cs b/src/DiffEngine/ResolvedTool.cs index 4f384855..ac3069ae 100644 --- a/src/DiffEngine/ResolvedTool.cs +++ b/src/DiffEngine/ResolvedTool.cs @@ -16,6 +16,12 @@ public class ResolvedTool public bool RequiresTarget { get; } public bool SupportsText { get; } + internal void CommandAndArguments(string tempFile, string targetFile, out string arguments, out string command) + { + arguments = Arguments(tempFile, targetFile); + command = $"\"{ExePath}\" {arguments}"; + } + public string BuildCommand(string tempFile, string targetFile) { return $"\"{ExePath}\" {Arguments(tempFile, targetFile)}"; diff --git a/src/DiffEngine/Tray/DiffEngineTray.cs b/src/DiffEngine/Tray/DiffEngineTray.cs index a084294b..88fae94c 100644 --- a/src/DiffEngine/Tray/DiffEngineTray.cs +++ b/src/DiffEngine/Tray/DiffEngineTray.cs @@ -16,17 +16,43 @@ static DiffEngineTray() public static bool IsRunning { get; } - public static Task AddDelete(string file, CancellationToken cancellation = default) + public static void AddDelete(string file) + { + if (!IsRunning) + { + return; + } + + PiperClient.SendDelete(file); + } + + public static void AddMove( + string tempFile, + string targetFile, + string exe, + string arguments, + bool canKill, + int? processId) + { + if (!IsRunning) + { + return; + } + + PiperClient.SendMove(tempFile, targetFile, exe, arguments, canKill, processId); + } + + public static Task AddDeleteAsync(string file, CancellationToken cancellation = default) { if (!IsRunning) { return Task.CompletedTask; } - return PiperClient.SendDelete(file, cancellation); + return PiperClient.SendDeleteAsync(file, cancellation); } - public static Task AddMove( + public static Task AddMoveAsync( string tempFile, string targetFile, string exe, @@ -40,7 +66,7 @@ public static Task AddMove( return Task.CompletedTask; } - return PiperClient.SendMove(tempFile, targetFile, exe, arguments, canKill, processId, cancellation); + return PiperClient.SendMoveAsync(tempFile, targetFile, exe, arguments, canKill, processId, cancellation); } } } \ No newline at end of file diff --git a/src/DiffEngineTray.Tests/OptionsFormTests.Default.verified.png b/src/DiffEngineTray.Tests/OptionsFormTests.Default.verified.png index 18a167c828e4202be49063a2c68fbd7dc4000f8c..861293a9f91f3b4432b1e867484a02e49639d357 100644 GIT binary patch delta 448 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 { }, s => received = s, source.Token); - await PiperClient.SendDelete("Foo", source.Token); + await PiperClient.SendDeleteAsync("Foo", source.Token); await Task.Delay(1000); source.Cancel(); await task; @@ -30,7 +30,7 @@ public async Task Move() MovePayload received = null!; var source = new CancellationTokenSource(); var task = PiperServer.Start(s => received = s, s => { }, source.Token); - await PiperClient.SendMove("Foo", "Bar", "theExe", "TheArguments \"s\"", true, 10, source.Token); + await PiperClient.SendMoveAsync("Foo", "Bar", "theExe", "TheArguments \"s\"", true, 10, source.Token); await Task.Delay(1000); source.Cancel(); await task; @@ -45,8 +45,8 @@ public async Task SendOnly() await File.WriteAllTextAsync(file, "a"); try { - await PiperClient.SendMove(file, file, "theExe", "TheArguments \"s\"", true, 10); - await PiperClient.SendDelete(file); + await PiperClient.SendMoveAsync(file, file, "theExe", "TheArguments \"s\"", true, 10); + await PiperClient.SendDeleteAsync(file); } catch (InvalidOperationException) { diff --git a/src/DiffEngineTray/PiperClient.cs b/src/DiffEngineTray/PiperClient.cs index 388ec436..61829589 100644 --- a/src/DiffEngineTray/PiperClient.cs +++ b/src/DiffEngineTray/PiperClient.cs @@ -8,19 +8,40 @@ static class PiperClient { - public static Task SendDelete( + public static void SendDelete(string file) + { + Send(BuildDeletePayload(file)); + } + + public static Task SendDeleteAsync( string file, CancellationToken cancellation = default) { - var payload = $@"{{ + var payload = BuildDeletePayload(file); + return SendAsync(payload, cancellation); + } + + static string BuildDeletePayload(string file) + { + return $@"{{ ""Type"":""Delete"", ""File"":""{file}"" }} "; - return Send(payload, cancellation); } - public static Task SendMove( + public static void SendMove( + string tempFile, + string targetFile, + string exe, + string arguments, + bool canKill, + int? processId) + { + Send(BuildMovePayload(tempFile, targetFile, exe, arguments, canKill, processId)); + } + + public static Task SendMoveAsync( string tempFile, string targetFile, string exe, @@ -28,6 +49,12 @@ public static Task SendMove( bool canKill, int? processId, CancellationToken cancellation = default) + { + var payload = BuildMovePayload(tempFile, targetFile, exe, arguments, canKill, processId); + return SendAsync(payload, cancellation); + } + + static string BuildMovePayload(string tempFile, string targetFile, string exe, string arguments, bool canKill, int? processId) { var builder = new StringBuilder($@"{{ ""Type"":""Move"", @@ -44,28 +71,68 @@ public static Task SendMove( } builder.Append('}'); - return Send(builder.ToString(), cancellation); + return builder.ToString(); } - static async Task Send(string payload, CancellationToken cancellation = default) + static void Send(string payload) { try { - await InnerSend(payload, cancellation); + InnerSend(payload); } catch (Exception exception) { - Trace.WriteLine($@"Failed to send payload to DiffEngineTray. + HandleSendException(payload, exception); + } + } + + static async Task SendAsync(string payload, CancellationToken cancellation = default) + { + try + { + await InnerSendAsync(payload, cancellation); + } + catch (Exception exception) + { + HandleSendException(payload, exception); + } + } + + static void HandleSendException(string payload, Exception exception) + { + Trace.WriteLine($@"Failed to send payload to DiffEngineTray. Payload: {payload} Exception: {exception}"); - } } - static async Task InnerSend(string payload, CancellationToken cancellation) + static void InnerSend(string payload) + { +#if(NETSTANDARD2_1) + using var pipe = new NamedPipeClientStream( + ".", + "DiffEngine", + PipeDirection.Out, + PipeOptions.CurrentUserOnly); + using var stream = new StreamWriter(pipe); + pipe.Connect(1000); + stream.Write(payload.AsMemory()); +#else + using var pipe = new NamedPipeClientStream( + ".", + "DiffEngine", + PipeDirection.Out, + PipeOptions.None); + using var stream = new StreamWriter(pipe); + pipe.Connect(1000); + stream.Write(payload); +#endif + } + + static async Task InnerSendAsync(string payload, CancellationToken cancellation) { #if(NETSTANDARD2_1) await using var pipe = new NamedPipeClientStream( diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 3a485e05..1e027797 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CS1591;CS0649 - 5.4.4 + 6.0.0 1.0.0 Json, Testing, Verify, Snapshot, Approvals Enables simple verification of complex models and documents.