diff --git a/src/Microsoft.DotNet.XHarness.Tests.Runners/AndroidApplicationEntryPoint.cs b/src/Microsoft.DotNet.XHarness.Tests.Runners/AndroidApplicationEntryPoint.cs
index 33cb4cd1a..e55198bf6 100644
--- a/src/Microsoft.DotNet.XHarness.Tests.Runners/AndroidApplicationEntryPoint.cs
+++ b/src/Microsoft.DotNet.XHarness.Tests.Runners/AndroidApplicationEntryPoint.cs
@@ -36,10 +36,7 @@ public override async Task RunAsync()
logger.MinimumLogLevel = MinimumLogLevel.Info;
var testAssemblies = GetTestAssemblies();
- var runner = await CreateRunner(logger);
-
- // if we have ignore files, ignore those tests
- await runner.Run(testAssemblies).ConfigureAwait(false);
+ var runner = await InternalRunAsync(logger);
TestRunner.Jargon jargon = Core.TestRunner.Jargon.NUnitV3;
switch (options.XmlVersion)
diff --git a/src/Microsoft.DotNet.XHarness.Tests.Runners/ApplicationEntryPoint.cs b/src/Microsoft.DotNet.XHarness.Tests.Runners/ApplicationEntryPoint.cs
index f03b7fb69..e3743769f 100644
--- a/src/Microsoft.DotNet.XHarness.Tests.Runners/ApplicationEntryPoint.cs
+++ b/src/Microsoft.DotNet.XHarness.Tests.Runners/ApplicationEntryPoint.cs
@@ -29,10 +29,25 @@ public enum TestRunnerType
/// Assemblies: Provide a list of the assembly information to run.
/// assemblies can be loaded from disk or from memory, is up to the
/// implementor.
+ ///
+ /// Clients that register to the class events and want to update the UI
+ /// are responsable to do so in the main UI thread. The application entry
+ /// point does not guarantee that the tests are executed in the ui thread.
+ ///
///
public abstract class ApplicationEntryPoint
{
+ ///
+ /// Event raised when the test run has started.
+ ///
+ public event EventHandler TestsStarted;
+
+ ///
+ /// Event raised when the test run has completed.
+ ///
+ public event EventHandler TestsCompleted;
+
protected abstract int? MaxParallelThreads { get; }
///
/// Must be implemented and return a class that returns the information
@@ -73,7 +88,7 @@ public abstract class ApplicationEntryPoint
///
public MinimumLogLevel MinimumLogLevel { get; set; } = MinimumLogLevel.Info;
- protected async Task CreateRunner(LogWriter logger)
+ protected async Task InternalRunAsync (LogWriter logger)
{
logger.MinimumLogLevel = MinimumLogLevel;
TestRunner runner;
@@ -102,6 +117,17 @@ protected async Task CreateRunner(LogWriter logger)
runner.SkipTests(skippedTests);
}
}
+
+ var testAssemblies = GetTestAssemblies();
+ // notify the clients we are starting
+ TestsStarted?.Invoke(this, new EventArgs());
+
+ await runner.Run(testAssemblies).ConfigureAwait(false);
+
+ var result = new TestRunResult(runner);
+ // notify the client we are done and the results, but do not expose
+ // the runner.
+ TestsCompleted?.Invoke(this, result);
return runner;
}
}
diff --git a/src/Microsoft.DotNet.XHarness.Tests.Runners/TestRunResult.cs b/src/Microsoft.DotNet.XHarness.Tests.Runners/TestRunResult.cs
new file mode 100644
index 000000000..42e4f2b4f
--- /dev/null
+++ b/src/Microsoft.DotNet.XHarness.Tests.Runners/TestRunResult.cs
@@ -0,0 +1,55 @@
+using Microsoft.DotNet.XHarness.Tests.Runners.Core;
+
+namespace Microsoft.DotNet.XHarness.Tests.Runners
+{
+ public struct TestRunResult
+ {
+ ///
+ /// Retrieve the number of executed tests in a run.
+ ///
+ public long ExecutedTests { get; private set; }
+
+ ///
+ /// Retrieve the number of failed tests in a run.
+ ///
+ public long FailedTests { get; private set; }
+
+ ///
+ /// Retrieve the number of not executed tests due to the filters in a
+ /// run.
+ ///
+ public long FilteredTests { get; private set; }
+
+ ///
+ /// Retrieve the number of inconclusive tests in a run.
+ ///
+ public long InconclusiveTests { get; private set; }
+
+ ///
+ /// Retrieve the number of passed tests in a run.
+ ///
+ public long PassedTests { get; private set; }
+
+ ///
+ /// Retrieve the number of skipped tests in a run.
+ ///
+ public long SkippedTests { get; private set; }
+
+ ///
+ /// Retrieve the total number of tests in a run. This value
+ /// includes all skipped and filtered tests and might no be equal
+ /// to the value returned by ExecutedTests.
+ ///
+ public long TotalTests { get; private set; }
+
+ internal TestRunResult(TestRunner runner) {
+ ExecutedTests = runner.ExecutedTests;
+ FailedTests = runner.FailedTests;
+ FilteredTests = runner.FilteredTests;
+ InconclusiveTests = runner.InconclusiveTests;
+ PassedTests = runner.PassedTests;
+ SkippedTests = runner.SkippedTests;
+ TotalTests = runner.TotalTests;
+ }
+ }
+}
diff --git a/src/Microsoft.DotNet.XHarness.Tests.Runners/iOSApplicationEntryPoint.cs b/src/Microsoft.DotNet.XHarness.Tests.Runners/iOSApplicationEntryPoint.cs
index 14bdb44ec..1c8e13f13 100644
--- a/src/Microsoft.DotNet.XHarness.Tests.Runners/iOSApplicationEntryPoint.cs
+++ b/src/Microsoft.DotNet.XHarness.Tests.Runners/iOSApplicationEntryPoint.cs
@@ -31,11 +31,9 @@ public override async Task RunAsync()
// we will write the normal console output using the LogWriter
var logger = (writer == null || options.EnableXml) ? new LogWriter(Device) : new LogWriter(Device, writer);
logger.MinimumLogLevel = MinimumLogLevel.Info;
- var testAssemblies = GetTestAssemblies();
- var runner = await CreateRunner(logger);
// if we have ignore files, ignore those tests
- await runner.Run(testAssemblies).ConfigureAwait(false);
+ var runner = await InternalRunAsync(logger);
TestRunner.Jargon jargon = Core.TestRunner.Jargon.NUnitV3;
switch (options.XmlVersion)