/// <summary> /// Runs the list of test methods. By default, orders the tests, groups them by method and runs them synchronously. /// </summary> /// <returns>Returns summary information about the tests that were run.</returns> protected virtual async Task <RunSummary> RunTestMethodsAsync() { var summary = new RunSummary(); IEnumerable <TTestCase> orderedTestCases; try { orderedTestCases = TestCaseOrderer.OrderTestCases(TestCases); } catch (Exception ex) { var innerEx = ex.Unwrap(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test case orderer '{TestCaseOrderer.GetType().FullName}' threw '{innerEx.GetType().FullName}' during ordering: {innerEx.Message}{Environment.NewLine}{innerEx.StackTrace}")); orderedTestCases = TestCases.ToList(); } var constructorArguments = CreateTestClassConstructorArguments(); foreach (var method in orderedTestCases.GroupBy(tc => tc.TestMethod, TestMethodComparer.Instance)) { summary.Aggregate(await RunTestMethodAsync(method.Key, (IReflectionMethodInfo)method.Key.Method, method, constructorArguments)); if (CancellationTokenSource.IsCancellationRequested) { break; } } return(summary); }
/// <summary> /// Orders the test collections using the TestCollectionOrderer. /// </summary> /// <returns>Test collections (and the associated test cases) in run order</returns> private List <Tuple <VsixTestCollection, List <IXunitTestCase> > > CreateTestCollections(IEnumerable <VsixTestCase> vsixTests) { var collections = new ConcurrentDictionary <string, ITestCollection>(); var testCases = from tc in vsixTests.OfType <VsixTestCase>() // For NewIdeInstance tests, every test case is its own new collection that will // run in parallel with the rest. Otherwise, it's a combination of VS + Suffix. let key = tc.NewIdeInstance.GetValueOrDefault() ? Guid.NewGuid().ToString() : tc.VisualStudioVersion + tc.RootSuffix let col = collections.GetOrAdd(key, x => new VsixTestCollection( TestAssembly, tc.TestMethod?.TestClass?.Class, tc.VisualStudioVersion, tc.RootSuffix)) select new { Collection = col, Test = tc }; var testCasesByCollection = testCases.GroupBy(tc => tc.Collection, TestCollectionComparer.Instance) .ToDictionary(group => group.Key, group => group.Select(x => (IXunitTestCase)x.Test).ToList()); IEnumerable <ITestCollection> orderedTestCollections; try { orderedTestCollections = TestCollectionOrderer.OrderTestCollections(testCasesByCollection.Keys); } catch (Exception ex) { var innerEx = ex.Unwrap(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Test collection orderer '{0}' threw '{1}' during ordering: {2}", TestCollectionOrderer.GetType().FullName, innerEx.GetType().FullName, innerEx.StackTrace)); orderedTestCollections = testCasesByCollection.Keys.ToList(); } return(orderedTestCollections .OfType <VsixTestCollection>() .Select(collection => Tuple.Create(collection, testCasesByCollection[collection])) .ToList()); }
/// <inheritdoc/> protected override void Initialize() { base.Initialize(); var factAttribute = TestMethod.Method.GetCustomAttributes(typeof(FactAttribute)).First(); var baseDisplayName = factAttribute.GetNamedArgument <string>("DisplayName") ?? BaseDisplayName; DisplayName = GetDisplayName(factAttribute, baseDisplayName); SkipReason = GetSkipReason(factAttribute); Timeout = GetTimeout(factAttribute); foreach (var traitAttribute in GetTraitAttributesData(TestMethod)) { var discovererAttribute = traitAttribute.GetCustomAttributes(typeof(TraitDiscovererAttribute)).FirstOrDefault(); if (discovererAttribute != null) { var discoverer = ExtensibilityPointFactory.GetTraitDiscoverer(DiagnosticMessageSink, discovererAttribute); if (discoverer != null) { foreach (var keyValuePair in discoverer.GetTraits(traitAttribute)) { Traits.Add(keyValuePair.Key, keyValuePair.Value); } } } else { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Trait attribute on '{DisplayName}' did not have [TraitDiscoverer]")); } } }
public static DiagnosticMessageSink ForInternalDiagnostics(LoggerHelper log, bool showDiagnostics) { var result = new DiagnosticMessageSink(); if (showDiagnostics) { result.DiagnosticMessageEvent += args => log.Log("{0}", args.Message.Message); } return(result); }
protected override bool IsValidTestClass(ITypeInfo type) { // only classes marked as test cases are valid var isTestCase = type.GetCustomAttributes(typeof(TestCaseAttribute)).Any(); if (!isTestCase) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Class {type.Name} is not a test case.")); } return(isTestCase); }
public static DiagnosticMessageSink ForDiagnostics(LoggerHelper log, string assemblyDisplayName, bool showDiagnostics) { var result = new DiagnosticMessageSink(); if (showDiagnostics) { result.DiagnosticMessageEvent += args => log.LogWarning("{0}: {1}", assemblyDisplayName, args.Message.Message); } return(result); }
private bool FindTestsForTypeAndWrapExceptions(ITestClass testClass, bool includeSourceInformation, IMessageBus messageBus, ITestFrameworkDiscoveryOptions discoveryOptions) { try { return(FindTestsForType(testClass, includeSourceInformation, messageBus, discoveryOptions)); } catch (Exception ex) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Exception during discovery:{0}{1}", Environment.NewLine, ex)); return(true); // Keep going on to the next type } }
/// <summary> /// Gets the test case discover instance for the given discoverer type. The instances are cached /// and reused, since they should not be stateful. /// </summary> /// <param name="discovererType">The discoverer type.</param> /// <returns>Returns the test case discoverer instance.</returns> protected IXunitTestCaseDiscoverer GetDiscoverer(Type discovererType) { try { return(ExtensibilityPointFactory.GetXunitTestCaseDiscoverer(DiagnosticMessageSink, discovererType)); } catch (Exception ex) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Discoverer type '{discovererType.FullName}' could not be created or does not implement IXunitTestCaseDiscoverer: {ex.Unwrap()}")); return(null); } }
/// <inheritdoc/> protected override async Task AfterTestClassStartingAsync() { var ordererAttribute = Class.GetCustomAttributes(typeof(TestCaseOrdererAttribute)).SingleOrDefault(); if (ordererAttribute != null) { try { var testCaseOrderer = ExtensibilityPointFactory.GetTestCaseOrderer(DiagnosticMessageSink, ordererAttribute); if (testCaseOrderer != null) { TestCaseOrderer = testCaseOrderer; } else { var args = ordererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Could not find type '{args[0]}' in {args[1]} for class-level test case orderer on test class '{TestClass.Class.Name}'")); } } catch (Exception ex) { var innerEx = ex.Unwrap(); var args = ordererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Class-level test case orderer '{args[0]}' for test class '{TestClass.Class.Name}' threw '{innerEx.GetType().FullName}' during construction: {innerEx.Message}{Environment.NewLine}{innerEx.StackTrace}")); } } var testClassTypeInfo = Class.Type.GetTypeInfo(); if (testClassTypeInfo.ImplementedInterfaces.Any(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(ICollectionFixture <>))) { Aggregator.Add(new TestClassException("A test class may not be decorated with ICollectionFixture<> (decorate the test collection class instead).")); } var createClassFixtureAsyncTasks = new List <Task>(); foreach (var interfaceType in testClassTypeInfo.ImplementedInterfaces.Where(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IClassFixture <>))) { createClassFixtureAsyncTasks.Add(CreateClassFixtureAsync(interfaceType.GetTypeInfo().GenericTypeArguments.Single())); } if (TestClass.TestCollection.CollectionDefinition != null) { var declarationType = ((IReflectionTypeInfo)TestClass.TestCollection.CollectionDefinition).Type; foreach (var interfaceType in declarationType.GetTypeInfo().ImplementedInterfaces.Where(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IClassFixture <>))) { createClassFixtureAsyncTasks.Add(CreateClassFixtureAsync(interfaceType.GetTypeInfo().GenericTypeArguments.Single())); } } await Task.WhenAll(createClassFixtureAsyncTasks); }
void RunTestsInAssembly(List <IDisposable> toDispose, AssemblyRunInfo runInfo) { if (cancelled) { return; } var assemblyFileName = runInfo.AssemblyFileName; var longRunningSeconds = runInfo.Configuration.LongRunningTestSecondsOrDefault; var controller = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName); lock (toDispose) toDispose.Add(controller); var xunitTestCases = runInfo.TestCases.Select(tc => new { vm = tc, tc = tc.TestCase }) .Where(tc => tc.tc.UniqueID != null) .ToDictionary(tc => tc.tc, tc => tc.vm); var executionOptions = TestFrameworkOptions.ForExecution(runInfo.Configuration); var diagSink = new DiagnosticMessageSink(d => context.Post(_ => OnDiagnosticMessage?.Invoke(d), null), runInfo.AssemblyFileName, executionOptions.GetDiagnosticMessagesOrDefault()); var deviceExecSink = new DeviceExecutionSink(xunitTestCases, this, context); IExecutionSink resultsSink = new DelegatingExecutionSummarySink(deviceExecSink, () => cancelled); if (longRunningSeconds > 0) { resultsSink = new DelegatingLongRunningTestDetectionSink(resultsSink, TimeSpan.FromSeconds(longRunningSeconds), diagSink); } var assm = new XunitProjectAssembly() { AssemblyFilename = runInfo.AssemblyFileName }; deviceExecSink.OnMessage(new TestAssemblyExecutionStarting(assm, executionOptions)); controller.RunTests(xunitTestCases.Select(tc => tc.Value.TestCase).ToList(), resultsSink, executionOptions); resultsSink.Finished.WaitOne(); deviceExecSink.OnMessage(new TestAssemblyExecutionFinished(assm, executionOptions, resultsSink.ExecutionSummary)); }
bool IsSpecificationClass(ITypeInfo typeInfo) { var type = typeInfo.ToRuntimeType(); while (type != null) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Test report.")); if (type == typeof(SpecificationBase)) { return(true); } type = type.BaseType; } return(false); }
/// <summary> /// Gets the test case discover instance for the given discoverer type. The instances are cached /// and reused, since they should not be stateful. /// </summary> /// <param name="discovererType">The discoverer type.</param> /// <returns>Returns the test case discoverer instance.</returns> protected IXunitTestCaseDiscoverer GetDiscoverer(Type discovererType) { if (!discovererCache.TryGetValue(discovererType, out IXunitTestCaseDiscoverer result)) { try { result = ExtensibilityPointFactory.GetXunitTestCaseDiscoverer(DiagnosticMessageSink, discovererType); } catch (Exception ex) { result = null; DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Discoverer type '{discovererType.FullName}' could not be created or does not implement IXunitTestCaseDiscoverer: {ex.Unwrap()}")); } discovererCache[discovererType] = result; } return(result); }
protected override bool FindTestsForType(ITestClass testClass, bool includeSourceInformation, IMessageBus messageBus, ITestFrameworkDiscoveryOptions discoveryOptions) { var type = testClass.Class; var summaryMethods = type.GetMethods(includePrivateMethods: false).Where(m => m.GetCustomAttributes(typeof(SummaryAttribute)).Any()).ToList(); if (summaryMethods.Count == 0) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test case class {type.Name} does not have a Summary method. This will not be discovered.")); return(true); } if (summaryMethods.Count > 1) { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test case class {type.Name} has more than one Summary methods. This will not be discovered.")); return(true); } return(base.FindTestsForType(testClass, includeSourceInformation, messageBus, discoveryOptions)); }
/// <summary> /// Orders the test collections using the <see cref="TestCollectionOrderer"/>. /// </summary> /// <returns>Test collections (and the associated test cases) in run order</returns> protected List <Tuple <ITestCollection, List <TTestCase> > > OrderTestCollections() { var testCasesByCollection = TestCases.GroupBy(tc => tc.TestMethod.TestClass.TestCollection, TestCollectionComparer.Instance) .ToDictionary(collectionGroup => collectionGroup.Key, collectionGroup => collectionGroup.ToList()); IEnumerable <ITestCollection> orderedTestCollections; try { orderedTestCollections = TestCollectionOrderer.OrderTestCollections(testCasesByCollection.Keys); } catch (Exception ex) { var innerEx = ex.Unwrap(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Test collection orderer '{0}' threw '{1}' during ordering: {2}", TestCollectionOrderer.GetType().FullName, innerEx.GetType().FullName, innerEx.StackTrace)); orderedTestCollections = testCasesByCollection.Keys.ToList(); } return(orderedTestCollections.Select(collection => Tuple.Create(collection, testCasesByCollection[collection])) .ToList()); }
protected async override Task <RunSummary> RunTestClassesAsync() { var groups = TestCases .GroupBy(tc => tc.TestMethod.TestClass, TestClassComparer.Instance); try { if (TestCaseOrderer is ITestClassOrderer orderer) { groups = orderer.OrderTestClasses(groups); } } catch (Exception ex) { if ((ex is TargetInvocationException tiex)) { ex = ex.InnerException; } DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test class orderer '{TestCaseOrderer.GetType().FullName}' threw '{ex.GetType().FullName}' during ordering: {ex.Message}{Environment.NewLine}{ex.StackTrace}")); } var summary = new RunSummary(); foreach (IGrouping <ITestClass, IXunitTestCase> testCasesByClass in groups) { summary.Aggregate( await RunTestClassAsync( testCasesByClass.Key, (IReflectionTypeInfo)testCasesByClass.Key.Class, testCasesByClass)); if (CancellationTokenSource.IsCancellationRequested) { break; } } return(summary); }
protected override async Task <RunSummary> RunTestMethodsAsync() { IEnumerable <IXunitTestCase> orderedTestCases; try { orderedTestCases = TestCaseOrderer.OrderTestCases(TestCases); } catch (Exception ex) { var innerEx = ex.Unwrap(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test case orderer '{TestCaseOrderer.GetType().FullName}' threw '{innerEx.GetType().FullName}' during ordering: {innerEx.Message}{Environment.NewLine}{innerEx.StackTrace}")); orderedTestCases = TestCases.ToList(); } var constructorArguments = CreateTestClassConstructorArguments(); var tasks = orderedTestCases .GroupBy(tc => tc.TestMethod, TestMethodComparer.Instance) .Select(method => (Func <Task <RunSummary> >)(() => RunTestMethodAsync(method.Key, (IReflectionMethodInfo)method.Key.Method, method, constructorArguments))) .ToArray(); return(await TaskExecutor.RunAsync(CancellationTokenSource.Token, tasks, TestClass)); }
/// <summary> /// Ensures the assembly runner is initialized (sets up the collection behavior, /// parallelization options, and test orderers from their assembly attributes). /// </summary> protected void Initialize() { if (initialized) { return; } collectionBehaviorAttribute = TestAssembly.Assembly.GetCustomAttributes(typeof(CollectionBehaviorAttribute)).SingleOrDefault(); if (collectionBehaviorAttribute != null) { disableParallelization = collectionBehaviorAttribute.GetNamedArgument <bool>("DisableTestParallelization"); maxParallelThreads = collectionBehaviorAttribute.GetNamedArgument <int>("MaxParallelThreads"); } disableParallelization = ExecutionOptions.DisableParallelization() ?? disableParallelization; var maxParallelThreadsOption = ExecutionOptions.MaxParallelThreads() ?? 0; if (maxParallelThreadsOption > 0) { maxParallelThreads = maxParallelThreadsOption; } var testCaseOrdererAttribute = TestAssembly.Assembly.GetCustomAttributes(typeof(TestCaseOrdererAttribute)).SingleOrDefault(); if (testCaseOrdererAttribute != null) { try { var testCaseOrderer = ExtensibilityPointFactory.GetTestCaseOrderer(DiagnosticMessageSink, testCaseOrdererAttribute); if (testCaseOrderer != null) { TestCaseOrderer = testCaseOrderer; } else { var args = testCaseOrdererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Could not find type '{0}' in {1} for assembly-level test case orderer", args[0], args[1])); } } catch (Exception ex) { var innerEx = ex.Unwrap(); var args = testCaseOrdererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Assembly-level test case orderer '{0}' threw '{1}' during construction: {2}", args[0], innerEx.GetType().FullName, innerEx.StackTrace)); } } var testCollectionOrdererAttribute = TestAssembly.Assembly.GetCustomAttributes(typeof(TestCollectionOrdererAttribute)).SingleOrDefault(); if (testCollectionOrdererAttribute != null) { try { var testCollectionOrderer = ExtensibilityPointFactory.GetTestCollectionOrderer(DiagnosticMessageSink, testCollectionOrdererAttribute); if (testCollectionOrderer != null) { TestCollectionOrderer = testCollectionOrderer; } else { var args = testCollectionOrdererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Could not find type '{0}' in {1} for assembly-level test collection orderer", args[0], args[1])); } } catch (Exception ex) { var innerEx = ex.Unwrap(); var args = testCollectionOrdererAttribute.GetConstructorArguments().Cast <string>().ToList(); DiagnosticMessageSink.OnMessage(new DiagnosticMessage("Assembly-level test collection orderer '{0}' threw '{1}' during construction: {2}", args[0], innerEx.GetType().FullName, innerEx.StackTrace)); } } initialized = true; }
protected override void OnSetUp() { DiagnosticMessageSink.OnMessage(new DiagnosticMessage("OnSetUp")); }
protected virtual XElement ExecuteAssembly(XunitProjectAssembly assembly) { if (cancel) return null; var assemblyElement = NeedsXml ? new XElement("assembly") : null; try { // Turn off pre-enumeration of theories, since there is no theory selection UI in this runner assembly.Configuration.PreEnumerateTheories = false; assembly.Configuration.DiagnosticMessages |= DiagnosticMessages; if (appDomains.HasValue) assembly.Configuration.AppDomain = appDomains.GetValueOrDefault() ? AppDomainSupport.Required : AppDomainSupport.Denied; // Setup discovery and execution options with command-line overrides var discoveryOptions = TestFrameworkOptions.ForDiscovery(assembly.Configuration); var executionOptions = TestFrameworkOptions.ForExecution(assembly.Configuration); if (maxThreadCount.HasValue && maxThreadCount.Value > -1) executionOptions.SetMaxParallelThreads(maxThreadCount); if (parallelizeTestCollections.HasValue) executionOptions.SetDisableParallelization(!parallelizeTestCollections); var assemblyDisplayName = Path.GetFileNameWithoutExtension(assembly.AssemblyFilename); var diagnosticMessageSink = new DiagnosticMessageSink(Log, assemblyDisplayName, assembly.Configuration.DiagnosticMessagesOrDefault); var appDomainSupport = assembly.Configuration.AppDomainOrDefault; var shadowCopy = assembly.Configuration.ShadowCopyOrDefault; var longRunningSeconds = assembly.Configuration.LongRunningTestSecondsOrDefault; using (var controller = new XunitFrontController(appDomainSupport, assembly.AssemblyFilename, assembly.ConfigFilename, shadowCopy, diagnosticMessageSink: diagnosticMessageSink)) using (var discoverySink = new TestDiscoverySink(() => cancel)) { // Discover & filter the tests reporterMessageHandler.OnMessage(new TestAssemblyDiscoveryStarting(assembly, controller.CanUseAppDomains && appDomainSupport != AppDomainSupport.Denied, shadowCopy, discoveryOptions)); controller.Find(false, discoverySink, discoveryOptions); discoverySink.Finished.WaitOne(); var testCasesDiscovered = discoverySink.TestCases.Count; var filteredTestCases = discoverySink.TestCases.Where(Filters.Filter).ToList(); var testCasesToRun = filteredTestCases.Count; reporterMessageHandler.OnMessage(new TestAssemblyDiscoveryFinished(assembly, discoveryOptions, testCasesDiscovered, testCasesToRun)); // Run the filtered tests if (testCasesToRun == 0) completionMessages.TryAdd(Path.GetFileName(assembly.AssemblyFilename), new ExecutionSummary()); else { if (SerializeTestCases) filteredTestCases = filteredTestCases.Select(controller.Serialize).Select(controller.Deserialize).ToList(); IExecutionSink resultsSink = new DelegatingExecutionSummarySink(reporterMessageHandler, () => cancel, (path, summary) => completionMessages.TryAdd(path, summary)); if (assemblyElement != null) resultsSink = new DelegatingXmlCreationSink(resultsSink, assemblyElement); if (longRunningSeconds > 0) resultsSink = new DelegatingLongRunningTestDetectionSink(resultsSink, TimeSpan.FromSeconds(longRunningSeconds), diagnosticMessageSink); if (FailSkips) resultsSink = new DelegatingFailSkipSink(resultsSink); reporterMessageHandler.OnMessage(new TestAssemblyExecutionStarting(assembly, executionOptions)); controller.RunTests(filteredTestCases, resultsSink, executionOptions); resultsSink.Finished.WaitOne(); reporterMessageHandler.OnMessage(new TestAssemblyExecutionFinished(assembly, executionOptions, resultsSink.ExecutionSummary)); if (resultsSink.ExecutionSummary.Failed != 0) ExitCode = 1; } } } catch (Exception ex) { var e = ex; while (e != null) { Log.LogError("{0}: {1}", e.GetType().FullName, e.Message); foreach (var stackLine in e.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) Log.LogError(stackLine); e = e.InnerException; } ExitCode = -1; } return assemblyElement; }
protected override void OnTearDown() { DiagnosticMessageSink.OnMessage(new DiagnosticMessage("OnTearDown")); }
/// <inheritdoc/> protected override void Initialize() { base.Initialize(); var serviceProviderFactoryAttribute = TestMethod.Method .GetCustomAttributes(typeof(ServiceProviderFactoryAttribute)) .SingleOrDefault(); if (serviceProviderFactoryAttribute == null) { serviceProviderFactoryAttribute = TestMethod.TestClass.Class.Assembly .GetCustomAttributes(typeof(ServiceProviderFactoryAttribute)) .SingleOrDefault(); } if (serviceProviderFactoryAttribute != null) { var factoryType = serviceProviderFactoryAttribute.GetNamedArgument <Type>("FactoryType"); // TODO: consider diagnostic message if the type exists but not the interface. if (factoryType != null && factoryType.GetInterface("IServiceProviderFactory") != null) { var serviceFactory = (IServiceProviderFactory)Activator.CreateInstance(factoryType); this.serviceProvider = serviceFactory.CreateProvider(); } } else { this.serviceProvider = new SimpleServiceProvider(); } bool isTestCase = true; var factAttribute = TestMethod.Method.GetCustomAttributes(typeof(TestCaseAttribute)).FirstOrDefault(); if (factAttribute == null) { factAttribute = TestMethod.Method.GetCustomAttributes(typeof(FactAttribute)).FirstOrDefault(); isTestCase = false; } var baseDisplayName = factAttribute.GetNamedArgument <string>("DisplayName") ?? BaseDisplayName; DisplayName = GetDisplayName(factAttribute, baseDisplayName); SkipReason = GetSkipReason(factAttribute); Timeout = GetTimeout(factAttribute); if (isTestCase) { string id = factAttribute.GetNamedArgument <string>("Id"); string ticket = factAttribute.GetNamedArgument <string>("Ticket"); string tags = factAttribute.GetNamedArgument <string>("Tags"); if (!string.IsNullOrWhiteSpace(id)) { Traits.Add("id", id); } if (!string.IsNullOrWhiteSpace(ticket)) { Traits.Add("ticket", ticket); } if (!string.IsNullOrWhiteSpace(tags)) { var set = tags.Split(';').Select(o => o.Trim()).ToArray(); foreach (var tag in set) { Traits.Add("Category", tag); Traits.Add("tag", tag); } } } foreach (var traitAttribute in GetTraitAttributesData(TestMethod)) { var discovererAttribute = traitAttribute.GetCustomAttributes(typeof(TraitDiscovererAttribute)).FirstOrDefault(); if (discovererAttribute != null) { var discoverer = ExtensibilityPointFactory.GetTraitDiscoverer(DiagnosticMessageSink, discovererAttribute); if (discoverer != null) { foreach (var keyValuePair in discoverer.GetTraits(traitAttribute)) { Traits.Add(keyValuePair.Key, keyValuePair.Value); } } } else { DiagnosticMessageSink.OnMessage(new DiagnosticMessage($"Trait attribute on '{DisplayName}' did not have [TraitDiscoverer]")); } } }