Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion Inasync.PrimitiveAssert.Tests/IssueTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Inasync.Tests {
Expand Down Expand Up @@ -50,7 +53,7 @@ public static void Test() {

static Action TestCase<T>(int testNo, T x, object y, Type? expectedException = null) => () => {
TestAA
.Act(() => x.AssertIs(y))
.Act(() => x.AssertIs(y, $"No.{testNo}"))
.Assert(expectedException, message: $"No.{testNo}");
};

Expand Down Expand Up @@ -95,5 +98,62 @@ public FooBar(int foo, int bar, string value, string fooValue, string barValue)
string IBar.Value => _barValue;
}
}

[TestMethod]
public void Issue19_Test() {
var actual = new {
Foo = 1,
Bar = 2,
};

TestAA.Act(() => actual.AssertIs(new { Foo = 1 })).Assert<PrimitiveAssertFailedException>(); // expected がターゲット型を満たしていないので失敗
TestAA.Act(() => actual.AssertIs(new { Foo = 1, Bar = 2 })).Assert(); // actual と expected が完全一致してるので成功
TestAA.Act(() => actual.AssertIs(new { Foo = 1, Bar = 2, Baz = 3 })).Assert<PrimitiveAssertFailedException>(); // expected がターゲット型と完全に一致していないので失敗
}

[TestMethod]
public void Issue19_2_Test() => Issue19_2.Test();

private static class Issue19_2 {

public static void Test() {
static Action TestCase(int testNo, Type targetType, object x, object y, Type? expectedException = null) => () => {
TestAA
.Act(() => x.AssertIs(targetType, y))
.Assert(expectedException, message: $"No.{testNo}");
};

var array = new[] { 1, 2, 3 };
var custom = new CustomEnumerable<int>(new[] { 1, 2, 3 });
new[] {
TestCase( 0, targetType: typeof(IEnumerable) , x: array , y: custom, expectedException: typeof(PrimitiveAssertFailedException)), // 失敗:ターゲット型と expected が一致していない
TestCase( 1, targetType: typeof(IEnumerable) , x: custom, y: array ), // 成功
TestCase( 2, targetType: typeof(CustomEnumerable<int>), x: array , y: custom, expectedException: typeof(PrimitiveAssertFailedException)), // 失敗:actual をターゲット型にダックキャストできない
TestCase( 3, targetType: typeof(CustomEnumerable<int>), x: custom, y: array , expectedException: typeof(PrimitiveAssertFailedException)), // 失敗:ターゲット型と expected が一致していない
TestCase( 4, targetType: typeof(CustomEnumerable<int>), x: custom, y: custom), // 成功
}.Invoke();
}

private class CustomEnumerable<T> : IEnumerable<T> {
private readonly IEnumerable<T> _source;

public CustomEnumerable(IEnumerable<T> source) {
_source = source ?? throw new ArgumentNullException(nameof(source));
}

public string? CustomValue { get; set; }

public IEnumerator<T> GetEnumerator() => _source.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => _source.GetEnumerator();
}
}

[TestMethod]
public void Issue20_Test() {
var actual = new[] { 1, 3, 2 }.OrderBy(x => x);

actual.AssertIs(new[] { 1, 2, 3 });
}
}
}
45 changes: 27 additions & 18 deletions Inasync.PrimitiveAssert.Tests/PrimitiveAssertTests_AssertIs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ public class PrimitiveAssertTests_AssertIs {

private static Action TestCase(int testNo, Type? target, object? x, object? y, Type? expectedException = null) => () => {
TestAA
.Act(() => x.AssertIs(target, y, message: $"No.{testNo}_a"))
.Assert(expectedException, message: $"No.{testNo}_a");

TestAA
.Act(() => y.AssertIs(target, x, message: $"No.{testNo}_b"))
.Assert(expectedException, message: $"No.{testNo}_b");
.Act(() => x.AssertIs(target, y, $"No.{testNo}"))
.Assert(expectedException, message: $"No.{testNo}");
};

[TestMethod]
Expand Down Expand Up @@ -58,9 +54,10 @@ public void AssertIs_Numeric() {
new[] {
TestCase( 0, target: typeof(int) , x: 1 , y: 1 ),
TestCase( 1, target: typeof(int) , x: 1 , y: 1.1m, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。
TestCase( 2, target: typeof(int) , x: 1.1d, y: 1.1m), // ターゲット型が int や double 等の数値型の場合は、小数を含む任意の数値を表す Numeric 型扱いとなる。
TestCase( 3, target: typeof(int) , x: 1 , y: "" , expectedException: typeof(PrimitiveAssertFailedException)), // 型の不一致。
TestCase(10, target: typeof(int?) , x: 1 , y: 1 ), // ターゲット型が数値型の Nullable の場合も Numeric 型扱い。
TestCase( 2, target: typeof(int) , x: 1.1d, y: 1.1m), // ターゲット型が int や double 等の数値型の場合は、小数を含む任意の数値を表す Numeric 扱いとなる。
TestCase( 3, target: typeof(int) , x: 1 , y: "" , expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。
TestCase( 4, target: typeof(int) , x: "" , y: 1 , expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。string 型は Numeric ではない。
TestCase(10, target: typeof(int?) , x: 1 , y: 1 ), // ターゲット型が数値型の Nullable の場合も Numeric 扱い。
TestCase(20, target: typeof(double) , x: 1 , y: 1.0m),
TestCase(30, target: typeof(decimal), x: 1m , y: 1.0m),
}.Invoke();
Expand All @@ -73,7 +70,7 @@ public void AssertIs_PrimitiveData() {
var dummy = new DummyStruct();
new[] {
TestCase( 0, target: typeof(Guid) , x: guid1, y: guid1),
TestCase( 1, target: typeof(Guid) , x: guid1, y: dummy, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。DummyStruct 型は基本データ型ではない
TestCase( 1, target: typeof(Guid) , x: guid1, y: dummy, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致
TestCase( 2, target: typeof(Guid) , x: dummy, y: guid1, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。DummyStruct 型は基本データ型ではない。
TestCase( 3, target: typeof(Guid) , x: guid1, y: guid2, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。
TestCase(10, target: typeof(string), x: guid1, y: guid1, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。string 型に Guid 値は割り当てられない。
Expand All @@ -88,7 +85,7 @@ public void AssertIs_Collection() {
TestCase( 0, target: typeof(IEnumerable), x: new[]{1,2}, y: new[]{1,2}),
TestCase( 1, target: typeof(IEnumerable), x: new[]{1,2}, y: new[]{1 }, expectedException: typeof(PrimitiveAssertFailedException)), // 要素数の不一致。
TestCase( 2, target: typeof(IEnumerable), x: new[]{1,2}, y: new[]{1,3}, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。
TestCase( 3, target: typeof(IEnumerable), x: new[]{1,2}, y: 1 , expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。IEnumerable 型に int 値は割り当てられない
TestCase( 3, target: typeof(IEnumerable), x: new[]{1,2}, y: 1 , expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致
TestCase( 4, target: typeof(IEnumerable), x: 1 , y: new[]{1,2}, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。IEnumerable 型に int 値は割り当てられない。

TestCase(10, target: typeof(string) , x: new[]{'a','b'} , y: new[]{'a','b'} , expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。string 型に char[] 値は割り当てられない。
Expand All @@ -97,8 +94,13 @@ public void AssertIs_Collection() {

TestCase(20, target: countType , x: new int[] {1,2}, y: new{Count=2}, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。Array は {Count:int} 匿名型を (ダック的に) 満たしていない (Count 等の明示的実装は比較されない)
TestCase(21, target: countType , x: new List<int>{1,2}, y: new{Count=2}),
TestCase(22, target: typeof(ICollection), x: new List<int>{1,2}, y: new{Count=2}, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。{Count:int} 匿名値は ICollection 型を (ダック的に) 満たしていない (SyncRoot とか)
TestCase(22, target: typeof(ICollection), x: new List<int>{1,2}, y: new{Count=2}, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致
TestCase(23, target: typeof(ICollection), x: new List<int>{1,2}, y: new[]{1,2} ),

TestCase(30, target: countType , x: new{Count=2}, y: new int[] {1,2}, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。Array に Count データメンバーが無い。
TestCase(31, target: countType , x: new{Count=2}, y: new List<int>{1,2}, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。countType と List<T> のデータメンバーが一致しない。
TestCase(32, target: typeof(ICollection), x: new{Count=2}, y: new List<int>{1,2}, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。{Count:int} 匿名型はコレクションをダック的に満たしていない。
TestCase(33, target: typeof(ICollection), x: new[]{1,2} , y: new List<int>{1,2}),
}.Invoke();
}

Expand All @@ -111,12 +113,17 @@ public void AssertIs_CompositeData() {
new[] {
TestCase( 0, target: typeof(DummyClass), x:dummy, y:dummy ),
TestCase( 1, target: typeof(DummyClass), x:dummy, y:DummyClass() ),
TestCase( 2, target: typeof(DummyClass), x:dummy, y:new{ReadOnlyField=1}, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。{ReadOnlyField:int} 匿名値は DummyClass 型を (ダック的に) 満たしていない
TestCase( 2, target: typeof(DummyClass), x:dummy, y:new{ReadOnlyField=1}, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。{ReadOnlyField:int} 匿名値は DummyClass 型とデータメンバーが一致しない
TestCase( 3, target: fooType , x:dummy, y:dummy , expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。DummyClass 値は {Foo:int} 匿名型を (ダック的に) 満たしていない。
TestCase(10, target: readOnlyFieldType , x:dummy, y:DummyClass() ),
TestCase(10, target: readOnlyFieldType , x:dummy, y:DummyClass() , expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。y はダックキャストされていないので、x に対して余分なデータメンバーが存在する。
TestCase(11, target: readOnlyFieldType , x:dummy, y:new{ReadOnlyField=1}),
TestCase(12, target: typeof(object) , x:dummy, y:new{} ), // issue #10: ターゲット型が object の場合データ メンバーが存在しない為、実質検証なし。
TestCase(13, target: typeof(object) , x:dummy, y:new{Foo=0} ), // issue #10: ターゲット型が object の場合データ メンバーが存在しない為、実質検証なし。
TestCase(12, target: typeof(object) , x:dummy, y:new{} ), // issue #10: ターゲット型が object の場合データ メンバーが存在しない。
TestCase(13, target: typeof(object) , x:dummy, y:new{Foo=0} , expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致、y はダックキャストされていないので、x に対して余分なデータメンバーが存在する。

TestCase(20, target: typeof(DummyClass), x:new{ReadOnlyField=1}, y:dummy, expectedException: typeof(PrimitiveAssertFailedException)), // ターゲット型違反。{ReadOnlyField:int} 匿名値は DummyClass 型を(ダックタイプ的に)満たしていない。
TestCase(21, target: readOnlyFieldType , x:new{ReadOnlyField=1}, y:dummy, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。y はダックキャストされていないので、x に対して余分なデータメンバーが存在する。
TestCase(22, target: typeof(object) , x:new{} , y:dummy, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。y はダックキャストされていないので、x に対して余分なデータメンバーが存在する。
TestCase(23, target: typeof(object) , x:new{Foo=0} , y:dummy, expectedException: typeof(PrimitiveAssertFailedException)), // 値の不一致。y はダックキャストされていないので、x に対して余分なデータメンバーが存在する。

TestCase(50, target: typeof(IList<int>[]), x:new[]{new List<int>{1,2}, new List<int>{3}}, y:new[]{new[]{1,2}, new[]{3}}),
}.Invoke();
Expand Down Expand Up @@ -167,7 +174,8 @@ public void AssertIs_Usage() {
LastError = new ApplicationException(),
};

TestCase(0, target: x.GetType(), x: x, y: y)();
x.AssertIs(y);
y.AssertIs(x);
}

[TestMethod]
Expand All @@ -180,7 +188,8 @@ Action TestCase<T>(int testNo, T x, object y, Type? expectedException = null) =>

new[] {
TestCase( 0, x: new{ Foo=1, Bar="B" }, y: new{ Foo=1 } , expectedException: typeof(PrimitiveAssertFailedException)),
TestCase( 1, x: new{ Foo=1 } , y: new{ Foo=1, Bar="B" }),
TestCase(14, x: new{ Foo=1, Bar="B" }, y: new{ Foo=1, Bar="B" }),
TestCase( 1, x: new{ Foo=1 } , y: new{ Foo=1, Bar="B" }, expectedException: typeof(PrimitiveAssertFailedException)),
TestCase( 2, x: new List<int>{ 1, 2 }, y: new[]{ 1, 2 } ), // issue #3: List<T> 等の汎用コレクションは複合型アサートを行わない (List<T> なら Capacity をアサートしない)
TestCase( 3, x: new[]{ 1, 2 } , y: new List<int>{ 1, 2 }), // issue #6: 配列や Array も汎用コレクションとして、複合型アサートは行わない。
TestCase( 4, x: new DummyStruct[0] , y: new List<DummyStruct>()), // issue #7: System 名前空間かどうかは配列判定に関係なかったので、適切な配列判定に修正。
Expand Down
8 changes: 4 additions & 4 deletions Inasync.PrimitiveAssert.Tests/PrimitiveSerializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Inasync.Tests {
public class PrimitiveSerializerTests {

[TestMethod]
public void Usage() {
public void ToPrimitiveString_Usage() {
var x = new {
AccountId = new Guid("f5b63bc6-9876-4e07-8400-f06daf3e4212"),
FullName = "John Smith",
Expand All @@ -27,11 +27,11 @@ public void Usage() {
Params2 = (ValueTuple<int, string>?)null,
Params3 = Tuple.Create(1, "bar"),
LastError = new ApplicationException(),
StringType = typeof(string),
};

var json = x.ToPrimitiveString();

Console.WriteLine(json);
Console.WriteLine(x.ToPrimitiveString(indented: false));
Console.WriteLine(x.ToPrimitiveString(indented: true));
}
}
}
2 changes: 1 addition & 1 deletion Inasync.PrimitiveAssert/Commons/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Collections.Generic;

namespace Inasync {
namespace Commons {

internal static class DictionaryExtensions {

Expand Down
11 changes: 11 additions & 0 deletions Inasync.PrimitiveAssert/Commons/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace Commons {

internal static class EnumerableExtensions {

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) {
return new HashSet<T>(source);
}
}
}
22 changes: 22 additions & 0 deletions Inasync.PrimitiveAssert/Commons/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,27 @@ public static IEnumerable<PropertyInfo> GetPropertiesEx(this Type type, BindingF
.SelectMany(x => x.GetProperties(bindingAttr))
;
}

/// <summary>
/// 判りやすい型名を返します。
/// </summary>
/// <param name="type">対象の型。</param>
/// <returns><paramref name="type"/> を判別しやすい型名。</returns>
public static string GetFriendlyName(this Type type) {
if (type.IsGenericType) {
string genericTypeName = type.Name.IndexOf('`') switch
{
{ } i when i >= 0 => type.Name.Remove(i),
_ => type.Name,
};
return $"{genericTypeName}<{string.Join(", ", type.GetGenericArguments().Select(x => x.GetFriendlyName()))}>";
}

if (type.IsArray) {
return type.GetElementType().GetFriendlyName() + "[]";
}

return type.Name;
}
}
}
2 changes: 1 addition & 1 deletion Inasync.PrimitiveAssert/Inasync.PrimitiveAssert.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PackageProjectUrl>https://github.com/in-async/PrimitiveAssert</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/in-async/PrimitiveAssert/blob/master/LICENSE</PackageLicenseUrl>
<PackageTags>library test unittest assert deep</PackageTags>
<Version>0.4.0</Version>
<Version>0.5.0</Version>
</PropertyGroup>

</Project>
Loading