diff --git a/src/KiBoards.Xunit/Services/KiBoardsElasticClient.cs b/src/KiBoards.Xunit/Services/KiBoardsElasticClient.cs index 27a31b8..c90ea96 100644 --- a/src/KiBoards.Xunit/Services/KiBoardsElasticClient.cs +++ b/src/KiBoards.Xunit/Services/KiBoardsElasticClient.cs @@ -1,6 +1,5 @@ using Nest; using Xunit.Abstractions; -using Xunit.Sdk; namespace KiBoards.Services { @@ -22,12 +21,12 @@ public async Task IndexDocumentAsync(T document) where T: class if (!result.IsValid) { - _messageSink.OnMessage(new DiagnosticMessage(result.DebugInformation)); + _messageSink.WriteMessage(result.DebugInformation); } } catch (Exception ex) { - _messageSink.OnMessage(new DiagnosticMessage(ex.Message)); + _messageSink.WriteException(ex); } } } diff --git a/src/KiBoards.Xunit/Services/KiBoardsTestRunner.cs b/src/KiBoards.Xunit/Services/KiBoardsTestRunner.cs index 9779e66..41ce1ee 100644 --- a/src/KiBoards.Xunit/Services/KiBoardsTestRunner.cs +++ b/src/KiBoards.Xunit/Services/KiBoardsTestRunner.cs @@ -1,5 +1,6 @@ using KiBoards.Models; using Nest; +using System.Collections; using System.Reflection; using Xunit.Abstractions; using Xunit.Sdk; @@ -9,41 +10,107 @@ namespace KiBoards.Services internal class KiBoardsTestRunner { private readonly KiBoardsElasticClient _elasticService; + private readonly KiBoardsTestRun _testRun; + + public string Version { get; private set; } public KiBoardsTestRunner(IMessageSink messageSink) { - var uriString = Environment.GetEnvironmentVariable("KIB_ELASTICSEARCH_HOST") ?? "http://localhost:9200"; - var connectionSettings = new ConnectionSettings(new Uri(uriString)); - - var elasticClient = new ElasticClient(connectionSettings - .DefaultMappingFor(m => m - .IndexName($"kiboards-testruns-{DateTime.UtcNow:yyyy-MM}") - .IdProperty(p => p.Id)) - .DefaultMappingFor(m => m - .IndexName($"kiboards-testcases-{DateTime.UtcNow:yyyy-MM}")) - - .MaxRetryTimeout(TimeSpan.FromMinutes(5)) - .EnableApiVersioningHeader() - .MaximumRetries(3)); - - - _elasticService = new KiBoardsElasticClient(elasticClient, messageSink); - - var version = Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion; - messageSink.OnMessage(new DiagnosticMessage($"KiBoards.Xunit {version} logging to {uriString}")); + try + { + _testRun = new KiBoardsTestRun() + { + Id = Guid.NewGuid().ToString(), + StartTime = DateTime.UtcNow, + MachineName = Environment.MachineName, + UserName = Environment.UserName, + Variables = new Dictionary() + }; + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetCustomAttribute() != null)) + Startup(assembly, messageSink); + + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) + { + var name = entry.Key.ToString(); + var value = entry.Value.ToString(); + + const string prefix = "KIB_VAR_"; + + if (name.Length > prefix.Length + 1 && name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(value)) + _testRun.Variables.TryAdd(name[prefix.Length..], value); + } + + var uriString = Environment.GetEnvironmentVariable("KIB_ELASTICSEARCH_HOST") ?? "http://localhost:9200"; + var connectionSettings = new ConnectionSettings(new Uri(uriString)); + + var elasticClient = new ElasticClient(connectionSettings + .DefaultMappingFor(m => m + .IndexName($"kiboards-testruns-{DateTime.UtcNow:yyyy-MM}") + .IdProperty(p => p.Id)) + .DefaultMappingFor(m => m + .IndexName($"kiboards-testcases-{DateTime.UtcNow:yyyy-MM}")) + + .MaxRetryTimeout(TimeSpan.FromMinutes(5)) + .EnableApiVersioningHeader() + .MaximumRetries(3)); + + _elasticService = new KiBoardsElasticClient(elasticClient, messageSink); + + Version = Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion; + messageSink.WriteMessage($"KiBoards.Xunit {Version} logging to {uriString}"); + } + catch (Exception ex) + { + messageSink.WriteException(ex); + } + } + + + private void Startup(Assembly assembly, IMessageSink messageSink) + { + try + { + var startup = assembly.GetCustomAttribute(); + Type type = assembly.GetType(startup.ClassName); + + if (type != null) + { + if (type.GetConstructor(new Type[] { typeof(string), typeof(IMessageSink) }) != null) + Activator.CreateInstance(type, _testRun.Id, messageSink); + else if (type.GetConstructor(new Type[] { typeof(string) }) != null) + Activator.CreateInstance(type, _testRun.Id); + else if (type.GetConstructor(new Type[] { typeof(IMessageSink) }) != null) + Activator.CreateInstance(type, messageSink); + else if (type.GetConstructor(new Type[] { }) != null) + Activator.CreateInstance(type); + } + } + catch (Exception ex) + { + messageSink.WriteException(ex); + } } - public async Task IndexTestRunAsync(TestRun testRun) + public async Task IndexTestRunAsync(RunSummary summary) { - await _elasticService.IndexDocumentAsync(testRun); + _testRun.Summary = new KiBoardsTestRunSummary() + { + Total = summary.Total, + Failed = summary.Failed, + Skipped = summary.Skipped, + Time = summary.Time, + }; + + await _elasticService.IndexDocumentAsync(_testRun); } public async Task IndexTestCaseRunAsync(ITestResultMessage testResult) { await _elasticService.IndexDocumentAsync(new KiBoardsTestCaseRun() { - Id = (TestFramework.TestRun.Id + testResult.TestCase.UniqueID).ComputeMD5(), - TestRun = TestFramework.TestRun, + Id = (_testRun.Id + testResult.TestCase.UniqueID).ComputeMD5(), + TestRun = _testRun, ExecutionTime = testResult.ExecutionTime, Output = testResult.Output, Failed = testResult is ITestFailed failed ? new KiBoardsTestCaseRunFailed() @@ -78,6 +145,12 @@ await _elasticService.IndexDocumentAsync(new KiBoardsTestCaseRun() Skipped = testResult is ITestSkipped skipped ? new KiBoardsTestCaseRunSkipped() { Reason = skipped.Reason } : null, Status = testResult is ITestPassed ? "Passed" : testResult is ITestFailed ? "Failed" : testResult is ITestSkipped ? "Skipped" : "Other" }); - } + } + + internal void AddTestCases(IEnumerable testCases) + { + _testRun.Name = string.Join(",", testCases.Select(a => Path.GetFileNameWithoutExtension(a.TestMethod.TestClass.Class.Assembly.AssemblyPath)).Distinct()); + _testRun.Hash = string.Join(",", testCases.OrderBy(a => a.UniqueID).Select(a => a.UniqueID)).ComputeMD5(); + } } } diff --git a/src/KiBoards.Xunit/TestStartupAttribute.cs b/src/KiBoards.Xunit/TestStartupAttribute.cs new file mode 100644 index 0000000..cf4a674 --- /dev/null +++ b/src/KiBoards.Xunit/TestStartupAttribute.cs @@ -0,0 +1,10 @@ +[AttributeUsage(AttributeTargets.Assembly)] +public class TestStartupAttribute : Attribute +{ + public string ClassName { get; set; } + + public TestStartupAttribute(string className) + { + ClassName = className; + } +} diff --git a/src/SimpleTest/SimpleTest.csproj b/src/SimpleTest/SimpleTest.csproj index 3c35cb8..bf2b4d2 100644 --- a/src/SimpleTest/SimpleTest.csproj +++ b/src/SimpleTest/SimpleTest.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -22,4 +22,8 @@ + + + + diff --git a/src/SimpleTest/Simple_Must.cs b/src/SimpleTest/Simple_Must.cs index ce800da..fc5f52e 100644 --- a/src/SimpleTest/Simple_Must.cs +++ b/src/SimpleTest/Simple_Must.cs @@ -1,4 +1,5 @@ [assembly: TestFramework("KiBoards.TestFramework", "KiBoards.Xunit")] +[assembly: TestStartup("SimpleTest.Startup")] namespace SimpleTest { @@ -12,5 +13,11 @@ public void Not_DivideByZero(int a, int b) Assert.Equal(0, a); } + + [Fact] + public void Pass() + { + Assert.Equal(0, 0); + } } } \ No newline at end of file diff --git a/src/SimpleTest/Startup.cs b/src/SimpleTest/Startup.cs new file mode 100644 index 0000000..37581ca --- /dev/null +++ b/src/SimpleTest/Startup.cs @@ -0,0 +1,13 @@ +using System.Reflection; +using Xunit.Abstractions; + +namespace SimpleTest +{ + public class Startup + { + public Startup(IMessageSink messageSink) + { + Environment.SetEnvironmentVariable("KIB_VAR_VERSION", Assembly.GetExecutingAssembly().GetCustomAttribute()?.InformationalVersion); + } + } +}