[PTRun]New plugin: Value generator with hashing functions (#26097)

* Add hashing plugin for Run

* Cleanup logic for hasher plugin

- The IComputeRequest interface should make it easier to implement new
  generators in the future
- The GUID generator can now generate all versions of GUID (a.k.a. UUID)
- The input for the hash functions is not quite right. The
  Wox.Plugin.Query class doesn't actually have a way to ge the raw query
  as given by the user. The issue is with multiple spaces. An input like
  "a a  a    a    a" would only be accessible as "a a a a a" using the
  Query class.
  So for now, hashing only works correctly if the input
  doesn't contain multiple consecutive spaces.
- Need a way to make clear the usage for generating GUIDv3 and v5, since
  they take 2 parameters. There are defaults, but they aren't very
  clear.

* Change plugin name to ValueGenerator

* Clean up error handling for the input parser

* Add result type and description to subtitle

* Change the icons

* Add Base64 encoding and unit tests

This commit adds Base64 encoding as a utility of the value generator
plugin. The command is `# base64 ***input***`.

Also added unit tests for the UUID/GUID generator and for the input
parser. I don't think tests are necessary for the hashing functions
or for the base64 encoder, since those were part of the the system
libraries.

I'll open a PR for the documentation tomorrow and mark this draft
for review.

* Excluded UUIDv2

* Change icons

* Add RawUserQuery to Wox.Plugin.Query

Getting the RawUserQuery is necessary to be able to handle queries like
`# md5 a    a`, where the intent is to get the hash for `a    a`.
The existing `RawQuery` removes consecutive whitespaces and there was no
other way of getting the request as entered by the user.

* Add ValueGenerator plugin to installer

Also add the unit tests for the plugin to the pipeline.

* Small cleanup

* Fix spelling

* Fix spelling again

* Spell check for guiddata

* More fixes

This commit adds the dev docs explaining the classes in the new plugin.
It also fixes the following issues:
1. ValueGenerator was not a dependency for the PowerLauncher project
2. The error message for an invalid SHA variant now displays the
   supported SHA variants
3. Hash requests now wait for a string to hash (i.e. no longer hash an
   empty string)
4. GUID v3 and v5 namespace aliases allow lowercase notation
5. Unnecessary debug logs
6. An empty query will now just log "Empty request"
7. An invalid query will also log user query

* Spell check fix again

* Change error message for unsupported GUID versions

* Remove Any CPU from the solution

* Add to installer

* Remove duplicated ValueGeneratorPluginFolder entry
This commit is contained in:
Tudor Croitoru
2023-07-18 10:44:02 +01:00
committed by GitHub
parent 9eea08d8a8
commit 602368f6cc
30 changed files with 1458 additions and 2 deletions

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Community.PowerToys.Run.Plugin.ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
{
[TestClass]
public class GUIDGeneratorTests
{
[TestMethod]
public void GUIDv1Generator()
{
var guidRequest = new GUID.GUIDRequest(1);
guidRequest.Compute();
var guid = guidRequest.Result;
Assert.IsNotNull(guid);
Assert.AreEqual(0x1000, GetGUIDVersion(guid));
}
[TestMethod]
public void GUIDv3Generator()
{
var guidRequest = new GUID.GUIDRequest(3, "ns:DNS", "abc");
guidRequest.Compute();
var guid = guidRequest.Result;
Assert.IsNotNull(guid);
Assert.AreEqual(0x3000, GetGUIDVersion(guid));
}
[TestMethod]
public void GUIDv4Generator()
{
var guidRequest = new GUID.GUIDRequest(4);
guidRequest.Compute();
var guid = guidRequest.Result;
Assert.IsNotNull(guid);
Assert.AreEqual(0x4000, GetGUIDVersion(guid));
}
[TestMethod]
public void GUIDv5Generator()
{
var guidRequest = new GUID.GUIDRequest(5, "ns:DNS", "abc");
guidRequest.Compute();
var guid = guidRequest.Result;
Assert.IsNotNull(guid);
Assert.AreEqual(0x5000, GetGUIDVersion(guid));
}
[DataTestMethod]
[DataRow(3, "ns:DNS", "abc", "5bd670ce-29c8-3369-a8a1-10ce44c7259e")]
[DataRow(3, "ns:URL", "abc", "874a8cb4-4e91-3055-a476-3d3e2ffe375f")]
[DataRow(3, "ns:OID", "abc", "5557cd36-6b67-38ac-83fe-825f5905fc15")]
[DataRow(3, "ns:X500", "abc", "589392cb-93e1-392c-a846-367c45ed1ecc")]
[DataRow(3, "589392cb-93e1-392c-a846-367c45ed1ecc", "abc", "f55f77d2-feed-378e-aa47-2f716b07aaad")]
[DataRow(3, "abc", "abc", null)]
[DataRow(3, "", "abc", null)]
[DataRow(3, null, "abc", null)]
[DataRow(3, "abc", "", null)]
[DataRow(3, "abc", null, null)]
[DataRow(5, "ns:DNS", "abc", "6cb8e707-0fc5-5f55-88d4-d4fed43e64a8")]
[DataRow(5, "ns:URL", "abc", "68661508-f3c4-55b4-945d-ae2b4dfe5db4")]
[DataRow(5, "ns:OID", "abc", "7697a46f-b283-5da3-8e7c-62c11c03dd9e")]
[DataRow(5, "ns:X500", "abc", "53e882a6-63b1-578b-8bf1-8f0878cfa6b7")]
[DataRow(5, "589392cb-93e1-392c-a846-367c45ed1ecc", "abc", "396466f5-96f4-57e2-aea1-78e1bb1bacc6")]
[DataRow(5, "abc", "abc", null)]
[DataRow(5, "", "abc", null)]
[DataRow(5, null, "abc", null)]
[DataRow(5, "abc", "", null)]
[DataRow(5, "abc", null, null)]
public void GUIDv3Andv5(int version, string namespaceName, string name, string expectedResult)
{
var expectException = false;
if (namespaceName == null)
{
expectException = true;
}
string[] predefinedNamespaces = new string[] { "ns:DNS", "ns:URL", "ns:OID", "ns:X500" };
if (namespaceName != null &&
!Guid.TryParse(namespaceName, out _) &&
!predefinedNamespaces.Contains(namespaceName))
{
expectException = true;
}
if (name == null)
{
expectException = true;
}
try
{
var guidRequest = new GUID.GUIDRequest(version, namespaceName, name);
guidRequest.Compute();
if (expectException)
{
Assert.Fail("GUID generator should have thrown an exception");
}
if (namespaceName != string.Empty)
{
Assert.IsTrue(guidRequest.IsSuccessful);
Assert.AreEqual(expectedResult, guidRequest.ResultToString());
}
else
{
Assert.IsFalse(guidRequest.IsSuccessful);
}
}
catch (AssertFailedException)
{
throw;
}
catch
{
if (!expectException)
{
throw;
}
}
}
private static short GetGUIDVersion(byte[] guid)
{
var time_hi_and_version = BitConverter.ToInt16(guid.AsSpan()[6..8]);
return (short)(time_hi_and_version & 0xF000);
}
}
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wox.Plugin;
namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests
{
[TestClass]
public class InputParserTests
{
[DataTestMethod]
[DataRow("md5 abc", typeof(Hashing.HashRequest))]
[DataRow("sha1 abc", typeof(Hashing.HashRequest))]
[DataRow("sha256 abc", typeof(Hashing.HashRequest))]
[DataRow("sha384 abc", typeof(Hashing.HashRequest))]
[DataRow("sha512 abc", typeof(Hashing.HashRequest))]
[DataRow("sha1111 abc", null)]
[DataRow("uuid", typeof(GUID.GUIDRequest))]
[DataRow("guidv3 ns:DNS abc", typeof(GUID.GUIDRequest))]
[DataRow("guidv2 ns:DNS abc", null)]
[DataRow("uUiD5 ns:URL abc", typeof(GUID.GUIDRequest))]
[DataRow("Guidvv ns:DNS abc", null)]
[DataRow("guidv4", typeof(GUID.GUIDRequest))]
[DataRow("base64 abc", typeof(Base64.Base64Request))]
[DataRow("base99 abc", null)]
[DataRow("base64s abc", null)]
public void ParserTest(string input, Type? expectedRequestType)
{
var parser = new InputParser();
var query = new Query(input);
var expectException = false;
string? command = null;
if (query.Terms.Count == 0)
{
expectException = true;
}
else
{
command = query.Terms[0];
}
if (command != null && !CommandIsKnown(command))
{
expectException = true;
}
try
{
IComputeRequest request = parser.ParseInput(query);
if (expectException)
{
Assert.Fail("Parser should have thrown an exception");
}
Assert.IsNotNull(request);
Assert.AreEqual(expectedRequestType, request.GetType());
}
catch (AssertFailedException)
{
throw;
}
catch
{
if (!expectException)
{
throw;
}
}
}
private static bool CommandIsKnown(string command)
{
string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64" };
if (hashes.Contains(command.ToLowerInvariant()))
{
return true;
}
Regex regex = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
return regex.IsMatch(command);
}
}
}