mirror of https://github.com/Squidex/squidex.git
Browse Source
* Improve performance. * Minor formatting. * Mini improvements. * Fix hasher.pull/1012/head
committed by
GitHub
27 changed files with 943 additions and 397 deletions
@ -0,0 +1,252 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Buffers; |
||||
|
using System.Security.Cryptography; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace Squidex.Infrastructure; |
||||
|
|
||||
|
public static class HashExtensions |
||||
|
{ |
||||
|
private const int MaxStackSize = 128; |
||||
|
private delegate int HashMethod(ReadOnlySpan<byte> source, Span<byte> destination); |
||||
|
private delegate string EncodeMethod(ReadOnlySpan<byte> source); |
||||
|
private static readonly HashMethod HashMD5 = MD5.HashData; |
||||
|
private static readonly HashMethod HashSHA256 = SHA256.HashData; |
||||
|
private static readonly HashMethod HashSHA512 = SHA512.HashData; |
||||
|
private static readonly EncodeMethod EncodeUTF8 = Encoding.UTF8.GetString; |
||||
|
private static readonly EncodeMethod EncodeBase64 = input => Convert.ToBase64String(input, default); |
||||
|
|
||||
|
public static string ToSha256Base64(this string value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA256, SHA256.HashSizeInBits, Encoding.UTF8, EncodeBase64); |
||||
|
} |
||||
|
|
||||
|
public static string ToSha256Base64(this ReadOnlySpan<byte> value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA256, SHA256.HashSizeInBits, EncodeBase64); |
||||
|
} |
||||
|
|
||||
|
public static string ToSha512(this string value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA512, SHA512.HashSizeInBits, Encoding.UTF8, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
public static string ToSha512(this ReadOnlySpan<byte> value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA512, SHA512.HashSizeInBits, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
public static string ToSha256(this string value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA256, SHA256.HashSizeInBits, Encoding.UTF8, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
public static string ToSha256(this ReadOnlySpan<byte> value) |
||||
|
{ |
||||
|
return ToHashed(value, HashSHA256, SHA256.HashSizeInBits, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
public static string ToMD5(this string value) |
||||
|
{ |
||||
|
return ToHashed(value, HashMD5, MD5.HashSizeInBits, Encoding.UTF8, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
public static string ToMD5(this ReadOnlySpan<byte> value) |
||||
|
{ |
||||
|
return ToHashed(value, HashMD5, MD5.HashSizeInBits, EncodeUTF8); |
||||
|
} |
||||
|
|
||||
|
private static string ToHashed(this string value, HashMethod algorithm, int hashSize, Encoding encoding, EncodeMethod encode) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(value)) |
||||
|
{ |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
string result; |
||||
|
|
||||
|
var length = encoding.GetByteCount(value); |
||||
|
|
||||
|
if (length > MaxStackSize) |
||||
|
{ |
||||
|
var buffer = ArrayPool<byte>.Shared.Rent(length); |
||||
|
try |
||||
|
{ |
||||
|
result = ConvertCore(algorithm, hashSize, value.AsSpan(), buffer, encode, encoding); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
ArrayPool<byte>.Shared.Return(buffer); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Span<byte> buffer = stackalloc byte[length]; |
||||
|
|
||||
|
result = ConvertCore(algorithm, hashSize, value.AsSpan(), buffer, encode, encoding); |
||||
|
} |
||||
|
|
||||
|
static string ConvertCore(HashMethod algorithm, int hashSize, ReadOnlySpan<char> source, Span<byte> destination, EncodeMethod encode, Encoding encoding) |
||||
|
{ |
||||
|
var written = encoding.GetBytes(source, destination); |
||||
|
|
||||
|
return ToHashed(destination[..written], algorithm, hashSize, encode); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private static string ToHashed(ReadOnlySpan<byte> bytes, HashMethod algorithm, int hashSize, EncodeMethod encode) |
||||
|
{ |
||||
|
if (bytes.Length == 0) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
string result; |
||||
|
|
||||
|
var length = hashSize / 8; |
||||
|
|
||||
|
if (length > MaxStackSize) |
||||
|
{ |
||||
|
var buffer = ArrayPool<byte>.Shared.Rent(length); |
||||
|
try |
||||
|
{ |
||||
|
result = ConvertCore(algorithm, bytes, buffer, encode); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
ArrayPool<byte>.Shared.Return(buffer); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Span<byte> buffer = stackalloc byte[length]; |
||||
|
|
||||
|
return ConvertCore(algorithm, bytes, buffer, encode); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
|
||||
|
static string ConvertCore(HashMethod algorithm, ReadOnlySpan<byte> source, Span<byte> destination, EncodeMethod encode) |
||||
|
{ |
||||
|
var written = algorithm(source, destination); |
||||
|
|
||||
|
return encode(destination[..written]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void AppendString(this IncrementalHash hash, string value) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(value)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var length = Encoding.Default.GetByteCount(value); |
||||
|
|
||||
|
if (length > MaxStackSize) |
||||
|
{ |
||||
|
var buffer = ArrayPool<byte>.Shared.Rent(length); |
||||
|
try |
||||
|
{ |
||||
|
ConvertCore(hash, value, buffer); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
ArrayPool<byte>.Shared.Return(buffer); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Span<byte> buffer = stackalloc byte[length]; |
||||
|
|
||||
|
ConvertCore(hash, value, buffer); |
||||
|
} |
||||
|
|
||||
|
static void ConvertCore(IncrementalHash hash, string source, Span<byte> destination) |
||||
|
{ |
||||
|
var written = Encoding.Default.GetBytes(source, destination); |
||||
|
|
||||
|
hash.AppendData(destination[..written]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void AppendLong(this IncrementalHash hash, long version) |
||||
|
{ |
||||
|
if (version == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Span<byte> buffer = stackalloc byte[sizeof(long)]; |
||||
|
|
||||
|
if (BitConverter.TryWriteBytes(buffer, version)) |
||||
|
{ |
||||
|
hash.AppendData(buffer); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static string GetHexStringAndReset(this IncrementalHash hash) |
||||
|
{ |
||||
|
Span<byte> buffer = stackalloc byte[hash.HashLengthInBytes]; |
||||
|
|
||||
|
hash.GetHashAndReset(buffer); |
||||
|
|
||||
|
return ToHexString(buffer); |
||||
|
} |
||||
|
|
||||
|
public static string ToHexString(this ReadOnlySpan<byte> data) |
||||
|
{ |
||||
|
string result; |
||||
|
|
||||
|
var length = data.Length * 2; |
||||
|
|
||||
|
if (length > MaxStackSize) |
||||
|
{ |
||||
|
var buffer = ArrayPool<char>.Shared.Rent(length); |
||||
|
try |
||||
|
{ |
||||
|
result = ConvertCore(data, buffer); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
ArrayPool<char>.Shared.Return(buffer); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Span<char> buffer = stackalloc char[length]; |
||||
|
|
||||
|
result = ConvertCore(data, buffer); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
|
||||
|
static string ConvertCore(ReadOnlySpan<byte> data, Span<char> hexBuffer) |
||||
|
{ |
||||
|
unchecked |
||||
|
{ |
||||
|
int n = 0; |
||||
|
for (int i = 0; i < data.Length; i++) |
||||
|
{ |
||||
|
byte b = data[i]; |
||||
|
|
||||
|
byte b1 = (byte)(b >> 4); |
||||
|
byte b2 = (byte)(b & 0xF); |
||||
|
|
||||
|
hexBuffer[n++] = (b1 < 10) ? (char)('0' + b1) : (char)('A' + (b1 - 10)); |
||||
|
hexBuffer[n++] = (b2 < 10) ? (char)('0' + b2) : (char)('A' + (b2 - 10)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return new string(hexBuffer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,182 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Security.Cryptography; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace Squidex.Infrastructure; |
||||
|
|
||||
|
public class HashExtensionsTests |
||||
|
{ |
||||
|
public static IEnumerable<object[]> InputLengths() |
||||
|
{ |
||||
|
yield return new object[] { 1 }; |
||||
|
yield return new object[] { 20 }; |
||||
|
yield return new object[] { 50 }; |
||||
|
yield return new object[] { 500 }; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_calculate_hex_code_from_empty_array() |
||||
|
{ |
||||
|
var source = Array.Empty<byte>(); |
||||
|
|
||||
|
var actual = ((ReadOnlySpan<byte>)source.AsSpan()).ToHexString(); |
||||
|
|
||||
|
Assert.Equal(string.Empty, actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_calculate_hex_code_from_byte_array() |
||||
|
{ |
||||
|
var source = new byte[] { 0x00, 0x01, 0xFF, 0x1A, 0x2B, 0x3C }; |
||||
|
|
||||
|
var actual = ((ReadOnlySpan<byte>)source.AsSpan()).ToHexString(); |
||||
|
|
||||
|
Assert.Equal("0001FF1A2B3C", actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_md5(int length) |
||||
|
{ |
||||
|
AssertHash(HashExtensions.ToMD5, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_md5_from_bytes(int length) |
||||
|
{ |
||||
|
AssertHashFromBytes(HashExtensions.ToMD5, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_sha256(int length) |
||||
|
{ |
||||
|
AssertHash(HashExtensions.ToSha256, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_sha256_from_bytes(int length) |
||||
|
{ |
||||
|
AssertHashFromBytes(HashExtensions.ToSha256, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_sha512(int length) |
||||
|
{ |
||||
|
AssertHash(HashExtensions.ToSha512, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_sha512_from_bytes(int length) |
||||
|
{ |
||||
|
AssertHashFromBytes(HashExtensions.ToSha512, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_to_sha256_base64(int length) |
||||
|
{ |
||||
|
AssertHash(HashExtensions.ToSha256Base64, length); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(InputLengths))] |
||||
|
public void Should_hash_sha256_base64_from_bytes(int length) |
||||
|
{ |
||||
|
AssertHashFromBytes(HashExtensions.ToSha256Base64, length); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_convert_same_hash_as_webhook_utils() |
||||
|
{ |
||||
|
var input = $"{Guid.NewGuid()}{new string('0', 100)}{Guid.NewGuid()}"; |
||||
|
|
||||
|
var hash1 = WebhookHash(input); |
||||
|
var hash2 = input.ToSha256Base64(); |
||||
|
|
||||
|
Assert.Equal(hash1, hash2); |
||||
|
} |
||||
|
|
||||
|
private delegate string HashFromBytes(ReadOnlySpan<byte> source); |
||||
|
|
||||
|
private static void AssertHashFromBytes(HashFromBytes hasher, int length) |
||||
|
{ |
||||
|
var input1 = CreateBuffer(length, 0); |
||||
|
var input2 = CreateBuffer(length, 42); |
||||
|
|
||||
|
var hash1_a = hasher(input1); |
||||
|
var hash1_b = hasher(input1); |
||||
|
|
||||
|
var hash2 = hasher(input2); |
||||
|
|
||||
|
Assert.NotNull(hash1_a); |
||||
|
Assert.NotNull(hash1_a); |
||||
|
Assert.NotNull(hash2); |
||||
|
|
||||
|
Assert.NotEmpty(hash1_a); |
||||
|
Assert.NotEmpty(hash1_b); |
||||
|
Assert.NotEmpty(hash2); |
||||
|
|
||||
|
Assert.Equal(hash1_a, hash1_b); |
||||
|
|
||||
|
Assert.NotEqual(hash1_a, hash2); |
||||
|
} |
||||
|
|
||||
|
private static void AssertHash(Func<string, string> hasher, int length) |
||||
|
{ |
||||
|
var input1 = new string('a', length); |
||||
|
var input2 = new string('b', length); |
||||
|
|
||||
|
var hash1_a = hasher(input1); |
||||
|
var hash1_b = hasher(input1); |
||||
|
|
||||
|
var hash2 = hasher(input2); |
||||
|
|
||||
|
Assert.NotNull(hash1_a); |
||||
|
Assert.NotNull(hash1_a); |
||||
|
Assert.NotNull(hash2); |
||||
|
|
||||
|
Assert.NotEmpty(hash1_a); |
||||
|
Assert.NotEmpty(hash1_b); |
||||
|
Assert.NotEmpty(hash2); |
||||
|
|
||||
|
Assert.Equal(hash1_a, hash1_b); |
||||
|
|
||||
|
Assert.NotEqual(hash1_a, hash2); |
||||
|
} |
||||
|
|
||||
|
private static byte[] CreateBuffer(int length, int offset) |
||||
|
{ |
||||
|
var input = new byte[length]; |
||||
|
|
||||
|
for (var i = 0; i < length; i++) |
||||
|
{ |
||||
|
input[i] = (byte)(i + offset); |
||||
|
} |
||||
|
|
||||
|
return input; |
||||
|
} |
||||
|
|
||||
|
private static string WebhookHash(string value) |
||||
|
{ |
||||
|
using (var sha = SHA256.Create()) |
||||
|
{ |
||||
|
var bytesArray = Encoding.UTF8.GetBytes(value); |
||||
|
var bytesHash = sha.ComputeHash(bytesArray); |
||||
|
|
||||
|
var result = Convert.ToBase64String(bytesHash); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,98 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using Squidex.Infrastructure.Reflection.Internal; |
|
||||
|
|
||||
namespace Squidex.Infrastructure.Reflection; |
|
||||
|
|
||||
public class PropertiesTypeAccessorTests |
|
||||
{ |
|
||||
public class TestClass |
|
||||
{ |
|
||||
private int target; |
|
||||
|
|
||||
public int ReadWrite |
|
||||
{ |
|
||||
get |
|
||||
{ |
|
||||
return target; |
|
||||
} |
|
||||
set |
|
||||
{ |
|
||||
target = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public int Read |
|
||||
{ |
|
||||
get => target; |
|
||||
} |
|
||||
|
|
||||
public int Write |
|
||||
{ |
|
||||
set => target = value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private readonly TestClass target = new TestClass(); |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_set_read_write_property() |
|
||||
{ |
|
||||
var sut = new PropertyAccessor(typeof(TestClass).GetProperty("ReadWrite")!); |
|
||||
|
|
||||
sut.Set(target, 123); |
|
||||
|
|
||||
Assert.Equal(123, target.Read); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_set_write_property() |
|
||||
{ |
|
||||
var accessor = new PropertyAccessor(typeof(TestClass).GetProperty("Write")!); |
|
||||
|
|
||||
accessor.Set(target, 123); |
|
||||
|
|
||||
Assert.Equal(123, target.Read); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_throw_exception_if_setting_readonly() |
|
||||
{ |
|
||||
var sut = new PropertyAccessor(typeof(TestClass).GetProperty("Read")!); |
|
||||
|
|
||||
Assert.Throws<NotSupportedException>(() => sut.Set(target, 123)); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_get_read_write_property() |
|
||||
{ |
|
||||
var sut = new PropertyAccessor(typeof(TestClass).GetProperty("ReadWrite")!); |
|
||||
|
|
||||
target.Write = 123; |
|
||||
|
|
||||
Assert.Equal(123, sut.Get(target)); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_get_read_property() |
|
||||
{ |
|
||||
var sut = new PropertyAccessor(typeof(TestClass).GetProperty("Read")!); |
|
||||
|
|
||||
target.Write = 123; |
|
||||
|
|
||||
Assert.Equal(123, sut.Get(target)); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_throw_exception_if_getting_writeonly_property() |
|
||||
{ |
|
||||
var sut = new PropertyAccessor(typeof(TestClass).GetProperty("Write")!); |
|
||||
|
|
||||
Assert.Throws<NotSupportedException>(() => sut.Get(target)); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,153 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.Reflection.Internal; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
public class PropertyAccessorTests |
||||
|
{ |
||||
|
public class TestClass |
||||
|
{ |
||||
|
private int readWrite; |
||||
|
private int readWriteVirtual; |
||||
|
|
||||
|
public int ReadWrite |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return readWrite; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
readWrite = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual int ReadWriteVirtual |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return readWriteVirtual; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
readWriteVirtual = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ReadWriteAuto { get; set; } |
||||
|
|
||||
|
public int Read |
||||
|
{ |
||||
|
get => readWrite; |
||||
|
} |
||||
|
|
||||
|
public int Write |
||||
|
{ |
||||
|
set => readWrite = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly TestClass target = new TestClass(); |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_set_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateSetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWrite")!); |
||||
|
|
||||
|
sut(target, 123); |
||||
|
|
||||
|
Assert.Equal(123, target.Read); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_set_virtual_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateSetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWriteVirtual")!); |
||||
|
|
||||
|
sut(target, 123); |
||||
|
|
||||
|
Assert.Equal(123, target.ReadWriteVirtual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_set_auto_implemented_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateSetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWriteAuto")!); |
||||
|
|
||||
|
sut(target, 123); |
||||
|
|
||||
|
Assert.Equal(123, target.ReadWriteAuto); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_set_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateSetter<TestClass, int>(typeof(TestClass).GetProperty("Write")!); |
||||
|
|
||||
|
sut(target, 123); |
||||
|
|
||||
|
Assert.Equal(123, target.Read); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_exception_if_setting_readonly() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateSetter<TestClass, int>(typeof(TestClass).GetProperty("Read")!); |
||||
|
|
||||
|
Assert.Throws<NotSupportedException>(() => sut(target, 123)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_get_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateGetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWrite")!); |
||||
|
|
||||
|
target.Write = 123; |
||||
|
|
||||
|
Assert.Equal(123, sut(target)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_get_virtual_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateGetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWriteVirtual")!); |
||||
|
|
||||
|
target.ReadWriteVirtual = 123; |
||||
|
|
||||
|
Assert.Equal(123, sut(target)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_get_auto_implemented_read_write_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateGetter<TestClass, int>(typeof(TestClass).GetProperty("ReadWriteAuto")!); |
||||
|
|
||||
|
target.ReadWriteAuto = 123; |
||||
|
|
||||
|
Assert.Equal(123, sut(target)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_get_read_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateGetter<TestClass, int>(typeof(TestClass).GetProperty("Read")!); |
||||
|
|
||||
|
target.Write = 123; |
||||
|
|
||||
|
Assert.Equal(123, sut(target)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_throw_exception_if_getting_writeonly_property() |
||||
|
{ |
||||
|
var sut = PropertyAccessor.CreateGetter<TestClass, int>(typeof(TestClass).GetProperty("Write")!); |
||||
|
|
||||
|
Assert.Throws<NotSupportedException>(() => sut(target)); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue