Beispiel #1
0
        private void WriteLog(AssertNode node, string additionalMessage)
        {
            if (_logger == null)
            {
                return;
            }

            _logger.Write(node.Actual.ToPrimitiveString());
            if (additionalMessage != null)
            {
                _logger.Write("    // " + additionalMessage);
            }
            _logger.WriteLine();
        }
Beispiel #2
0
        /// <exception cref="ArgumentException">ターゲット型に同じ名前のデータメンバーが 2 つ以上存在します。</exception>
        private bool TryCompositeAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            targetType = Nullable.GetUnderlyingType(targetType) ?? targetType;

            var actualType   = actual.GetType();
            var expectedType = expected.GetType();

            if (targetType.IsAssignableFrom(actualType))
            {
                actualType = targetType;
            }

            // ターゲット型に同じ名前のデータメンバーが含まれていない事をチェック。
            // 同じ名前のデータメンバーが 2 つ以上存在する場合、アサート対象を解決できない為 (CS0229 相当)。
            var targetDataMembers = targetType.GetDataMembers().ToArray();
            var duplicateMember   = targetDataMembers.ToLookup(x => x.Name).FirstOrDefault(g => g.Count() > 1);

            if (duplicateMember != null)
            {
                var duplicateMemberNames = duplicateMember.Select(x => $"'{x.DeclaringType.GetFriendlyName()}.{x.Name}'");
                throw new ArgumentException(message: $"ターゲット型 '{targetType.GetFriendlyName()}' に含まれる {string.Join(" と ", duplicateMemberNames)} があいまいです。");
            }

            // 各データ メンバーの比較
            var actualMemberMap   = actualType.GetDataMembers().ToDictionary(x => x.Name);
            var expectedMemberMap = expectedType.GetDataMembers().ToDictionary(x => x.Name);

            foreach (var member in targetDataMembers)
            {
                if (!actualMemberMap.TryRemove(member.Name, out var actualMember))
                {
                    throw new PrimitiveAssertFailedException(node, $"actual にデータ メンバー {member.Name} が見つかりません。", _message);
                }
                if (!expectedMemberMap.TryRemove(member.Name, out var expectedMember))
                {
                    throw new PrimitiveAssertFailedException(node, $"expected にデータ メンバー {member.Name} が見つかりません。", _message);
                }

                var actualMemberValue   = actualMember.GetValue(actual);
                var expectedMemberValue = expectedMember.GetValue(expected);
                AssertIs(new AssertNode(member.Name, member.DataType, actualMemberValue, expectedMemberValue, node));
            }
            if (expectedMemberMap.Count > 0)
            {
                throw new PrimitiveAssertFailedException(node, $"expected のデータ メンバー '{string.Join("', '", expectedMemberMap.Keys)}' がターゲット型に存在しません。", _message);
            }

            return(false);
        }
Beispiel #3
0
        private bool TryCollectionAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            if (!typeof(IEnumerable).IsAssignableFrom(targetType))
            {
                return(false);
            }
            if (targetType == typeof(string))
            {
                return(false);
            }

            var actualType   = actual.GetType();
            var expectedType = expected.GetType();

            if (!(actual is IEnumerable))
            {
                throw new PrimitiveAssertFailedException(node, $"ターゲット型 {targetType.GetFriendlyName()} はコレクション型ですが、actual の型 {actualType.GetFriendlyName()} は非コレクション型です。", _message);
            }
            if (!(expected is IEnumerable))
            {
                throw new PrimitiveAssertFailedException(node, $"actual はコレクション型ですが、expected の型 {expectedType.GetFriendlyName()} は非コレクション型です。", _message);
            }
            var actualItems   = ((IEnumerable)actual).AsCollection();
            var expectedItems = ((IEnumerable)expected).AsCollection();

            if (actualItems.Count != expectedItems.Count)
            {
                throw new PrimitiveAssertFailedException(node, $"actual の要素数 {actualItems.Count} と expected の要素数 {expectedItems.Count} が等しくありません。", _message);
            }

            var itemType     = targetType.GetEnumerableElementType();
            var actualIter   = actualItems.GetEnumerator();
            var expectedIter = expectedItems.GetEnumerator();

            for (var i = 0; i < actualItems.Count; i++)
            {
                actualIter.MoveNext();
                expectedIter.MoveNext();

                // NOTE: 要素型が不明の場合は、要素のランタイム型にフォールバック。
                var itemTargetType = itemType ?? actualIter.Current?.GetType();
                AssertIs(new AssertNode("[" + i + "]", itemTargetType, actualIter.Current, expectedIter.Current, node));
            }
            if (!targetType.IsSystemCollection())
            {
                return(false);
            }
            if (!expectedType.IsSystemCollection())
            {
                return(false);
            }

            return(true);
        }
Beispiel #4
0
        private bool TryCircularReferenceAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            var actualType   = actual.GetType();
            var expectedType = expected.GetType();

            if (actualType.IsValueType || expectedType.IsValueType)
            {
                return(false);
            }

            for (var parent = node.Parent; parent != null; parent = parent.Parent)
            {
                if (ReferenceEquals(parent.Actual, actual))
                {
                    if (ReferenceEquals(parent.Expected, expected))
                    {
                        if (!actualType.IsDuckImplemented(targetType))
                        {
                            throw new PrimitiveAssertFailedException(node, "actual と expected は同じパスに対する循環参照ですが、ターゲット型に違反しています。", _message);
                        }

                        WriteLog(node, $"actual と expected は同じパスに対する循環参照です。循環先のパス: {parent.Path}");
                        return(true);
                    }
                    else
                    {
                        throw new PrimitiveAssertFailedException(node, "actual は循環参照ですが、expected が循環参照ではありません。", _message);
                    }
                }
                else
                {
                    if (ReferenceEquals(parent.Expected, expected))
                    {
                        throw new PrimitiveAssertFailedException(node, "actual は循環参照ではありませんが、expected が循環参照です。", _message);
                    }
                }
            }
            return(false);
        }
Beispiel #5
0
        private bool TryReferenceAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            // 参照の比較
            if (!ReferenceEquals(actual, expected))
            {
                return(false);
            }

            var actualType = actual.GetType();

            if (!actualType.IsDuckImplemented(targetType))
            {
                throw new PrimitiveAssertFailedException(node, "actual と expected は同じ参照ですが、ターゲット型に違反しています。", _message);
            }

            WriteLog(node, "actual と expected は同じ参照です。");
            return(true);
        }
Beispiel #6
0
        private bool TryPrimitiveAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            if (!targetType.IsPrimitiveData())
            {
                return(false);
            }

            if (!targetType.IsInstanceOfType(actual))
            {
                throw new PrimitiveAssertFailedException(node, "ターゲット型は基本データ型ですが、actual はターゲット型に違反しています。", _message);
            }
            if (!actual.Equals(expected))
            {
                throw new PrimitiveAssertFailedException(node, $"actual と expected は基本データ型として等しくありません。", _message);
            }

            WriteLog(node, $"actual と expected は {targetType.GetFriendlyName()} 型として等しいです。");
            return(true);
        }
Beispiel #7
0
        /// <exception cref="ArgumentException">ターゲット型に同じ名前のデータメンバーが 2 つ以上存在します。</exception>
        public void AssertIs(AssertNode node)
        {
            var targetType = node.TargetType;
            var actual     = node.Actual;
            var expected   = node.Expected;

            _logger?.Write($"{node.MemberName}: {node.TargetType?.GetFriendlyName() ?? "(null)"} = ");
            try {
                if (TryPredicateAssertIs(targetType, actual, expected, node))
                {
                    return;
                }

                // null 比較
                if (targetType is null)
                {
                    if (!(actual is null))
                    {
                        throw new PrimitiveAssertFailedException(node, "ターゲット型は null ですが、actual は非 null です。", _message);
                    }
                    if (!(expected is null))
                    {
                        throw new PrimitiveAssertFailedException(node, "actual は null ですが、expected は非 null です。", _message);
                    }

                    WriteLog(node, "actual と expected はどちらも null です。");
                    return;
                }
                if (targetType.IsValueType && !targetType.IsNullable())
                {
                    if (actual is null)
                    {
                        throw new PrimitiveAssertFailedException(node, "ターゲット型は null 非許容型ですが、actual は null です。", _message);
                    }
                }
                if (actual is null)
                {
                    if (expected is null)
                    {
                        WriteLog(node, "actual と expected はどちらも null です。");
                        return;
                    }
                    else
                    {
                        throw new PrimitiveAssertFailedException(node, "actual は null ですが、expected が非 null です。", _message);
                    }
                }
                else
                {
                    if (expected is null)
                    {
                        throw new PrimitiveAssertFailedException(node, "actual は非 null ですが、expected が null です。", _message);
                    }
                }

                if (TryNumericAssertIs(targetType, actual, expected, node))
                {
                    return;
                }
                if (TryPrimitiveAssertIs(targetType, actual, expected, node))
                {
                    return;
                }
                if (TryReferenceAssertIs(targetType, actual, expected, node))
                {
                    return;
                }
                if (TryCircularReferenceAssertIs(targetType, actual, expected, node))
                {
                    return;
                }
            }
            catch (PrimitiveAssertFailedException) {
                _logger?.WriteLine();
                throw;
            }

            if (_logger != null)
            {
                _logger.WriteLine("{");
                _logger.Indent++;
            }
            if (TryCollectionAssertIs(targetType, actual, expected, node))
            {
                goto EndBlock;
            }
            if (TryCompositeAssertIs(targetType, actual, expected, node))
            {
                goto EndBlock;
            }
EndBlock:
            if (_logger != null)
            {
                _logger.Indent--;
                _logger.WriteLine("}");
            }
            return;
        }
Beispiel #8
0
        private bool TryNumericAssertIs(Type targetType, object actual, object expected, AssertNode node)
        {
            if (!Numeric.IsNumeric(targetType))
            {
                return(false);
            }

            if (!Numeric.TryCreate(actual, out var actualNumeric))
            {
                throw new PrimitiveAssertFailedException(node, "ターゲット型は数値型ですが、actual は非数値型です。", _message);
            }
            if (!actualNumeric.Equals(expected))
            {
                throw new PrimitiveAssertFailedException(node, "actual と expected は数値型として等しくありません。", _message);
            }

            WriteLog(node, "actual と expected は数値型として等しいです。");
            return(true);
        }
Beispiel #9
0
        private bool TryPredicateAssertIs(Type?targetType, object?actual, object?expected, AssertNode node)
        {
            if (expected is null)
            {
                return(false);
            }

            if (expected is AssertPredicate predicate)
            {
                if (!predicate(actual))
                {
                    throw new PrimitiveAssertFailedException(node, "actual はカスタム条件に一致しませんでした。", _message);
                }

                WriteLog(node, "actual はカスタム条件に一致しました。");
                return(true);
            }

            Type expectedType = expected.GetType();

            if (expectedType.IsGenericType && expectedType.GetGenericTypeDefinition() == typeof(AssertPredicate <>))
            {
                Type genericType = expectedType.GetGenericArguments()[0];

                if (!genericType.IsAssignableFrom(actual?.GetType()))
                {
                    throw new PrimitiveAssertFailedException(node, "actual はカスタム条件の型引数にキャストできませんでした。", _message);
                }
                if (!(bool)expectedType.GetMethod("Invoke").Invoke(expected, new[] { actual }))
                {
                    throw new PrimitiveAssertFailedException(node, "actual はカスタム条件に一致しませんでした。", _message);
                }

                WriteLog(node, "actual はカスタム条件に一致しました。");
                return(true);
            }

            return(false);
        }
Beispiel #10
0
 internal PrimitiveAssertFailedException(AssertNode node, string reason, string?message) : base($"{message}: {reason}{Environment.NewLine}{node}")
 {
     Reason = reason;
     Node   = node;
 }