예제 #1
0
        /// <inheritdoc/>
        public virtual IEnumerable <object?[]>?GetData(
            _IAttributeInfo dataAttribute,
            _IMethodInfo testMethod)
        {
            Guard.ArgumentNotNull(nameof(dataAttribute), dataAttribute);
            Guard.ArgumentNotNull(nameof(testMethod), testMethod);

            if (dataAttribute is _IReflectionAttributeInfo reflectionDataAttribute &&
                testMethod is _IReflectionMethodInfo reflectionTestMethod)
            {
                var attribute = (DataAttribute)reflectionDataAttribute.Attribute;

                try
                {
                    return(attribute.GetData(reflectionTestMethod.MethodInfo));
                }
                catch (ArgumentException)
                {
                    // If we couldn't find the data on the base type, check if it is in current type.
                    // This allows base classes to specify data that exists on a sub type, but not on the base type.
                    var reflectionTestMethodType = (_IReflectionTypeInfo)reflectionTestMethod.Type;
                    if (attribute is MemberDataAttribute memberDataAttribute && memberDataAttribute.MemberType == null)
                    {
                        memberDataAttribute.MemberType = reflectionTestMethodType.Type;
                    }

                    return(attribute.GetData(reflectionTestMethod.MethodInfo));
                }
            }

            return(null);
        }
예제 #2
0
        /// <inheritdoc/>
        public virtual ValueTask <IReadOnlyCollection <ITheoryDataRow>?> GetData(
            _IAttributeInfo dataAttribute,
            _IMethodInfo testMethod)
        {
            Guard.ArgumentNotNull(dataAttribute);
            Guard.ArgumentNotNull(testMethod);

            if (dataAttribute is _IReflectionAttributeInfo reflectionDataAttribute &&
                testMethod is _IReflectionMethodInfo reflectionTestMethod)
            {
                var attribute = (DataAttribute)reflectionDataAttribute.Attribute;

                try
                {
                    return(attribute.GetData(reflectionTestMethod.MethodInfo));
                }
                catch (ArgumentException)
                {
                    // If we couldn't find the data on the base type, check if it is in current type.
                    // This allows base classes to specify data that exists on a sub type, but not on the base type.
                    var reflectionTestMethodType = (_IReflectionTypeInfo)reflectionTestMethod.Type;
                    if (attribute is MemberDataAttribute memberDataAttribute && memberDataAttribute.MemberType == null)
                    {
                        memberDataAttribute.MemberType = reflectionTestMethodType.Type;
                    }

                    return(attribute.GetData(reflectionTestMethod.MethodInfo));
                }
            }

            return(new(default(IReadOnlyCollection <ITheoryDataRow>)));
        }
예제 #3
0
        /// <summary>
        /// Discover test cases from a test method. By default, if the method is generic, or
        /// it contains arguments, returns a single <see cref="ExecutionErrorTestCase"/>;
        /// otherwise, it returns the result of calling <see cref="CreateTestCase"/>.
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="factAttribute">The fact attribute attached to the test method.</param>
        /// <returns>Returns zero or more test cases represented by the test method.</returns>
        public virtual IEnumerable <IXunitTestCase> Discover(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo factAttribute)
        {
            Guard.ArgumentNotNull(nameof(discoveryOptions), discoveryOptions);
            Guard.ArgumentNotNull(nameof(testMethod), testMethod);
            Guard.ArgumentNotNull(nameof(factAttribute), factAttribute);

            IXunitTestCase testCase;

            if (testMethod.Method.GetParameters().Any())
            {
                testCase = ErrorTestCase(discoveryOptions, testMethod, "[Fact] methods are not allowed to have parameters. Did you mean to use [Theory]?");
            }
            else if (testMethod.Method.IsGenericMethodDefinition)
            {
                testCase = ErrorTestCase(discoveryOptions, testMethod, "[Fact] methods are not allowed to be generic.");
            }
            else
            {
                testCase = CreateTestCase(discoveryOptions, testMethod, factAttribute);
            }

            return(new[] { testCase });
        }
예제 #4
0
    /// <summary>
    /// Gets all the custom attributes for the given attribute.
    /// </summary>
    /// <param name="attributeInfo">The attribute</param>
    /// <param name="attributeType">The type of the attribute to find</param>
    /// <returns>The matching attributes that decorate the attribute</returns>
    public static IEnumerable <_IAttributeInfo> GetCustomAttributes(this _IAttributeInfo attributeInfo, Type attributeType)
    {
        Guard.ArgumentNotNull(nameof(attributeInfo), attributeInfo);
        Guard.ArgumentNotNull(nameof(attributeType), attributeType);
        Guard.NotNull("Attribute type cannot be a generic type parameter", attributeType.AssemblyQualifiedName);

        return(attributeInfo.GetCustomAttributes(attributeType.AssemblyQualifiedName));
    }
예제 #5
0
        /// <inheritdoc/>
        public virtual IEnumerable <KeyValuePair <string, string> > GetTraits(_IAttributeInfo traitAttribute)
        {
            Guard.ArgumentNotNull(nameof(traitAttribute), traitAttribute);

            var ctorArgs = traitAttribute.GetConstructorArguments().Cast <string>().ToList();

            yield return(new KeyValuePair <string, string>(ctorArgs[0], ctorArgs[1]));
        }
예제 #6
0
    /// <summary>
    /// Gets all the custom attributes for the given attribute.
    /// </summary>
    /// <param name="attributeInfo">The attribute</param>
    /// <param name="attributeType">The type of the attribute to find</param>
    /// <returns>The matching attributes that decorate the attribute</returns>
    public static IReadOnlyCollection <_IAttributeInfo> GetCustomAttributes(this _IAttributeInfo attributeInfo, Type attributeType)
    {
        Guard.ArgumentNotNull(attributeInfo);
        Guard.ArgumentNotNull(attributeType);
        Guard.NotNull("Attribute type cannot be a generic type parameter", attributeType.AssemblyQualifiedName);

        return(attributeInfo.GetCustomAttributes(attributeType.AssemblyQualifiedName));
    }
예제 #7
0
        /// <inheritdoc/>
        public virtual bool SupportsDiscoveryEnumeration(
            _IAttributeInfo dataAttribute,
            _IMethodInfo testMethod)
        {
            Guard.ArgumentNotNull(dataAttribute);
            Guard.ArgumentNotNull(testMethod);

            return(true);
        }
예제 #8
0
        /// <inheritdoc/>
        public override bool SupportsDiscoveryEnumeration(
            _IAttributeInfo dataAttribute,
            _IMethodInfo testMethod)
        {
            Guard.ArgumentNotNull(dataAttribute);
            Guard.ArgumentNotNull(testMethod);

            return(!dataAttribute.GetNamedArgument <bool>("DisableDiscoveryEnumeration"));
        }
예제 #9
0
        /// <summary>
        /// Gets a test case orderer, as specified in a reflected <see cref="TestCaseOrdererAttribute"/>.
        /// </summary>
        /// <param name="testCaseOrdererAttribute">The test case orderer attribute.</param>
        /// <returns>The test case orderer, if the type is loadable; <c>null</c>, otherwise.</returns>
        public static ITestCaseOrderer?GetTestCaseOrderer(_IAttributeInfo testCaseOrdererAttribute)
        {
            Guard.ArgumentNotNull(testCaseOrdererAttribute);

            var ordererType = TypeFromAttributeConstructor(testCaseOrdererAttribute);

            if (ordererType == null)
            {
                return(null);
            }

            return(GetTestCaseOrderer(ordererType));
        }
예제 #10
0
        /// <summary>
        /// Gets a data discoverer, as specified in a reflected <see cref="DataDiscovererAttribute"/>.
        /// </summary>
        /// <param name="dataDiscovererAttribute">The data discoverer attribute</param>
        /// <returns>The data discoverer, if the type is loadable; <c>null</c>, otherwise.</returns>
        public static IDataDiscoverer?GetDataDiscoverer(_IAttributeInfo dataDiscovererAttribute)
        {
            Guard.ArgumentNotNull(dataDiscovererAttribute);

            var discovererType = TypeFromAttributeConstructor(dataDiscovererAttribute);

            if (discovererType == null)
            {
                return(null);
            }

            return(GetDataDiscoverer(discovererType));
        }
예제 #11
0
        /// <summary>
        /// Gets a test framework discoverer, as specified in a reflected <see cref="TestFrameworkDiscovererAttribute"/>.
        /// </summary>
        /// <param name="testFrameworkDiscovererAttribute">The test framework discoverer attribute</param>
        public static ITestFrameworkTypeDiscoverer?GetTestFrameworkTypeDiscoverer(_IAttributeInfo testFrameworkDiscovererAttribute)
        {
            Guard.ArgumentNotNull(testFrameworkDiscovererAttribute);

            var testFrameworkDiscovererType = TypeFromAttributeConstructor(testFrameworkDiscovererAttribute);

            if (testFrameworkDiscovererType == null)
            {
                return(null);
            }

            return(GetTestFrameworkTypeDiscoverer(testFrameworkDiscovererType));
        }
예제 #12
0
        /// <summary>
        /// Creates test cases for the entire theory. This is used when one or more of the theory data items
        /// are not serializable, or if the user has requested to skip theory pre-enumeration. By default,
        /// returns a single instance of <see cref="XunitDelayEnumeratedTheoryTestCase"/>, which performs the data discovery
        /// at runtime.
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <returns>The test case</returns>
        protected virtual ValueTask <IReadOnlyCollection <IXunitTestCase> > CreateTestCasesForTheory(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute)
        {
            var testCase = new XunitDelayEnumeratedTheoryTestCase(
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod
                );

            return(new(new[] { testCase }));
        }
예제 #13
0
        static string[] GetCultures(_IAttributeInfo culturedTheoryAttribute)
        {
            var ctorArgs = culturedTheoryAttribute.GetConstructorArguments().ToArray();
            var cultures = Reflector.ConvertArguments(ctorArgs, new[] { typeof(string[]) }).Cast <string[]>().Single();

            if (cultures == null || cultures.Length == 0)
            {
                cultures = new[] { "en-US", "fr-FR" }
            }
            ;

            return(cultures);
        }
    }
예제 #14
0
        /// <summary>
        /// Creates test cases for the entire theory. This is used when one or more of the theory data items
        /// are not serializable, or if the user has requested to skip theory pre-enumeration. By default,
        /// returns a single instance of <see cref="XunitDelayEnumeratedTheoryTestCase"/>, which performs the data discovery
        /// at runtime.
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <returns>The test case</returns>
        protected virtual IEnumerable <IXunitTestCase> CreateTestCasesForTheory(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute)
        {
            var testCase = new XunitDelayEnumeratedTheoryTestCase(
                DiagnosticMessageSink,
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod
                );

            return(new[] { testCase });
        }
예제 #15
0
        /// <summary>
        /// Creates test cases for a skipped theory. By default, returns a single instance of <see cref="XunitTestCase"/>
        /// (which inherently discovers the skip reason via the fact attribute).
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <param name="skipReason">The skip reason that decorates <paramref name="theoryAttribute"/>.</param>
        /// <returns>The test cases</returns>
        protected virtual ValueTask <IReadOnlyCollection <IXunitTestCase> > CreateTestCasesForSkippedTheory(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute,
            string skipReason)
        {
            // TODO: Skip reason should be passed down into the test case
            var testCase = new XunitTestCase(
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod
                );

            return(new(new[] { testCase }));
        }
        /// <inheritdoc/>
        public Type?GetTestFrameworkType(_IAttributeInfo attribute)
        {
            Guard.ArgumentNotNull(nameof(attribute), attribute);

            var args = attribute.GetConstructorArguments().ToArray();

            if (args.Length == 1)
            {
                return((Type)args[0] !);
            }

            var stringArgs = args.Cast <string>().ToArray();

            return(SerializationHelper.GetType(stringArgs[1], stringArgs[0]));
        }
예제 #17
0
        /// <summary>
        /// Creates a single <see cref="XunitTestCase"/> for the given test method.
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method.</param>
        /// <param name="factAttribute">The attribute that decorates the test method.</param>
        protected virtual IXunitTestCase CreateTestCase(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo factAttribute)
        {
            Guard.ArgumentNotNull(discoveryOptions);
            Guard.ArgumentNotNull(testMethod);
            Guard.ArgumentNotNull(factAttribute);

            return(new XunitTestCase(
                       discoveryOptions.MethodDisplayOrDefault(),
                       discoveryOptions.MethodDisplayOptionsOrDefault(),
                       testMethod
                       ));
        }
예제 #18
0
        /// <summary>
        /// Creates test cases for a skipped theory. By default, returns a single instance of <see cref="XunitTestCase"/>
        /// (which inherently discovers the skip reason via the fact attribute).
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <param name="skipReason">The skip reason that decorates <paramref name="theoryAttribute"/>.</param>
        /// <returns>The test cases</returns>
        protected virtual IEnumerable <IXunitTestCase> CreateTestCasesForSkip(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute,
            string skipReason)
        {
            // TODO: Skip reason should be passed down into the test case
            var testCase = new XunitTestCase(
                DiagnosticMessageSink,
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod
                );

            return(new[] { testCase });
        }
예제 #19
0
        /// <inheritdoc/>
        public virtual IEnumerable <object?[]> GetData(
            _IAttributeInfo dataAttribute,
            _IMethodInfo testMethod)
        {
            // The data from GetConstructorArguments does not maintain its original form (in particular, collections
            // end up as generic IEnumerable<T>). So we end up needing to call .ToArray() on the enumerable so that
            // we can restore the correct argument type from InlineDataAttribute.
            //
            // In addition, [InlineData(null)] gets translated into passing a null array, not a single array with a null
            // value in it, which is why the null coalesce operator is required (this is covered by the acceptance test
            // in Xunit2TheoryAcceptanceTests.InlineDataTests.SingleNullValuesWork).

            var args = (IEnumerable <object?>?)dataAttribute.GetConstructorArguments().Single() ?? new object?[] { null };

            return(new[] { args.ToArray() });
        }
        protected override IEnumerable <IXunitTestCase> CreateTestCasesForTheory(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute)
        {
            var cultures = GetCultures(theoryAttribute);

            return(cultures.Select(
                       culture => new CulturedXunitTheoryTestCase(
                           DiagnosticMessageSink,
                           discoveryOptions.MethodDisplayOrDefault(),
                           discoveryOptions.MethodDisplayOptionsOrDefault(),
                           testMethod,
                           culture
                           )
                       ).ToList());
        }
예제 #21
0
        protected override ValueTask <IReadOnlyCollection <IXunitTestCase> > CreateTestCasesForTheory(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute)
        {
            var cultures = GetCultures(theoryAttribute);
            var result   = cultures.Select(
                culture => new CulturedXunitTheoryTestCase(
                    discoveryOptions.MethodDisplayOrDefault(),
                    discoveryOptions.MethodDisplayOptionsOrDefault(),
                    testMethod,
                    culture
                    )
                ).CastOrToReadOnlyCollection();

            return(new(result));
        }
예제 #22
0
        /// <summary>
        /// Creates test cases for a single row of skipped data. By default, returns a single instance of <see cref="XunitSkippedDataRowTestCase"/>
        /// with the data row inside of it.
        /// </summary>
        /// <remarks>If this method is overridden, the implementation will have to override <see cref="TestMethodTestCase.SkipReason"/> otherwise
        /// the default behavior will look at the <see cref="TheoryAttribute"/> and the test case will not be skipped.</remarks>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <param name="dataRow">The row of data for this test case.</param>
        /// <param name="skipReason">The reason this test case is to be skipped</param>
        /// <returns>The test cases</returns>
        protected virtual IEnumerable <IXunitTestCase> CreateTestCasesForSkippedDataRow(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute,
            object?[] dataRow,
            string skipReason)
        {
            var testCase = new XunitSkippedDataRowTestCase(
                DiagnosticMessageSink,
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod,
                dataRow
                ,
                skipReason);

            return(new[] { testCase });
        }
예제 #23
0
        /// <summary>
        /// Creates test cases for a single row of data. By default, returns a single instance of <see cref="XunitTestCase"/>
        /// with the data row inside of it.
        /// </summary>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <param name="displayName">The optional display name for the test</param>
        /// <param name="traits">The traits associated with the test case.</param>
        /// <param name="dataRow">The row of data for this test case.</param>
        /// <returns>The test cases</returns>
        protected virtual ValueTask <IReadOnlyCollection <IXunitTestCase> > CreateTestCasesForDataRow(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute,
            string?displayName,
            Dictionary <string, List <string> >?traits,
            object?[] dataRow)
        {
            var testCase = new XunitPreEnumeratedTheoryTestCase(
                discoveryOptions.MethodDisplayOrDefault(),
                discoveryOptions.MethodDisplayOptionsOrDefault(),
                testMethod,
                dataRow,
                traits: traits,
                displayName: displayName
                );

            return(new(new[] { testCase }));
        }
예제 #24
0
        public ValueTask <IReadOnlyCollection <IXunitTestCase> > Discover(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo factAttribute)
        {
            var ctorArgs = factAttribute.GetConstructorArguments().ToArray();
            var cultures = Reflector.ConvertArguments(ctorArgs, new[] { typeof(string[]) }).Cast <string[]>().Single();

            if (cultures == null || cultures.Length == 0)
            {
                cultures = new[] { "en-US", "fr-FR" }
            }
            ;

            var methodDisplay        = discoveryOptions.MethodDisplayOrDefault();
            var methodDisplayOptions = discoveryOptions.MethodDisplayOptionsOrDefault();

            var result =
                cultures
                .Select(culture => CreateTestCase(testMethod, culture, methodDisplay, methodDisplayOptions))
                .CastOrToReadOnlyCollection();

            return(new(result));
        }

        CulturedXunitTestCase CreateTestCase(
            _ITestMethod testMethod,
            string culture,
            TestMethodDisplay methodDisplay,
            TestMethodDisplayOptions methodDisplayOptions)
        {
            return(new CulturedXunitTestCase(
                       methodDisplay,
                       methodDisplayOptions,
                       testMethod,
                       culture
                       ));
        }
    }
예제 #25
0
        protected override ValueTask <IReadOnlyCollection <IXunitTestCase> > CreateTestCasesForDataRow(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute,
            string?displayName,
            Dictionary <string, List <string> >?traits,
            object?[] dataRow)
        {
            var cultures = GetCultures(theoryAttribute);
            var result   = cultures.Select(
                culture => new CulturedXunitTestCase(
                    discoveryOptions.MethodDisplayOrDefault(),
                    discoveryOptions.MethodDisplayOptionsOrDefault(),
                    testMethod,
                    culture,
                    dataRow,
                    traits,
                    displayName)
                ).CastOrToReadOnlyCollection();

            return(new(result));
        }
예제 #26
0
        public IEnumerable <IXunitTestCase> Discover(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo factAttribute)
        {
            var ctorArgs = factAttribute.GetConstructorArguments().ToArray();
            var cultures = Reflector.ConvertArguments(ctorArgs, new[] { typeof(string[]) }).Cast <string[]>().Single();

            if (cultures == null || cultures.Length == 0)
            {
                cultures = new[] { "en-US", "fr-FR" }
            }
            ;

            var methodDisplay        = discoveryOptions.MethodDisplayOrDefault();
            var methodDisplayOptions = discoveryOptions.MethodDisplayOptionsOrDefault();

            return
                (cultures
                 .Select(culture => CreateTestCase(testMethod, culture, methodDisplay, methodDisplayOptions))
                 .ToList());
        }

        CulturedXunitTestCase CreateTestCase(
            _ITestMethod testMethod,
            string culture,
            TestMethodDisplay methodDisplay,
            TestMethodDisplayOptions methodDisplayOptions)
        {
            return(new CulturedXunitTestCase(
                       diagnosticMessageSink,
                       methodDisplay,
                       methodDisplayOptions,
                       testMethod,
                       culture
                       ));
        }
    }
예제 #27
0
            public IEnumerable <KeyValuePair <string, string> > GetTraits(_IAttributeInfo traitAttribute)
            {
                var ctorArgs = traitAttribute.GetConstructorArguments().ToList();

                yield return(new KeyValuePair <string, string>("Bug", ctorArgs[0] !.ToString() !));
            }
예제 #28
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Xunit2AttributeInfo"/> class.
 /// </summary>
 /// <param name="v3AttributeInfo">The v3 attribute info to wrap.</param>
 public Xunit2AttributeInfo(_IAttributeInfo v3AttributeInfo)
 {
     V3AttributeInfo = Guard.ArgumentNotNull(nameof(v3AttributeInfo), v3AttributeInfo);
 }
예제 #29
0
        /// <summary>
        /// Discover test cases from a test method.
        /// </summary>
        /// <remarks>
        /// This method performs the following steps:
        /// - If the theory attribute is marked with Skip, returns the single test case from <see cref="CreateTestCasesForSkippedTheory"/>;
        /// - If pre-enumeration is off, or any of the test data is non serializable, returns the single test case from <see cref="CreateTestCasesForTheory"/>;
        /// - If there is no theory data, returns a single test case of <see cref="ExecutionErrorTestCase"/> with the error in it;
        /// - Otherwise, it returns one test case per data row, created by calling <see cref="CreateTestCasesForDataRow"/> or <see cref="CreateTestCasesForSkippedDataRow"/> if the data attribute has a skip reason.
        /// </remarks>
        /// <param name="discoveryOptions">The discovery options to be used.</param>
        /// <param name="testMethod">The test method the test cases belong to.</param>
        /// <param name="theoryAttribute">The theory attribute attached to the test method.</param>
        /// <returns>Returns zero or more test cases represented by the test method.</returns>
        public virtual async ValueTask <IReadOnlyCollection <IXunitTestCase> > Discover(
            _ITestFrameworkDiscoveryOptions discoveryOptions,
            _ITestMethod testMethod,
            _IAttributeInfo theoryAttribute)
        {
            Guard.ArgumentNotNull(discoveryOptions);
            Guard.ArgumentNotNull(testMethod);
            Guard.ArgumentNotNull(theoryAttribute);

            // Special case Skip, because we want a single Skip (not one per data item); plus, a skipped test may
            // not actually have any data (which is quasi-legal, since it's skipped).
            var skipReason = theoryAttribute.GetNamedArgument <string>("Skip");

            if (skipReason != null)
            {
                return(await CreateTestCasesForSkippedTheory(discoveryOptions, testMethod, theoryAttribute, skipReason));
            }

            var preEnumerate =
                discoveryOptions.PreEnumerateTheoriesOrDefault() &&
                !theoryAttribute.GetNamedArgument <bool>(nameof(TheoryAttribute.DisableDiscoveryEnumeration));

            if (preEnumerate)
            {
                try
                {
                    var dataAttributes = testMethod.Method.GetCustomAttributes(typeof(DataAttribute));
                    var results        = new List <IXunitTestCase>();

                    foreach (var dataAttribute in dataAttributes)
                    {
                        var             discovererAttribute = dataAttribute.GetCustomAttributes(typeof(DataDiscovererAttribute)).First();
                        IDataDiscoverer?discoverer;
                        try
                        {
                            discoverer = ExtensibilityPointFactory.GetDataDiscoverer(discovererAttribute);
                        }
                        catch (InvalidCastException)
                        {
                            if (dataAttribute is _IReflectionAttributeInfo reflectionAttribute)
                            {
                                results.Add(
                                    new ExecutionErrorTestCase(
                                        discoveryOptions.MethodDisplayOrDefault(),
                                        discoveryOptions.MethodDisplayOptionsOrDefault(),
                                        testMethod,
                                        $"Data discoverer specified for {reflectionAttribute.Attribute.GetType()} on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not implement IDataDiscoverer."
                                        )
                                    );
                            }
                            else
                            {
                                results.Add(
                                    new ExecutionErrorTestCase(
                                        discoveryOptions.MethodDisplayOrDefault(),
                                        discoveryOptions.MethodDisplayOptionsOrDefault(),
                                        testMethod,
                                        $"A data discoverer specified on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not implement IDataDiscoverer."
                                        )
                                    );
                            }

                            continue;
                        }

                        if (discoverer == null)
                        {
                            if (dataAttribute is _IReflectionAttributeInfo reflectionAttribute)
                            {
                                results.Add(
                                    new ExecutionErrorTestCase(
                                        discoveryOptions.MethodDisplayOrDefault(),
                                        discoveryOptions.MethodDisplayOptionsOrDefault(),
                                        testMethod,
                                        $"Data discoverer specified for {reflectionAttribute.Attribute.GetType()} on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not exist."
                                        )
                                    );
                            }
                            else
                            {
                                results.Add(
                                    new ExecutionErrorTestCase(
                                        discoveryOptions.MethodDisplayOrDefault(),
                                        discoveryOptions.MethodDisplayOptionsOrDefault(),
                                        testMethod,
                                        $"A data discoverer specified on {testMethod.TestClass.Class.Name}.{testMethod.Method.Name} does not exist."
                                        )
                                    );
                            }

                            continue;
                        }

                        skipReason = dataAttribute.GetNamedArgument <string>("Skip");

                        if (!discoverer.SupportsDiscoveryEnumeration(dataAttribute, testMethod.Method))
                        {
                            return(await CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute));
                        }

                        var data = await discoverer.GetData(dataAttribute, testMethod.Method);

                        if (data == null)
                        {
                            results.Add(
                                new ExecutionErrorTestCase(
                                    discoveryOptions.MethodDisplayOrDefault(),
                                    discoveryOptions.MethodDisplayOptionsOrDefault(),
                                    testMethod,
                                    $"Test data returned null for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}. Make sure it is statically initialized before this test method is called."
                                    )
                                );

                            continue;
                        }

                        foreach (var dataRow in data)
                        {
                            // Determine whether we can serialize the test case, since we need a way to uniquely
                            // identify a test and serialization is the best way to do that. If it's not serializable,
                            // this will throw and we will fall back to a single theory test case that gets its data at runtime.
                            // Also, if we can, we should attempt to resolve it to its parameter type right now, because
                            // the incoming data might be serializable but the actual parameter value that it gets converted
                            // to might not be, and serialization uses the resolved argument and not the input argument.
                            var resolvedData      = dataRow.GetData();
                            var dataRowSkipReason = skipReason ?? dataRow.Skip;
                            if (testMethod.Method is _IReflectionMethodInfo reflectionMethodInfo)
                            {
                                resolvedData = reflectionMethodInfo.MethodInfo.ResolveMethodArguments(resolvedData);
                            }

                            if (!resolvedData.All(d => SerializationHelper.IsSerializable(d)))
                            {
                                var typeNames =
                                    resolvedData
                                    .Select(x => x?.GetType().FullName)
                                    .WhereNotNull()
                                    .Select(x => $"'{x}'")
                                    .ToList();

                                TestContext.Current?.SendDiagnosticMessage(
                                    "Non-serializable data (one or more of: {0}) found for '{1}.{2}'; falling back to single test case.",
                                    string.Join(", ", typeNames),
                                    testMethod.TestClass.Class.Name,
                                    testMethod.Method.Name
                                    );

                                return(await CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute));
                            }

                            try
                            {
                                var testCases =
                                    dataRowSkipReason != null
                                                                                ? CreateTestCasesForSkippedDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow.TestDisplayName, dataRow.Traits, resolvedData, dataRowSkipReason)
                                                                                : CreateTestCasesForDataRow(discoveryOptions, testMethod, theoryAttribute, dataRow.TestDisplayName, dataRow.Traits, resolvedData);

                                results.AddRange(await testCases);
                            }
                            catch (Exception ex)
                            {
                                TestContext.Current?.SendDiagnosticMessage(
                                    "Error creating theory test case for for '{0}.{1}'; falling back to single test case. Exception message: '{2}'",
                                    testMethod.TestClass.Class.Name,
                                    testMethod.Method.Name,
                                    ex.Message
                                    );

                                return(await CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute));
                            }
                        }
                    }

                    if (results.Count == 0)
                    {
                        results.Add(
                            new ExecutionErrorTestCase(
                                discoveryOptions.MethodDisplayOrDefault(),
                                discoveryOptions.MethodDisplayOptionsOrDefault(),
                                testMethod,
                                $"No data found for {testMethod.TestClass.Class.Name}.{testMethod.Method.Name}"
                                )
                            );
                    }

                    return(results);
                }
                catch (Exception ex)                    // If something goes wrong, fall through to return just the XunitTestCase
                {
                    TestContext.Current?.SendDiagnosticMessage(
                        "Exception thrown during theory discovery on '{0}.{1}'; falling back to single test case.{2}{3}",
                        testMethod.TestClass.Class.Name,
                        testMethod.Method.Name,
                        Environment.NewLine,
                        ex
                        );
                }
            }

            return(await CreateTestCasesForTheory(discoveryOptions, testMethod, theoryAttribute));
        }
예제 #30
0
 public IReadOnlyCollection <KeyValuePair <string, string> > GetTraits(_IAttributeInfo traitAttribute) =>
 new[]
 {
     new KeyValuePair <string, string>("Foo", "Biff"),
     new KeyValuePair <string, string>("Baz", "2112")
 };