Skip to content

Commit

Permalink
add HashVerify and HashProp unit tests + Appveyor
Browse files Browse the repository at this point in the history
  • Loading branch information
gurnec committed Aug 24, 2016
1 parent 7cdcc0a commit a152a3a
Show file tree
Hide file tree
Showing 16 changed files with 743 additions and 7 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/HashCheck.sdf
/HashCheck.opensdf
*.vcxproj.user
*.csproj.user
*.suo
/Bin
/Obj
/installer/*.exe
/installer/*.exe.sha*
/*.VC.db
/*.VC.VC.opendb
/packages
/UnitTests/bin
/UnitTests/obj
/UnitTests/vectors
19 changes: 19 additions & 0 deletions HashCheck.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A3CCE2E5-7BD9-4381-9D92-BD06D96EA49B}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
appveyor.yml = appveyor.yml
installer\HashCheck.nsi = installer\HashCheck.nsi
license.txt = license.txt
Expand All @@ -13,22 +14,40 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HashCheck", "HashCheck.vcxproj", "{DCAD938F-9032-4AF9-BA67-8C0CF309D986}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{5BD2397A-F10B-4847-8814-E539FB04AC8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Debug|Any CPU.ActiveCfg = Debug|x64
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Debug|Win32.ActiveCfg = Debug|Win32
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Debug|Win32.Build.0 = Debug|Win32
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Debug|x64.ActiveCfg = Debug|x64
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Debug|x64.Build.0 = Debug|x64
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Release|Any CPU.ActiveCfg = Release|x64
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Release|Win32.ActiveCfg = Release|Win32
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Release|Win32.Build.0 = Release|Win32
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Release|x64.ActiveCfg = Release|x64
{DCAD938F-9032-4AF9-BA67-8C0CF309D986}.Release|x64.Build.0 = Release|x64
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|Win32.ActiveCfg = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|Win32.Build.0 = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|x64.ActiveCfg = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Debug|x64.Build.0 = Debug|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|Any CPU.Build.0 = Release|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|Win32.ActiveCfg = Release|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|Win32.Build.0 = Release|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|x64.ActiveCfg = Release|Any CPU
{5BD2397A-F10B-4847-8814-E539FB04AC8F}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 2 additions & 0 deletions HashCheckResources.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define IDD_HASHSAVE 200

// Hash calculation property sheet and controls
// (some of these must be copied to HashProp.cs)
#define IDD_HASHPROP 300
#define IDC_RESULTS 301
#define IDC_STATUSBOX 302
Expand All @@ -25,6 +26,7 @@
#define IDC_OPTIONS 306

// Hash verification dialog and controls
// (some of these must be copied to HashVerify.cs)
#define IDD_HASHVERF 400
#define IDC_LIST 401
#define IDC_SUMMARY 402
Expand Down
201 changes: 201 additions & 0 deletions UnitTests/HashProp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/**
* HashProp unit tests
* Copyright (C) 2016 Christopher Gurnee. All rights reserved.
*
* Please refer to readme.md for information about this source code.
* Please refer to license.txt for details about distribution and modification.
**/

using Xunit;
using TestStack.White;
using TestStack.White.UIItems;
using TestStack.White.UIItems.WindowItems;
using TestStack.White.UIItems.Finders;
using System;
using System.IO;
using System.Diagnostics;
using System.Windows.Automation;
using Microsoft.Win32;
using static UnitTests.ShellExecuteEx;

namespace UnitTests
{
public class HashProp
{
// from HashCheckResources.h
const string IDC_RESULTS = "301";
const string IDC_STATUSBOX = "302";

// Relative path from the class .dll to the test vectors
const string RELATIVE_PATH_PREFIX = @"..\..\vectors\";


static string PATH_PREFIX;
static bool is_com_surrogate;
//
Window OpenHashPropWindow(string filename)
{
// Combine the path to the class .dll with the relative path to the test vectors
if (PATH_PREFIX == null)
PATH_PREFIX = Path.Combine(
Path.GetDirectoryName(
Uri.UnescapeDataString(
new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).AbsolutePath
)
),
RELATIVE_PATH_PREFIX
);

// Open the Checksums tab in the shell file properties sheet
SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO();
sei.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(sei);
sei.fMask = 0x0000010C; // SEE_MASK_INVOKEIDLIST | SEE_MASK_NOASYNC
sei.lpVerb = "Properties";
sei.lpFile = Path.Combine(PATH_PREFIX, filename);
sei.lpParameters = "Checksums";
sei.nShow = 1; // SW_SHOWNORMAL
ShellExecuteExW(ref sei);

TestStack.White.Configuration.CoreAppXmlConfiguration.Instance.FindWindowTimeout = 1500; // 1.5 sec.

// Find the file properties sheet window
Window prop_window = null;

// Prior to Windows 10, the file properties sheet opens in the curent process
if (! is_com_surrogate)
{
Application app = Application.Attach(Process.GetCurrentProcess());
try
{
prop_window = app.GetWindow(filename + " Properties");
is_com_surrogate = false;
}
catch (AutomationException) { }
}

// Starting with Windows 10, the file properties sheet is hosted in a dllhost COM surrogate
if (prop_window == null)
{
foreach (Process proc in Process.GetProcessesByName("dllhost"))
{
Application app = Application.Attach(proc);
try
{
prop_window = app.GetWindow(filename + " Properties");
break;
}
catch (AutomationException) { }
}
Assert.NotNull(prop_window);
is_com_surrogate = true;
}

return prop_window;
}


void VerifyResults(Window prop_window, string expected_results)
{
// Wait for hashing to complete (status contains a summary instead of "Progress")
GroupBox status = prop_window.Get<GroupBox>(SearchCriteria.ByNativeProperty(
AutomationElement.AutomationIdProperty, IDC_STATUSBOX));
while (status.Name == "Progress")
System.Threading.Thread.Sleep(100);

// Verify the results
TextBox results = prop_window.Get<TextBox>(SearchCriteria.ByNativeProperty(
AutomationElement.AutomationIdProperty, IDC_RESULTS));
Assert.Equal(expected_results, results.BulkText, true, true, true); // ignores case, line endings and whitespace changes
}


[Theory]
[InlineData(
"SHA256LongMsg.rsp-0000.dat", false,
@" File: SHA256LongMsg.rsp-0000.dat
CRC-32: 29154b1f
SHA-1: a0f8b5c82a0ee5a9119251cd75c55c0ae350923d
SHA-256: 3c593aa539fdcdae516cdf2f15000f6634185c88f505b39775fb9ab137a10aa2
SHA-512: ae4b5efe9d7e301699dc03ec1bc1f60f83565be9a40d8e38c660a7f9463b22b1ed907f9f4024c9d0ba30c9c38ac12d8cc354d8c4539e63dd6f26a0ce9ce7b71a
" + "\n",
@" File: SHA256LongMsg.rsp-0000.dat
CRC-32: 29154b1f
MD5: 632250fa8b3f9d248e0d37044460a4b7
SHA-1: a0f8b5c82a0ee5a9119251cd75c55c0ae350923d
SHA-256: 3c593aa539fdcdae516cdf2f15000f6634185c88f505b39775fb9ab137a10aa2
SHA-512: ae4b5efe9d7e301699dc03ec1bc1f60f83565be9a40d8e38c660a7f9463b22b1ed907f9f4024c9d0ba30c9c38ac12d8cc354d8c4539e63dd6f26a0ce9ce7b71a
SHA3-256: aae3e14fd718aed758574f170c23a2f172a3690587340aa3c5c8d538ebd66bd4
SHA3-512: 2620b58e343e8a80e091405b65ced686c58c3996854e092d01202dc91598d596aea148131c77513a4ae7f509800abcdb363c45864ef2e291206cd56775ff3e46
" + "\n")]
[InlineData(
"SHA3_VeryLongMsg.dat", true,
@" File: SHA3_VeryLongMsg.dat
CRC-32: 4ed80f64
SHA-1: 6ca7cca8ac206ec5a82a612ee5d4ba6ac766d239
SHA-256: 8716fbf9a5f8c4562b48528e2d3085b64c56b5d1169ccf3295ad03e805580676
SHA-512: 421b072b4fda96eb569ae55b8a9a5b4b5073a623649bd409dbb999e527372994b3a1a91f53c719837868c7fe11bba67640143255a3fbc5c895d2119274b0caff
" + "\n",
@" File: SHA3_VeryLongMsg.dat
CRC-32: 4ed80f64
MD5: 1553faf40e3618f9b902c1e559552828
SHA-1: 6ca7cca8ac206ec5a82a612ee5d4ba6ac766d239
SHA-256: 8716fbf9a5f8c4562b48528e2d3085b64c56b5d1169ccf3295ad03e805580676
SHA-512: 421b072b4fda96eb569ae55b8a9a5b4b5073a623649bd409dbb999e527372994b3a1a91f53c719837868c7fe11bba67640143255a3fbc5c895d2119274b0caff
SHA3-256: 6a934f386ff779e33a1068c5f3e4c5c0a117968be4264b8f80ec511a1c0b6eed
SHA3-512: d11fbca35e2383481bd99253a289c035e7a98a36507e4feabf8151fc51e6d77c2f737c4bd747362896ab61df2c066e6a27e7fa2f5bf645b54d98e07e135c4870
" + "\n")]
// BUG: only works for the English translation
public void HashesTest(string name, bool interrupt, string def_expected_results, string full_expected_results)
{
// Reset the selected checksums back to the default
RegistryKey reg_key = Registry.CurrentUser.OpenSubKey("SOFTWARE");
reg_key = reg_key.OpenSubKey("HashCheck", true); // (true == with-write-access)
if (reg_key != null)
reg_key.DeleteValue("Checksums", false); // (false == no-exceptions-if-not-found)

Window prop_window = OpenHashPropWindow(name);
try
{
VerifyResults(prop_window, def_expected_results);

// If we should try to interrupt hash calculation in-progress, restart it
// (VerifyResults() above waits for completion, the following does not)
if (interrupt)
{
prop_window.Close();
prop_window = null;
prop_window = OpenHashPropWindow(name);
}

// Open the options dialog
prop_window.Get<Button>("Options").Click();
Window options_window = null;
while (true)
{
foreach (Window mw in prop_window.ModalWindows())
{
if (mw.Name.StartsWith("HashCheck Options"))
{
options_window = mw;
goto options_window_found;
}
}
System.Threading.Thread.Sleep(0);
}
options_window_found:

// Enable all the hash option checkboxes and click OK
foreach (CheckBox cb in options_window.GetMultiple(SearchCriteria.ByControlType(ControlType.CheckBox)))
cb.State = ToggleState.On;
options_window.Get<Button>("OK").Click();

VerifyResults(prop_window, full_expected_results);
}
finally
{
if (prop_window != null)
prop_window.Close();
}
}
}
}
102 changes: 102 additions & 0 deletions UnitTests/HashVerify.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* HashVerify unit tests
* Copyright (C) 2016 Christopher Gurnee. All rights reserved.
*
* Please refer to readme.md for information about this source code.
* Please refer to license.txt for details about distribution and modification.
**/

using Xunit;
using TestStack.White;
using TestStack.White.UIItems;
using TestStack.White.UIItems.Finders;
using System;
using System.IO;
using System.Diagnostics;
using System.Windows.Automation;

namespace UnitTests
{
public class HashVerifySetup
{
// Relative path from the class .dll to the test vectors
const string RELATIVE_PATH_PREFIX = @"..\..\vectors\";
internal string PATH_PREFIX;

internal Application app;

public HashVerifySetup()
{
// Combine the path to the class .dll with the relative path to the test vectors
PATH_PREFIX = Path.Combine(
Path.GetDirectoryName(
Uri.UnescapeDataString(
new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).AbsolutePath
)
),
RELATIVE_PATH_PREFIX
);

Process proc = Process.GetCurrentProcess();
app = Application.Attach(proc);

// Workaround required when debugging unit tests in the VS GUI -
// for some reason the first time this is called the DLL loads but HashVerify doesn't start
if (Debugger.IsAttached && proc.MainModule.ModuleName.StartsWith("vstest.executionengine."))
Process.Start(Path.Combine(PATH_PREFIX, "SHA3_256ShortMsg.rsp.sha3-256"));
}
}

public class HashVerify : IClassFixture<HashVerifySetup>
{
// from HashCheckResources.h
const string IDC_MATCH_RESULTS = "404";
const string IDC_PENDING_RESULTS = "410";

HashVerifySetup common;

public HashVerify(HashVerifySetup c)
{
common = c;
}

[Theory]
[InlineData("SHA1ShortMsg.rsp.sha1")]
[InlineData("SHA1LongMsg.rsp.sha1")]
[InlineData("SHA256ShortMsg.rsp.sha256")]
[InlineData("SHA256LongMsg.rsp.sha256")]
[InlineData("SHA512ShortMsg.rsp.sha512")]
[InlineData("SHA512LongMsg.rsp.sha512")]
[InlineData("SHA3_256ShortMsg.rsp.sha3-256")]
[InlineData("SHA3_256LongMsg.rsp.sha3-256")]
[InlineData("SHA3_512ShortMsg.rsp.sha3-512")]
[InlineData("SHA3_512LongMsg.rsp.sha3-512")]
[InlineData("hashcheck.md5")]
[InlineData("hashcheck.sha1")]
public void NistTest(string name)
{
// This opens a HashCheck Verify window in the current process
Process.Start(Path.Combine(common.PATH_PREFIX, name));

var verify_window = common.app.GetWindow(name);
try
{
// Wait for hashing to complete (there are 0 pending results)
var re0 = new System.Text.RegularExpressions.Regex(@"\b0\b");
Label pending_results = verify_window.Get<Label>(SearchCriteria.ByNativeProperty(
AutomationElement.AutomationIdProperty, IDC_PENDING_RESULTS));
while (! re0.IsMatch(pending_results.Text))
System.Threading.Thread.Sleep(0);

// On success, the hash matches should be "# of #" where the #'s are the same
Label match_results = verify_window.Get<Label>(SearchCriteria.ByNativeProperty(
AutomationElement.AutomationIdProperty, IDC_MATCH_RESULTS));
Assert.Matches(@"\b(\d{2,3})\b.*\b\1\b", match_results.Text);
}
finally
{
verify_window.Close();
}
}
}
}
Loading

0 comments on commit a152a3a

Please sign in to comment.