mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-23 00:18:47 +00:00
Add FileInfo.GetHash<T>
Wraps Stream.GetHash<T>, but saves the caller having to OpenRead themselves
This commit is contained in:
parent
5168948a1d
commit
374933e45e
48
X10D.Tests/src/Core/FileInfoTests.cs
Normal file
48
X10D.Tests/src/Core/FileInfoTests.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace X10D.Tests.Core;
|
||||
|
||||
[TestClass]
|
||||
public class FileInfoTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void GetHash()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
const string expectedHash = "0A4D55A8D778E5022FAB701977C5D840BBC486D0"; // SHA-1
|
||||
File.WriteAllText(fileName, "Hello World");
|
||||
Assert.IsTrue(File.Exists(fileName), $"File.Exists(\"{fileName}\")");
|
||||
|
||||
byte[] hash = new FileInfo(fileName).GetHash<SHA1>();
|
||||
string actualHash = BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
|
||||
Assert.AreEqual(expectedHash, actualHash);
|
||||
|
||||
File.Delete(fileName); // cleanup is important
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHash_NullFileInfo()
|
||||
{
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<ArgumentNullException>(() => ((FileInfo?)null)!.GetHash<SHA1>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetHash_InvalidFile()
|
||||
{
|
||||
string fileName = $"temp.{DateTimeOffset.Now.ToUnixTimeSeconds()}.bin";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
Assert.Fail("Temporary file already exists");
|
||||
}
|
||||
|
||||
// any HashAlgorithm will do, but SHA1 is used above. so to remain consistent, we use it here
|
||||
Assert.ThrowsException<FileNotFoundException>(() => new FileInfo(fileName).GetHash<SHA1>());
|
||||
}
|
||||
}
|
43
X10D/src/FileInfoExtensions/FileInfoExtensions.cs
Normal file
43
X10D/src/FileInfoExtensions/FileInfoExtensions.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace X10D;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="FileInfo" />.
|
||||
/// </summary>
|
||||
public static class FileInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the hash of a file using the specified hash algorithm.
|
||||
/// </summary>
|
||||
/// <param name="value">The file whose hash to compute.</param>
|
||||
/// <typeparam name="T">A <see cref="HashAlgorithm" /> derived type.</typeparam>
|
||||
/// <returns>A <see cref="byte" /> array representing the hash of the file.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="FileNotFoundException">The file pointed at by <paramref name="value" /> does not exist.</exception>
|
||||
/// <exception cref="IOException">The opened file stream cannot be read.</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <para><typeparamref name="T" /> does not offer a static <c>Create</c> method.</para>
|
||||
/// -or-
|
||||
/// <para>
|
||||
/// An invocation to the static <c>Create</c> method defined in <typeparamref name="T" /> returned
|
||||
/// <see langword="null" />.
|
||||
/// </para>
|
||||
/// </exception>
|
||||
public static byte[] GetHash<T>(this FileInfo value)
|
||||
where T : HashAlgorithm
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
if (!value.Exists)
|
||||
{
|
||||
throw new FileNotFoundException("Cannot get hash of non-existent file.", value.FullName);
|
||||
}
|
||||
|
||||
using FileStream stream = value.OpenRead();
|
||||
return stream.GetHash<T>();
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ public static partial class StreamExtensions
|
||||
/// <param name="stream">The stream whose hash is to be computed.</param>
|
||||
/// <returns>A <see cref="byte" /> array representing the hash of the stream.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="stream" /> is <see langword="null" />.</exception>
|
||||
/// <exception cref="IOException">The stream does not support reading.</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// <typeparamref name="T" /> does not offer a static <c>Create</c> method.
|
||||
/// -or-
|
||||
@ -26,6 +27,10 @@ public static partial class StreamExtensions
|
||||
/// <see langword="null" />.
|
||||
/// </exception>
|
||||
/// <exception cref="ObjectDisposedException">The stream has already been disposed.</exception>
|
||||
/// <exception cref="TypeInitializationException">
|
||||
/// The specified <see cref="HashAlgorithm" /> does not offer a parameterless <c>Create</c> method, or its <c>Create</c>
|
||||
/// method returns a type that is not assignable to <typeparamref name="T" />.
|
||||
/// </exception>
|
||||
/// <remarks>This method consumes the stream from its current position!.</remarks>
|
||||
public static byte[] GetHash<T>(this Stream stream)
|
||||
where T : HashAlgorithm
|
||||
@ -41,7 +46,9 @@ public static partial class StreamExtensions
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
MethodInfo? createMethod = type.GetMethod("Create", BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
MethodInfo? createMethod = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||
.FirstOrDefault(c => c.Name == "Create" && c.GetParameters().Length == 0);
|
||||
if (createMethod is null)
|
||||
{
|
||||
throw new TypeInitializationException(type.FullName,
|
||||
|
Loading…
Reference in New Issue
Block a user