Skip to content

Commit

Permalink
Merge pull request #418 from robdmoore/tortoise-and-vs-diff
Browse files Browse the repository at this point in the history
Adding currently running Visual Studio and Tortoise Git diff tools
  • Loading branch information
josephwoodward committed Jan 12, 2017
2 parents 785598a + ca48424 commit 55f6828
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,13 @@ public class KnownDiffTools
[JetBrains.Annotations.UsedImplicitlyAttribute()]
public readonly Shouldly.Configuration.DiffTool CodeCompare;
[JetBrains.Annotations.UsedImplicitlyAttribute()]
public readonly Shouldly.Configuration.DiffTool CurrentVisualStudio;
[JetBrains.Annotations.UsedImplicitlyAttribute()]
public readonly Shouldly.Configuration.DiffTool KDiff3;
[JetBrains.Annotations.UsedImplicitlyAttribute()]
public readonly Shouldly.Configuration.DiffTool P4Merge;
[JetBrains.Annotations.UsedImplicitlyAttribute()]
public readonly Shouldly.Configuration.DiffTool TortoiseGitMerge;
public KnownDiffTools() { }
public static Shouldly.Configuration.KnownDiffTools Instance { get; }
}
Expand Down
2 changes: 1 addition & 1 deletion src/Shouldly/Configuration/DiffTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DiffTool
public DiffTool(string name, string path, ArgumentGenerator argGenerator)
{
Name = name;
_path = Path.IsPathRooted(path) && File.Exists(path) ? path : Discover(path);
_path = path == null ? null : (Path.IsPathRooted(path) && File.Exists(path) ? path : Discover(path));
_argGenerator = argGenerator;
}

Expand Down
12 changes: 12 additions & 0 deletions src/Shouldly/Configuration/KnownDiffTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public class KnownDiffTools
public readonly DiffTool CodeCompare = new DiffTool("Code Compare", @"Devart\Code Compare\CodeMerge.exe", CodeCompareArgs);
[UsedImplicitly]
public readonly DiffTool P4Merge = new DiffTool("P4Merge", @"Perforce\p4merge.exe", P4MergeArgs);
[UsedImplicitly]
public readonly DiffTool TortoiseGitMerge = new DiffTool("Tortoise Git Merge", @"TortoiseGit\bin\TortoiseGitMerge.exe", TortoiseGitMergeArgs);
[UsedImplicitly]
public readonly DiffTool CurrentVisualStudio = new CurrentlyRunningVisualStudioDiffTool();

public static KnownDiffTools Instance { get; } = new KnownDiffTools();

Expand Down Expand Up @@ -45,6 +49,14 @@ static string P4MergeArgs(string received, string approved, bool approvedExists)

return $"\"{approved}\" \"{approved}\" \"{received}\" \"{approved}\"";
}

static string TortoiseGitMergeArgs(string received, string approved, bool approvedExists)
{
if (!approvedExists)
File.AppendAllText(approved, string.Empty);

return $"\"{received}\" \"{approved}\"";
}
}
}
#endif
192 changes: 192 additions & 0 deletions src/Shouldly/Configuration/VisualStudioDiffTool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#if ShouldMatchApproved
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace Shouldly.Configuration
{
internal class CurrentlyRunningVisualStudioDiffTool : DiffTool
{
public CurrentlyRunningVisualStudioDiffTool() : base("Current Visual Studio", GetPath(), VisualStudioArgs) {}

static string VisualStudioArgs(string received, string approved, bool approvedExists)
{
if (!approvedExists)
File.AppendAllText(approved, string.Empty);

return $"/diff \"{received}\" \"{approved}\"";
}

/*
Copyright (c) Llewellyn Falco. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions
and limitations under the License.
*/
// https://github.com/approvals/ApprovalTests.Net/blob/master/ApprovalTests/Reporters/VisualStudioReporter.cs
private static string GetPath()
{
var processAndParent = ParentProcessUtils.CurrentProcessWithAncestors().ToArray();

Process process = null;

try
{
process = processAndParent.FirstOrDefault(x => x.MainModule.FileName.EndsWith("devenv.exe"));
}
catch (Exception)
{
// Any exception means we are not working in this environment.
return null;
}

if (process != null)
{
var processModule = process.MainModule;
var version = processModule.FileVersionInfo.FileMajorPart;
if (11 <= version)
{
return processModule.FileName;
}
}

return null;
}

/*
Copyright (c) Llewellyn Falco. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you
may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions
and limitations under the License.
*/
// https://github.com/approvals/ApprovalTests.Net/blob/master/ApprovalTests/Utilities/ParentProcessUtils.cs
static class ParentProcessUtils
{
public static Process GetParentProcess(Process currentProcess)
{
return ParentProcess(currentProcess);
}

public static IEnumerable<Process> CurrentProcessWithAncestors()
{
return GetSelfAndAncestors(Process.GetCurrentProcess());
}

static IEnumerable<Process> GetSelfAndAncestors(Process self)
{
var processIds = new HashSet<int>();

var process = self;
while (process != null)
{
yield return process;

if (processIds.Contains(process.Id))
{
// loop detected (parent id have been re-allocated to a child process!)
yield break;
}
processIds.Add(process.Id);

process = ParentProcess(process);
}
}


private static Process ParentProcess(Process process)
{
var parentPid = 0;
var processPid = process.Id;
const uint TH32_CS_SNAPPROCESS = 2;
// Take snapshot of processes
var hSnapshot = CreateToolhelp32Snapshot(TH32_CS_SNAPPROCESS, 0);
if (hSnapshot == IntPtr.Zero)
{
return null;
}

var procInfo = new PROCESSENTRY32 { dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32)) };


// Read first
if (Process32First(hSnapshot, ref procInfo) == false)
{
return null;
}

// Loop through the snapshot
do
{
// If it's me, then ask for my parent.
if (processPid == procInfo.th32ProcessID)
{
parentPid = (int)procInfo.th32ParentProcessID;
}
} while (parentPid == 0 && Process32Next(hSnapshot, ref procInfo)); // Read next

if (parentPid <= 0)
{
return null;
}

try
{
return Process.GetProcessById(parentPid);
}
catch (ArgumentException)
{
//Process with an Id of X is not running
return null;
}
}

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessId);

[DllImport("kernel32.dll")]
private static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);


[DllImport("kernel32.dll")]
private static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);

[StructLayout(LayoutKind.Sequential)]
private struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
};
}
}
}
#endif

0 comments on commit 55f6828

Please sign in to comment.