From bed0abccd7abadd138afda7f1d7f6ebcc045a241 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 16 Nov 2023 20:36:57 +0100 Subject: [PATCH] roll back MauiTestUtils upstream update --- .../CodeBehindGenerator.cs | 35 +- .../GeneratorDiagnostics.cs | 27 +- .../RunnerGenerator.cs | 149 ++- ...Utils.DeviceTests.Runners.SourceGen.csproj | 2 +- .../AppHostBuilderExtensions.cs | 62 +- .../DeviceTests.Runners/GlobalNamespaces.cs | 4 - .../Android/HeadlessTestRunner.cs | 117 +- .../Android/MauiTestActivity.cs | 41 +- .../Android/MauiTestInstrumentation.cs | 203 ++-- .../HeadlessRunner/TestDevice.cs | 25 +- .../Windows/ControlsHeadlessTestRunner.cs | 161 --- .../Windows/HeadlessTestRunner.cs | 92 -- .../HeadlessRunner/Windows/TestLogger.cs | 28 - .../HeadlessRunner/iOS/HeadlessTestRunner.cs | 90 +- .../iOS/MauiTestApplicationDelegate.cs | 176 ++-- .../iOS/MauiTestViewController.cs | 40 +- .../HeadlessRunnerOptions.cs | 11 +- .../DeviceTests.Runners/TestDispatcher.cs | 47 +- .../DeviceTests.Runners/TestOptions.cs | 27 +- .../DeviceTests.Runners/TestServices.cs | 33 +- .../TestUtils.DeviceTests.Runners.csproj | 2 +- .../DeviceTests.Runners/TestWindow.cs | 42 +- .../VisualRunner/AssemblyRunInfo.cs | 25 +- .../Converters/RunStatusToColorConverter.cs | 37 +- .../VisualRunner/DeviceRunner.cs | 458 ++++---- .../VisualRunner/ITestListener.cs | 9 +- .../VisualRunner/ITestRunner.cs | 19 +- .../VisualRunner/MauiVisualRunnerApp.xaml | 2 +- .../VisualRunner/MauiVisualRunnerApp.xaml.cs | 41 +- .../VisualRunner/Navigation/INavigation.cs | 9 +- .../VisualRunner/Navigation/Navigator.cs | 41 +- .../VisualRunner/Navigation/PageType.cs | 17 +- .../VisualRunner/Pages/CreditsPage.xaml | 2 +- .../VisualRunner/Pages/CreditsPage.xaml.cs | 23 +- .../VisualRunner/Pages/HomePage.xaml.cs | 72 +- .../Pages/TestAssemblyPage.xaml.cs | 27 +- .../VisualRunner/Pages/TestResultPage.xaml | 2 +- .../VisualRunner/Pages/TestResultPage.xaml.cs | 13 +- .../VisualRunner/Sinks/DeviceExecutionSink.cs | 107 +- .../Sinks/DiagnosticMessageSink.cs | 13 +- .../VisualRunner/Utils/AsyncLock.cs | 59 +- .../Utils/FilteredCollectionView.cs | 444 ++++---- .../VisualRunner/Utils/SortedList.cs | 132 +-- .../VisualRunner/Utils/TestRunLogger.cs | 141 ++- .../VisualRunner/ViewModels/HomeViewModel.cs | 159 ++- .../VisualRunner/ViewModels/RunStatus.cs | 19 +- .../ViewModels/TestAssemblyViewModel.cs | 527 +++++---- .../ViewModels/TestCaseViewModel.cs | 177 ++-- .../ViewModels/TestResultViewModel.cs | 107 +- .../VisualRunner/ViewModels/TestState.cs | 17 +- .../VisualRunner/ViewModels/ViewModelBase.cs | 39 +- .../XamlExtensions/EmbeddedHtmlExtension.cs | 19 +- .../EmbeddedResourceExtension.cs | 35 +- .../AssertionExtensions.Android.cs | 827 ++++----------- .../AssertionExtensions.Windows.cs | 680 ++++-------- .../DeviceTests/AssertionExtensions.cs | 251 +---- .../DeviceTests/AssertionExtensions.iOS.cs | 997 +++++------------- .../DeviceTests/CaptureHelper.Windows.cs | 95 -- .../DeviceTests/ColorComparison.Android.cs | 23 +- .../DeviceTests/ColorComparison.Windows.cs | 25 +- .../DeviceTests/ColorComparison.iOS.cs | 57 +- .../DeviceTests/RepeatAttribute.cs | 40 +- .../DeviceTests/TestUtils.DeviceTests.csproj | 6 +- .../DeviceTests/UINSWindow.iOS.cs | 6 +- .../DeviceTests/xUnitCustomizations.cs | 251 +++-- 65 files changed, 2784 insertions(+), 4680 deletions(-) delete mode 100644 test/MauiTestUtils/DeviceTests.Runners/GlobalNamespaces.cs delete mode 100644 test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/ControlsHeadlessTestRunner.cs delete mode 100644 test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/HeadlessTestRunner.cs delete mode 100644 test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/TestLogger.cs delete mode 100644 test/MauiTestUtils/DeviceTests/CaptureHelper.Windows.cs diff --git a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/CodeBehindGenerator.cs b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/CodeBehindGenerator.cs index a3a0114bf8..f29b9537ef 100644 --- a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/CodeBehindGenerator.cs +++ b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/CodeBehindGenerator.cs @@ -1,28 +1,27 @@ using Microsoft.CodeAnalysis; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen; + +[Generator] +public class CodeBehindGenerator : ISourceGenerator { - [Generator] - public class CodeBehindGenerator : ISourceGenerator + public void Initialize(GeneratorInitializationContext context) { - public void Initialize(GeneratorInitializationContext context) - { - //#if DEBUG - //if (!System.Diagnostics.Debugger.IsAttached) - // System.Diagnostics.Debugger.Launch(); - //#endif - } + //#if DEBUG + //if (!System.Diagnostics.Debugger.IsAttached) + // System.Diagnostics.Debugger.Launch(); + //#endif + } - public void Execute(GeneratorExecutionContext context) - { - if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.TargetFramework", out var targetFramework)) - return; + public void Execute(GeneratorExecutionContext context) + { + if (!context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.TargetFramework", out var targetFramework)) + return; - context.Log($"TargetFramework: {targetFramework}"); + context.Log($"TargetFramework: {targetFramework}"); - var generator = new RunnerGenerator(context, targetFramework); + var generator = new RunnerGenerator(context, targetFramework); - generator?.Generate(); - } + generator?.Generate(); } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/GeneratorDiagnostics.cs b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/GeneratorDiagnostics.cs index 2c85318dd6..3b17f6791a 100644 --- a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/GeneratorDiagnostics.cs +++ b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/GeneratorDiagnostics.cs @@ -1,20 +1,19 @@ using System.Diagnostics; using Microsoft.CodeAnalysis; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen; + +static class GeneratorDiagnostics { - static class GeneratorDiagnostics - { - public static readonly DiagnosticDescriptor LoggingMessage = new DiagnosticDescriptor( - id: "TST1001", - title: "Logging Message", - messageFormat: "{0}", - category: "Logging", - DiagnosticSeverity.Info, - isEnabledByDefault: true); + public static readonly DiagnosticDescriptor LoggingMessage = new DiagnosticDescriptor( + id: "TST1001", + title: "Logging Message", + messageFormat: "{0}", + category: "Logging", + DiagnosticSeverity.Info, + isEnabledByDefault: true); - [Conditional("DEBUG")] - public static void Log(this GeneratorExecutionContext context, string message) => - context.ReportDiagnostic(Diagnostic.Create(LoggingMessage, Location.None, message)); - } + [Conditional("DEBUG")] + public static void Log(this GeneratorExecutionContext context, string message) => + context.ReportDiagnostic(Diagnostic.Create(LoggingMessage, Location.None, message)); } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/RunnerGenerator.cs b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/RunnerGenerator.cs index 760d870331..d5bf175bac 100644 --- a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/RunnerGenerator.cs +++ b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/RunnerGenerator.cs @@ -3,99 +3,99 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen; + +public class RunnerGenerator { - public class RunnerGenerator + public RunnerGenerator(GeneratorExecutionContext context, string targetFramework) { - public RunnerGenerator(GeneratorExecutionContext context, string targetFramework) - { - Context = context; + Context = context; - TargetFramework = targetFramework; + TargetFramework = targetFramework; - context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationId", out var applicationId); - context.Log($"ApplicationId: {applicationId}"); - ApplicationId = applicationId ?? throw new Exception("ApplicationId needs to be set."); + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationId", out var applicationId); + context.Log($"ApplicationId: {applicationId}"); + ApplicationId = applicationId ?? throw new Exception("ApplicationId needs to be set."); - context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationTitle", out var applicationTitle); - context.Log($"ApplicationTitle: {applicationTitle}"); - ApplicationTitle = applicationTitle ?? "Tests"; + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.ApplicationTitle", out var applicationTitle); + context.Log($"ApplicationTitle: {applicationTitle}"); + ApplicationTitle = applicationTitle ?? "Tests"; - context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); - context.Log($"RootNamespace: {rootNamespace}"); - RootNamespace = rootNamespace ?? "TestRunnerNamespace"; + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace); + context.Log($"RootNamespace: {rootNamespace}"); + RootNamespace = rootNamespace ?? "TestRunnerNamespace"; - ContainsSplashScreen = false; - foreach (var file in context.AdditionalFiles) + ContainsSplashScreen = false; + foreach (var file in context.AdditionalFiles) + { + var options = context.AnalyzerConfigOptions.GetOptions(file); + if (options.TryGetValue("build_metadata.AdditionalFiles.IsMauiSplashScreen", out var isMauiSplashScreen) && bool.TryParse(isMauiSplashScreen, out var isSplash) && isSplash) { - var options = context.AnalyzerConfigOptions.GetOptions(file); - if (options.TryGetValue("build_metadata.AdditionalFiles.IsMauiSplashScreen", out var isMauiSplashScreen) && bool.TryParse(isMauiSplashScreen, out var isSplash) && isSplash) - { - ContainsSplashScreen = true; - break; - } + ContainsSplashScreen = true; + break; } - context.Log($"ContainsSplashScreen: {ContainsSplashScreen}"); } + context.Log($"ContainsSplashScreen: {ContainsSplashScreen}"); + } - public GeneratorExecutionContext Context { get; } - - public string TargetFramework { get; } - - public string RootNamespace { get; } + public GeneratorExecutionContext Context { get; } - public string ApplicationId { get; } + public string TargetFramework { get; } - public string ApplicationTitle { get; } + public string RootNamespace { get; } - public bool ContainsSplashScreen { get; } + public string ApplicationId { get; } - public void Generate() - { - Context.Log($"Generating runners..."); + public string ApplicationTitle { get; } - if (TargetFramework.IndexOf("-android", StringComparison.OrdinalIgnoreCase) != -1) - { - var code = GenerateAndroidSource(); - var name = "TestRunner.Android.sg.cs"; + public bool ContainsSplashScreen { get; } - AddSource(name, code); - } - else if (TargetFramework.IndexOf("-ios", StringComparison.OrdinalIgnoreCase) != -1) - { - var code = GenerateIosSource(); - var name = "TestRunner.iOS.sg.cs"; + public void Generate() + { + Context.Log($"Generating runners..."); - AddSource(name, code); - } - else if (TargetFramework.IndexOf("-maccatalyst", StringComparison.OrdinalIgnoreCase) != -1) - { - var code = GenerateIosSource(); - var name = "TestRunner.MacCatalyst.sg.cs"; + if (TargetFramework.IndexOf("-android", StringComparison.OrdinalIgnoreCase) != -1) + { + var code = GenerateAndroidSource(); + var name = "TestRunner.Android.sg.cs"; - AddSource(name, code); - } + AddSource(name, code); } + else if (TargetFramework.IndexOf("-ios", StringComparison.OrdinalIgnoreCase) != -1) + { + var code = GenerateIosSource(); + var name = "TestRunner.iOS.sg.cs"; - protected void AddSource(string filename, string contents) + AddSource(name, code); + } + else if (TargetFramework.IndexOf("-maccatalyst", StringComparison.OrdinalIgnoreCase) != -1) { - Context.Log($"AddSource: {filename}"); - Context.AddSource(filename, SourceText.From(contents, Encoding.UTF8)); + var code = GenerateIosSource(); + var name = "TestRunner.MacCatalyst.sg.cs"; + + AddSource(name, code); } + } - string GenerateAndroidSource() - { - var mauiProgramName = "MauiProgram"; - var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; - var splash = ContainsSplashScreen ? @"Theme = ""@style/Maui.SplashTheme""," : ""; + protected void AddSource(string filename, string contents) + { + Context.Log($"AddSource: {filename}"); + Context.AddSource(filename, SourceText.From(contents, Encoding.UTF8)); + } - var appName = "MainApplication"; - var visualActivityName = "MainActivity"; + string GenerateAndroidSource() + { + var mauiProgramName = "MauiProgram"; + var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; + var splash = ContainsSplashScreen ? @"Theme = ""@style/Maui.SplashTheme""," : ""; + + var appName = "MainApplication"; + var visualActivityName = "MainActivity"; - var instrumentationName = "TestInstrumentation"; - var headlessActivityName = "TestActivity"; + var instrumentationName = "TestInstrumentation"; + var headlessActivityName = "TestActivity"; - return @" + return @" #if !SKIP_RUNNER_ENTRYPOINT_GENERATION && !SKIP_VISUAL_RUNNER_ENTRYPOINT_GENERATION && !SKIP_VISUAL_RUNNER_APPLICATION_GENERATION namespace " + RootNamespace + @" { @@ -162,16 +162,16 @@ public partial class " + headlessActivityName + @" : global::Microsoft.Maui.Test } #endif "; - } + } - string GenerateIosSource() - { - var mauiProgramName = "MauiProgram"; - var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; - var visualDelegateName = "VisualRunnerAppDelegate"; - var headlessDelegateName = "HeadlessRunnerAppDelegate"; + string GenerateIosSource() + { + var mauiProgramName = "MauiProgram"; + var mauiProgramFullName = @"global::" + RootNamespace + "." + mauiProgramName; + var visualDelegateName = "VisualRunnerAppDelegate"; + var headlessDelegateName = "HeadlessRunnerAppDelegate"; - return @" + return @" #if !SKIP_RUNNER_ENTRYPOINT_GENERATION && !SKIP_VISUAL_RUNNER_ENTRYPOINT_GENERATION && !SKIP_RUNNER_PROGRAM_GENERATION namespace " + RootNamespace + @" { @@ -219,6 +219,5 @@ partial class " + headlessDelegateName + @" : global::Microsoft.Maui.TestUtils.D } #endif "; - } } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj index bf0f8aede6..6ff7f156c8 100644 --- a/test/MauiTestUtils/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj +++ b/test/MauiTestUtils/DeviceTests.Runners.SourceGen/TestUtils.DeviceTests.Runners.SourceGen.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 diff --git a/test/MauiTestUtils/DeviceTests.Runners/AppHostBuilderExtensions.cs b/test/MauiTestUtils/DeviceTests.Runners/AppHostBuilderExtensions.cs index 990579f8e2..58d7a34f3b 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/AppHostBuilderExtensions.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/AppHostBuilderExtensions.cs @@ -6,50 +6,36 @@ using Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; using Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public static class AppHostBuilderExtensions { - public static class AppHostBuilderExtensions + public static MauiAppBuilder ConfigureTests(this MauiAppBuilder appHostBuilder, TestOptions options) { - public static MauiAppBuilder ConfigureTests(this MauiAppBuilder appHostBuilder, TestOptions options) - { - appHostBuilder.Services.AddSingleton(options); - - return appHostBuilder; - } - - public static MauiAppBuilder UseVisualRunner(this MauiAppBuilder appHostBuilder) - { - appHostBuilder.UseMauiApp(svc => new MauiVisualRunnerApp( - svc.GetRequiredService(), - svc.GetRequiredService().CreateLogger("TestRun"))); - - return appHostBuilder; - } - - public static MauiAppBuilder UseHeadlessRunner(this MauiAppBuilder appHostBuilder, HeadlessRunnerOptions options) - { - appHostBuilder.Services.AddSingleton(options); - -#if __ANDROID__ || __IOS__ || MACCATALYST || WINDOWS - appHostBuilder.Services.AddTransient(svc => new HeadlessTestRunner( - svc.GetRequiredService(), - svc.GetRequiredService())); -#endif + appHostBuilder.Services.AddSingleton(options); + + return appHostBuilder; + } - return appHostBuilder; - } + public static MauiAppBuilder UseVisualRunner(this MauiAppBuilder appHostBuilder) + { + appHostBuilder.UseMauiApp(svc => new MauiVisualRunnerApp( + svc.GetRequiredService(), + svc.GetRequiredService().CreateLogger("TestRun"))); -#if WINDOWS - public static MauiAppBuilder UseControlsHeadlessRunner(this MauiAppBuilder appHostBuilder, HeadlessRunnerOptions options) - { - appHostBuilder.Services.AddSingleton(options); + return appHostBuilder; + } - appHostBuilder.Services.AddTransient(svc => new ControlsHeadlessTestRunner( - svc.GetRequiredService(), - svc.GetRequiredService())); + public static MauiAppBuilder UseHeadlessRunner(this MauiAppBuilder appHostBuilder, HeadlessRunnerOptions options) + { + appHostBuilder.Services.AddSingleton(options); - return appHostBuilder; - } +#if __ANDROID__ || __IOS__ || MACCATALYST + appHostBuilder.Services.AddTransient(svc => new HeadlessTestRunner( + svc.GetRequiredService(), + svc.GetRequiredService())); #endif + + return appHostBuilder; } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/GlobalNamespaces.cs b/test/MauiTestUtils/DeviceTests.Runners/GlobalNamespaces.cs deleted file mode 100644 index c88b852f91..0000000000 --- a/test/MauiTestUtils/DeviceTests.Runners/GlobalNamespaces.cs +++ /dev/null @@ -1,4 +0,0 @@ -global using Microsoft.Maui; -global using Microsoft.Maui.Graphics; -global using Microsoft.Maui.Handlers; -global using Microsoft.Maui.Platform; diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/HeadlessTestRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/HeadlessTestRunner.cs index 3cccc294b9..53e1875ce1 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/HeadlessTestRunner.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/HeadlessTestRunner.cs @@ -8,87 +8,86 @@ using Microsoft.DotNet.XHarness.TestRunners.Common; using Microsoft.DotNet.XHarness.TestRunners.Xunit; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +class HeadlessTestRunner : AndroidApplicationEntryPoint { - class HeadlessTestRunner : AndroidApplicationEntryPoint - { - readonly HeadlessRunnerOptions _runnerOptions; - readonly TestOptions _options; - readonly string _resultsPath; + readonly HeadlessRunnerOptions _runnerOptions; + readonly TestOptions _options; + readonly string _resultsPath; - public HeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) - { - _runnerOptions = runnerOptions; - _options = options; + public HeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) + { + _runnerOptions = runnerOptions; + _options = options; - var cache = Application.Context.CacheDir!.AbsolutePath; - _resultsPath = Path.Combine(cache, _runnerOptions.TestResultsFilename); - } + var cache = Application.Context.CacheDir!.AbsolutePath; + _resultsPath = Path.Combine(cache, _runnerOptions.TestResultsFilename); + } - protected override bool LogExcludedTests => true; + protected override bool LogExcludedTests => true; - public override TextWriter? Logger => null; + public override TextWriter? Logger => null; - public override string TestsResultsFinalPath => _resultsPath; + public override string TestsResultsFinalPath => _resultsPath; - protected override int? MaxParallelThreads => System.Environment.ProcessorCount; + protected override int? MaxParallelThreads => System.Environment.ProcessorCount; - protected override IDevice Device { get; } = new TestDevice(); + protected override IDevice Device { get; } = new TestDevice(); - protected override IEnumerable GetTestAssemblies() => - _options.Assemblies - .Distinct() - .Select(assembly => - { - // Android needs this file to "exist" but it uses the assembly actually. - var path = Path.Combine(Application.Context.CacheDir!.AbsolutePath, assembly.GetName().Name + ".dll"); - if (!File.Exists(path)) - File.Create(path).Close(); + protected override IEnumerable GetTestAssemblies() => + _options.Assemblies + .Distinct() + .Select(assembly => + { + // Android needs this file to "exist" but it uses the assembly actually. + var path = Path.Combine(Application.Context.CacheDir!.AbsolutePath, assembly.GetName().Name + ".dll"); + if (!File.Exists(path)) + File.Create(path).Close(); - return new TestAssemblyInfo(assembly, path); - }); + return new TestAssemblyInfo(assembly, path); + }); - protected override void TerminateWithSuccess() { } + protected override void TerminateWithSuccess() { } - protected override TestRunner GetTestRunner(LogWriter logWriter) - { - var testRunner = base.GetTestRunner(logWriter); - if (_options.SkipCategories?.Count > 0) - testRunner.SkipCategories(_options.SkipCategories); - return testRunner; - } + protected override TestRunner GetTestRunner(LogWriter logWriter) + { + var testRunner = base.GetTestRunner(logWriter); + if (_options.SkipCategories?.Count > 0) + testRunner.SkipCategories(_options.SkipCategories); + return testRunner; + } - public async Task RunTestsAsync() - { - var bundle = new Bundle(); + public async Task RunTestsAsync() + { + var bundle = new Bundle(); - TestsCompleted += OnTestsCompleted; + TestsCompleted += OnTestsCompleted; - await RunAsync(); + await RunAsync(); - TestsCompleted -= OnTestsCompleted; + TestsCompleted -= OnTestsCompleted; - if (File.Exists(TestsResultsFinalPath)) - bundle.PutString("test-results-path", TestsResultsFinalPath); + if (File.Exists(TestsResultsFinalPath)) + bundle.PutString("test-results-path", TestsResultsFinalPath); - if (bundle.GetLong("return-code", -1) == -1) - bundle.PutLong("return-code", 1); + if (bundle.GetLong("return-code", -1) == -1) + bundle.PutLong("return-code", 1); - return bundle; + return bundle; - void OnTestsCompleted(object? sender, TestRunResult results) - { - var message = - $"Tests run: {results.ExecutedTests} " + - $"Passed: {results.PassedTests} " + - $"Inconclusive: {results.InconclusiveTests} " + - $"Failed: {results.FailedTests} " + - $"Ignored: {results.SkippedTests}"; + void OnTestsCompleted(object? sender, TestRunResult results) + { + var message = + $"Tests run: {results.ExecutedTests} " + + $"Passed: {results.PassedTests} " + + $"Inconclusive: {results.InconclusiveTests} " + + $"Failed: {results.FailedTests} " + + $"Ignored: {results.SkippedTests}"; - bundle.PutString("test-execution-summary", message); + bundle.PutString("test-execution-summary", message); - bundle.PutLong("return-code", results.FailedTests == 0 ? 0 : 1); - } + bundle.PutLong("return-code", results.FailedTests == 0 ? 0 : 1); } } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestActivity.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestActivity.cs index 1b9047c8cf..62f7d49770 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestActivity.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestActivity.cs @@ -5,34 +5,33 @@ using AndroidX.AppCompat.App; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +public abstract class MauiTestActivity : AppCompatActivity { - public abstract class MauiTestActivity : AppCompatActivity + public TaskCompletionSource TaskCompletionSource { get; } = new TaskCompletionSource(); + + protected override void OnCreate(Bundle? savedInstanceState) { - public TaskCompletionSource TaskCompletionSource { get; } = new TaskCompletionSource(); + base.OnCreate(savedInstanceState); - protected override void OnCreate(Bundle? savedInstanceState) + // Do the work on the background thread to avoid a keyDispatchingTimedOut ANR + Task.Run(async () => { - base.OnCreate(savedInstanceState); - - // Do the work on the background thread to avoid a keyDispatchingTimedOut ANR - Task.Run(async () => + try { - try - { - var runner = MauiTestInstrumentation.Current.Services.GetRequiredService(); + var runner = MauiTestInstrumentation.Current.Services.GetRequiredService(); - var bundle = await runner.RunTestsAsync(); + var bundle = await runner.RunTestsAsync(); - TaskCompletionSource.TrySetResult(bundle); - } - catch (Exception ex) - { - TaskCompletionSource.TrySetException(ex); - } + TaskCompletionSource.TrySetResult(bundle); + } + catch (Exception ex) + { + TaskCompletionSource.TrySetException(ex); + } - Finish(); - }); - } + Finish(); + }); } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestInstrumentation.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestInstrumentation.cs index 78bb6096c1..a101a56f9c 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestInstrumentation.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Android/MauiTestInstrumentation.cs @@ -9,141 +9,140 @@ using Android.Runtime; using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +public abstract class MauiTestInstrumentation : Instrumentation { - public abstract class MauiTestInstrumentation : Instrumentation + readonly TaskCompletionSource _waitForApplication = new(); + Java.Lang.Class _activityClass = null!; + + protected MauiTestInstrumentation(IntPtr handle, JniHandleOwnership ownership) + : base(handle, ownership) { - readonly TaskCompletionSource _waitForApplication = new(); - Java.Lang.Class _activityClass = null!; + Current = this; + } - protected MauiTestInstrumentation(IntPtr handle, JniHandleOwnership ownership) - : base(handle, ownership) - { - Current = this; - } + public static MauiTestInstrumentation Current { get; private set; } = null!; - public static MauiTestInstrumentation Current { get; private set; } = null!; + public Bundle? Arguments { get; private set; } - public Bundle? Arguments { get; private set; } + public IServiceProvider Services { get; private set; } = null!; - public IServiceProvider Services { get; private set; } = null!; + public TestOptions Options { get; private set; } = null!; - public TestOptions Options { get; private set; } = null!; + public HeadlessRunnerOptions RunnerOptions { get; private set; } = null!; - public HeadlessRunnerOptions RunnerOptions { get; private set; } = null!; + public Context? CurrentExecutionContext { get; private set; } - public Context? CurrentExecutionContext { get; private set; } + public override void OnCreate(Bundle? arguments) + { + _activityClass = Java.Lang.Class.ForName(Context!.PackageName + ".TestActivity"); + Arguments = arguments; - public override void OnCreate(Bundle? arguments) - { - _activityClass = Java.Lang.Class.ForName(Context!.PackageName + ".TestActivity"); - Arguments = arguments; + base.OnCreate(arguments); - base.OnCreate(arguments); + Start(); + } - Start(); - } + public override void CallApplicationOnCreate(Application? app) + { + base.CallApplicationOnCreate(app); - public override void CallApplicationOnCreate(Application? app) - { - base.CallApplicationOnCreate(app); + if (app == null) + _waitForApplication.SetException(new ArgumentNullException(nameof(app))); + else + _waitForApplication.SetResult(app); + } - if (app == null) - _waitForApplication.SetException(new ArgumentNullException(nameof(app))); - else - _waitForApplication.SetResult(app); - } + public override async void OnStart() + { + base.OnStart(); - public override async void OnStart() - { - base.OnStart(); + await _waitForApplication.Task; - await _waitForApplication.Task; + Services = MauiApplication.Current.Services; + Options = Services.GetRequiredService(); + RunnerOptions = Services.GetRequiredService(); - Services = IPlatformApplication.Current?.Services ?? throw new InvalidOperationException("Unable to find Application Services"); - Options = Services.GetRequiredService(); - RunnerOptions = Services.GetRequiredService(); + var resultsFilename = Arguments?.GetString("results-file-name"); + if (!string.IsNullOrWhiteSpace(resultsFilename)) + RunnerOptions.TestResultsFilename = resultsFilename; - var resultsFilename = Arguments?.GetString("results-file-name"); - if (!string.IsNullOrWhiteSpace(resultsFilename)) - RunnerOptions.TestResultsFilename = resultsFilename; + var bundle = await RunTestsAsync(); - var bundle = await RunTestsAsync(); + CopyFile(bundle); - CopyFile(bundle); + Finish(Result.Ok, bundle); + } - Finish(Result.Ok, bundle); - } + void CopyFile(Bundle bundle) + { + var resultsFile = bundle.GetString("test-results-path"); + if (resultsFile == null) + return; + + var guid = Guid.NewGuid().ToString("N"); + var name = Path.GetFileName(resultsFile); + + string finalPath; + if (!OperatingSystem.IsAndroidVersionAtLeast(30)) + { + var root = Application.Context.GetExternalFilesDir(null)!.AbsolutePath!; + var dir = Path.Combine(root, guid); + + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); - void CopyFile(Bundle bundle) + finalPath = Path.Combine(dir, name); + File.Copy(resultsFile, finalPath, true); + } + else { - var resultsFile = bundle.GetString("test-results-path"); - if (resultsFile == null) - return; - - var guid = Guid.NewGuid().ToString("N"); - var name = Path.GetFileName(resultsFile); - - string finalPath; - if (!OperatingSystem.IsAndroidVersionAtLeast(30)) - { - var root = Application.Context.GetExternalFilesDir(null)!.AbsolutePath!; - var dir = Path.Combine(root, guid); - - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - finalPath = Path.Combine(dir, name); - File.Copy(resultsFile, finalPath, true); - } - else - { - var downloads = Android.OS.Environment.DirectoryDownloads!; - var relative = Path.Combine(downloads, Context!.PackageName!, guid); - - var values = new ContentValues(); - values.Put(MediaStore.IMediaColumns.DisplayName, name); - values.Put(MediaStore.IMediaColumns.MimeType, "text/xml"); - values.Put(MediaStore.IMediaColumns.RelativePath, relative); - - var resolver = Context!.ContentResolver!; - var uri = resolver.Insert(MediaStore.Downloads.ExternalContentUri, values)!; - using (var dest = resolver.OpenOutputStream(uri)!) - using (var source = File.OpenRead(resultsFile)) - source.CopyTo(dest); + var downloads = Android.OS.Environment.DirectoryDownloads!; + var relative = Path.Combine(downloads, Context!.PackageName!, guid); + + var values = new ContentValues(); + values.Put(MediaStore.IMediaColumns.DisplayName, name); + values.Put(MediaStore.IMediaColumns.MimeType, "text/xml"); + values.Put(MediaStore.IMediaColumns.RelativePath, relative); + + var resolver = Context!.ContentResolver!; + var uri = resolver.Insert(MediaStore.Downloads.ExternalContentUri, values)!; + using (var dest = resolver.OpenOutputStream(uri)!) + using (var source = File.OpenRead(resultsFile)) + source.CopyTo(dest); #pragma warning disable CS0618 // Type or member is obsolete - var root = Android.OS.Environment.ExternalStorageDirectory!.AbsolutePath; + var root = Android.OS.Environment.ExternalStorageDirectory!.AbsolutePath; #pragma warning restore CS0618 // Type or member is obsolete - finalPath = Path.Combine(root, relative, name); - } - - bundle.PutString("test-results-path", finalPath); + finalPath = Path.Combine(root, relative, name); } - Task RunTestsAsync() + bundle.PutString("test-results-path", finalPath); + } + + Task RunTestsAsync() + { + if (RunnerOptions.RequiresUIContext) { - if (RunnerOptions.RequiresUIContext) - { - var intent = new Intent(TargetContext, _activityClass); - intent.AddFlags(ActivityFlags.NewTask); + var intent = new Intent(TargetContext, _activityClass); + intent.AddFlags(ActivityFlags.NewTask); - var activity = StartActivitySync(intent); - if (activity is not MauiTestActivity testActivity) - throw new InvalidOperationException($"Unexpected activity type '{activity?.GetType().FullName ?? ""}'."); + var activity = StartActivitySync(intent); + if (activity is not MauiTestActivity testActivity) + throw new InvalidOperationException($"Unexpected activity type '{activity?.GetType().FullName ?? ""}'."); - CurrentExecutionContext = activity; + CurrentExecutionContext = activity; - return testActivity.TaskCompletionSource.Task; - } - else - { - CurrentExecutionContext = TargetContext; + return testActivity.TaskCompletionSource.Task; + } + else + { + CurrentExecutionContext = TargetContext; - var runner = Services.GetRequiredService(); + var runner = Services.GetRequiredService(); - return runner.RunTestsAsync(); - } + return runner.RunTestsAsync(); } } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/TestDevice.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/TestDevice.cs index 56713721e6..872d373c3a 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/TestDevice.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/TestDevice.cs @@ -5,24 +5,21 @@ using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.Devices; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner -{ - class TestDevice : IDevice - { - public string BundleIdentifier => AppInfo.PackageName; +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; - public string UniqueIdentifier => Guid.NewGuid().ToString("N"); +class TestDevice : IDevice +{ + public string BundleIdentifier => AppInfo.PackageName; - public string Name => DeviceInfo.Name; + public string UniqueIdentifier => Guid.NewGuid().ToString("N"); - public string Model => DeviceInfo.Model; + public string Name => DeviceInfo.Name; - public string SystemName => DeviceInfo.Platform.ToString(); + public string Model => DeviceInfo.Model; - public string SystemVersion => DeviceInfo.VersionString; + public string SystemName => DeviceInfo.Platform.ToString(); - public string Locale => CultureInfo.CurrentCulture.Name; + public string SystemVersion => DeviceInfo.VersionString; - public static bool RunHeadless = false; - } -} \ No newline at end of file + public string Locale => CultureInfo.CurrentCulture.Name; +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/ControlsHeadlessTestRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/ControlsHeadlessTestRunner.cs deleted file mode 100644 index 79a917ef86..0000000000 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/ControlsHeadlessTestRunner.cs +++ /dev/null @@ -1,161 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.DotNet.XHarness.TestRunners.Common; -using Microsoft.DotNet.XHarness.TestRunners.Xunit; -using Xunit; - -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner -{ - public class ControlsHeadlessTestRunner : AndroidApplicationEntryPoint - { - const string CategoriesFileName = "devicetestcategories.txt"; - readonly string _categoriesFilePath; - - public static string? TestResultsFile; - public static int? LoopCount; - - readonly HeadlessRunnerOptions _runnerOptions; - readonly TestOptions _options; - string? _resultsPath; - readonly int _loopCount; - TestLogger _logger; - - public ControlsHeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) - { - _runnerOptions = runnerOptions; - _options = options; - _resultsPath = TestResultsFile; - _categoriesFilePath = Path.Combine(Path.GetDirectoryName(_resultsPath) ?? string.Empty, CategoriesFileName); - _loopCount = LoopCount ?? 0; - _logger = new(); - } - - protected override bool LogExcludedTests => true; - - public override TextWriter? Logger => _logger; - - public override string TestsResultsFinalPath => _resultsPath!; - - protected override int? MaxParallelThreads => System.Environment.ProcessorCount; - - protected override IDevice Device { get; } = new TestDevice(); - - protected override IEnumerable GetTestAssemblies() => - _options.Assemblies - .Distinct() - .Select(assembly => new TestAssemblyInfo(assembly, assembly.Location)); - - protected override void TerminateWithSuccess() - { - UI.Xaml.Application.Current.Exit(); - } - - protected override TestRunner GetTestRunner(LogWriter logWriter) - { - var testRunner = base.GetTestRunner(logWriter); - - var allCategories = File.ReadAllLines(_categoriesFilePath); - var categoriesToRun = allCategories.Skip(_loopCount).Take(1).ToArray(); - - List categoriesToSkip = new(); - - foreach (var test in allCategories.Except(categoriesToRun)) - { - categoriesToSkip.Add($"Category={test}"); - } - - var currentCategory = categoriesToRun[0]; - var resultPath = _resultsPath?.Split(".xml") ?? new[] { "" }; - _resultsPath = $"{resultPath[0]}_{currentCategory}.xml"; - - testRunner.SkipCategories(categoriesToSkip); - - return testRunner; - } - - public async Task RunTestsAsync() - { - TestsCompleted += OnTestsCompleted; - - try - { - // Got called with -1 parameter, just discover the tests to run - if (_loopCount == -1) - { - var categories = DiscoverTestsInAssemblies(); - File.WriteAllLines(_categoriesFilePath, categories.ToArray()); - - TerminateWithSuccess(); - } - - await RunAsync(); - } - catch (Exception ex) - { - _logger.WriteLine(ex.ToString()); - } - TestsCompleted -= OnTestsCompleted; - - if (File.Exists(TestsResultsFinalPath)) - return TestsResultsFinalPath; - - return null; - - void OnTestsCompleted(object? sender, TestRunResult results) - { - var message = - $"Tests run: {results.ExecutedTests} " + - $"Passed: {results.PassedTests} " + - $"Inconclusive: {results.InconclusiveTests} " + - $"Failed: {results.FailedTests} " + - $"Ignored: {results.SkippedTests}"; - - _logger.WriteLine("test-execution-summary" + message); - _logger.WriteLine("return-code " + (results.FailedTests == 0 ? 0 : 1)); - } - } - - IEnumerable DiscoverTestsInAssemblies() - { - var result = new List(); - - try - { - foreach (var assm in GetTestAssemblies()) - { - var nameWithoutExt = assm.Assembly.GetName().Name; - var assemblyFileName = Storage.FileSystemUtils.PlatformGetFullAppPackageFilePath($"{nameWithoutExt}.dll"); - - var discoveryOptions = TestFrameworkOptions.ForDiscovery(); - - try - { - using (var framework = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName, null, false)) - using (var sink = new TestDiscoverySink()) - { - framework.Find(false, sink, discoveryOptions); - sink.Finished.WaitOne(); - - result.AddRange(sink.TestCases.SelectMany(tc => tc.Traits["Category"]).Distinct()); - } - } - catch (Exception e) - { - Debug.WriteLine(e); - } - } - } - catch (Exception e) - { - Debug.WriteLine(e); - } - - return result; - } - } -} \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/HeadlessTestRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/HeadlessTestRunner.cs deleted file mode 100644 index 435697b42b..0000000000 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/HeadlessTestRunner.cs +++ /dev/null @@ -1,92 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.DotNet.XHarness.TestRunners.Common; -using Microsoft.DotNet.XHarness.TestRunners.Xunit; - -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner -{ - public class HeadlessTestRunner : AndroidApplicationEntryPoint - { - public static string? TestResultsFile; - - readonly HeadlessRunnerOptions _runnerOptions; - readonly TestOptions _options; - readonly string? _resultsPath; - TestLogger _logger; - - public HeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) - { - _runnerOptions = runnerOptions; - _options = options; - _resultsPath = TestResultsFile; - _logger = new(); - } - - protected override bool LogExcludedTests => true; - - public override TextWriter? Logger => _logger; - - public override string TestsResultsFinalPath => _resultsPath!; - - protected override int? MaxParallelThreads => Environment.ProcessorCount; - - protected override IDevice Device { get; } = new TestDevice(); - - protected override IEnumerable GetTestAssemblies() => - _options.Assemblies - .Distinct() - .Select(assembly => new TestAssemblyInfo(assembly, assembly.Location)); - - protected override void TerminateWithSuccess() - { - UI.Xaml.Application.Current.Exit(); - } - - protected override TestRunner GetTestRunner(LogWriter logWriter) - { - var testRunner = base.GetTestRunner(logWriter); - - if (_options.SkipCategories?.Count > 0) - testRunner.SkipCategories(_options.SkipCategories); - - return testRunner; - } - - public async Task RunTestsAsync() - { - TestsCompleted += OnTestsCompleted; - - try - { - await RunAsync(); - } - catch (Exception ex) - { - _logger.WriteLine(ex.ToString()); - } - TestsCompleted -= OnTestsCompleted; - - if (File.Exists(TestsResultsFinalPath)) - return TestsResultsFinalPath; - - return null; - - void OnTestsCompleted(object? sender, TestRunResult results) - { - var message = - $"Tests run: {results.ExecutedTests} " + - $"Passed: {results.PassedTests} " + - $"Inconclusive: {results.InconclusiveTests} " + - $"Failed: {results.FailedTests} " + - $"Ignored: {results.SkippedTests}"; - - _logger.WriteLine("test-execution-summary" + message); - _logger.WriteLine("return-code " + (results.FailedTests == 0 ? 0 : 1)); - } - } - } -} \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/TestLogger.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/TestLogger.cs deleted file mode 100644 index 383770c3fb..0000000000 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/Windows/TestLogger.cs +++ /dev/null @@ -1,28 +0,0 @@ -#nullable enable -using System; -using System.IO; -using System.Text; - -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner -{ - public class TestLogger : TextWriter - { - public TestLogger() - { - } - - public override void Write(char value) - { - Console.Write(value); - System.Diagnostics.Debug.Write(value); - } - - public override void WriteLine(string? value) - { - Console.WriteLine(value); - System.Diagnostics.Debug.WriteLine(value); - } - - public override Encoding Encoding => Encoding.Default; - } -} \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/HeadlessTestRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/HeadlessTestRunner.cs index c127f41bc7..3bccb896dc 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/HeadlessTestRunner.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/HeadlessTestRunner.cs @@ -5,66 +5,64 @@ using System.Threading.Tasks; using Microsoft.DotNet.XHarness.TestRunners.Common; using Microsoft.DotNet.XHarness.TestRunners.Xunit; -using ObjCRuntime; using UIKit; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +class HeadlessTestRunner : iOSApplicationEntryPoint { - class HeadlessTestRunner : iOSApplicationEntryPoint - { - readonly HeadlessRunnerOptions _runnerOptions; - readonly TestOptions _options; + readonly HeadlessRunnerOptions _runnerOptions; + readonly TestOptions _options; - public HeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) - { - _runnerOptions = runnerOptions; - _options = options; - } + public HeadlessTestRunner(HeadlessRunnerOptions runnerOptions, TestOptions options) + { + _runnerOptions = runnerOptions; + _options = options; + } - protected override bool LogExcludedTests => true; + protected override bool LogExcludedTests => true; - protected override int? MaxParallelThreads => Environment.ProcessorCount; + protected override int? MaxParallelThreads => Environment.ProcessorCount; - protected override IDevice Device { get; } = new TestDevice(); + protected override IDevice Device { get; } = new TestDevice(); - protected override IEnumerable GetTestAssemblies() => - _options.Assemblies - .Distinct() - .Select(assembly => new TestAssemblyInfo(assembly, assembly.Location)); + protected override IEnumerable GetTestAssemblies() => + _options.Assemblies + .Distinct() + .Select(assembly => new TestAssemblyInfo(assembly, assembly.Location)); - protected override void TerminateWithSuccess() - { - var s = new ObjCRuntime.Selector("terminateWithSuccess"); - UIApplication.SharedApplication.PerformSelector(s, UIApplication.SharedApplication, 0); - } + protected override void TerminateWithSuccess() + { + var s = new ObjCRuntime.Selector("terminateWithSuccess"); + UIApplication.SharedApplication.PerformSelector(s, UIApplication.SharedApplication, 0); + } - protected override TestRunner GetTestRunner(LogWriter logWriter) - { - var testRunner = base.GetTestRunner(logWriter); - if (_options.SkipCategories?.Count > 0) - testRunner.SkipCategories(_options.SkipCategories); - return testRunner; - } + protected override TestRunner GetTestRunner(LogWriter logWriter) + { + var testRunner = base.GetTestRunner(logWriter); + if (_options.SkipCategories?.Count > 0) + testRunner.SkipCategories(_options.SkipCategories); + return testRunner; + } - public async Task RunTestsAsync() - { - TestsCompleted += OnTestsCompleted; + public async Task RunTestsAsync() + { + TestsCompleted += OnTestsCompleted; - await RunAsync(); + await RunAsync(); - TestsCompleted -= OnTestsCompleted; + TestsCompleted -= OnTestsCompleted; - void OnTestsCompleted(object? sender, TestRunResult results) - { - var message = - $"Tests run: {results.ExecutedTests} " + - $"Passed: {results.PassedTests} " + - $"Inconclusive: {results.InconclusiveTests} " + - $"Failed: {results.FailedTests} " + - $"Ignored: {results.SkippedTests}"; + void OnTestsCompleted(object? sender, TestRunResult results) + { + var message = + $"Tests run: {results.ExecutedTests} " + + $"Passed: {results.PassedTests} " + + $"Inconclusive: {results.InconclusiveTests} " + + $"Failed: {results.FailedTests} " + + $"Ignored: {results.SkippedTests}"; - Console.WriteLine(message); - } + Console.WriteLine(message); } } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs index a38321f624..21a66de6af 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestApplicationDelegate.cs @@ -3,128 +3,122 @@ using System.Collections.Generic; using System.Threading.Tasks; using Foundation; -using Microsoft.DotNet.XHarness.iOS.Shared.Execution; -using Microsoft.DotNet.XHarness.TestRunners.Common; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Hosting; using ObjCRuntime; using UIKit; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +public abstract class MauiTestApplicationDelegate : UIApplicationDelegate { - public abstract class MauiTestApplicationDelegate : UIApplicationDelegate + // TODO: https://github.com/xamarin/xamarin-macios/issues/12555 + readonly static string[] EnvVarNames = { + "NUNIT_AUTOSTART", + "NUNIT_AUTOEXIT", + "NUNIT_ENABLE_NETWORK", + "DISABLE_SYSTEM_PERMISSION_TESTS", + "NUNIT_HOSTNAME", + "NUNIT_TRANSPORT", + "NUNIT_LOG_FILE", + "NUNIT_HOSTPORT", + "USE_TCP_TUNNEL", + "RUN_END_TAG", + "NUNIT_ENABLE_XML_OUTPUT", + "NUNIT_ENABLE_XML_MODE", + "NUNIT_XML_VERSION", + "NUNIT_SORTNAMES", + "NUNIT_RUN_ALL", + "NUNIT_SKIPPED_METHODS", + "NUNIT_SKIPPED_CLASSES", + }; + + readonly static Dictionary EnvVars = new(); + + static MauiTestApplicationDelegate() { - // TODO: https://github.com/xamarin/xamarin-macios/issues/12555 - readonly static string[] EnvVarNames = { - "NUNIT_AUTOSTART", - "NUNIT_AUTOEXIT", - "NUNIT_ENABLE_NETWORK", - "DISABLE_SYSTEM_PERMISSION_TESTS", - "NUNIT_HOSTNAME", - "NUNIT_TRANSPORT", - "NUNIT_LOG_FILE", - "NUNIT_HOSTPORT", - "USE_TCP_TUNNEL", - "RUN_END_TAG", - "NUNIT_ENABLE_XML_OUTPUT", - "NUNIT_ENABLE_XML_MODE", - "NUNIT_XML_VERSION", - "NUNIT_SORTNAMES", - "NUNIT_RUN_ALL", - "NUNIT_SKIPPED_METHODS", - "NUNIT_SKIPPED_CLASSES", - }; - - readonly static Dictionary EnvVars = new(); - - static MauiTestApplicationDelegate() + // copy into dictionary for later + foreach (var envvar in EnvVarNames) { - // copy into dictionary for later - foreach (var envvar in EnvVarNames) - { - EnvVars[envvar] = Environment.GetEnvironmentVariable(envvar); - } - - // Add entry to indicate we're running headless - EnvVars.Add("headlessrunner", "true"); + EnvVars[envvar] = Environment.GetEnvironmentVariable(envvar); } + } - static void SetEnvironmentVariables() + static void SetEnvironmentVariables() + { + // read from dictionary + foreach (var envvar in EnvVars) { - // read from dictionary - foreach (var envvar in EnvVars) - { - Console.WriteLine($" {envvar.Key} = '{envvar.Value}'"); - Environment.SetEnvironmentVariable(envvar.Key, envvar.Value); - } + Console.WriteLine($" {envvar.Key} = '{envvar.Value}'"); + Environment.SetEnvironmentVariable(envvar.Key, envvar.Value); } + } - public static bool IsHeadlessRunner(string[] args) - { - // usually means this is from xharness - return args?.Length > 0 || Environment.GetEnvironmentVariable("NUNIT_AUTOEXIT")?.Length > 0; - } + public static bool IsHeadlessRunner(string[] args) + { + // usually means this is from xharness + return args?.Length > 0 || Environment.GetEnvironmentVariable("NUNIT_AUTOEXIT")?.Length > 0; + } - protected MauiTestApplicationDelegate() - { - Current = this; - } + protected MauiTestApplicationDelegate() + { + Current = this; + } - public static MauiTestApplicationDelegate Current { get; private set; } = null!; + public static MauiTestApplicationDelegate Current { get; private set; } = null!; - public IServiceProvider Services { get; private set; } = null!; + public IServiceProvider Services { get; private set; } = null!; - public TestOptions Options { get; private set; } = null!; + public TestOptions Options { get; private set; } = null!; - public HeadlessRunnerOptions RunnerOptions { get; private set; } = null!; + public HeadlessRunnerOptions RunnerOptions { get; private set; } = null!; - public override UIWindow? Window { get; set; } + public override UIWindow? Window { get; set; } - protected abstract MauiApp CreateMauiApp(); + protected abstract MauiApp CreateMauiApp(); - public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) + public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) + { + Runtime.MarshalManagedException += (object sender, MarshalManagedExceptionEventArgs args) => { - Runtime.MarshalManagedException += (object sender, MarshalManagedExceptionEventArgs args) => - { - Console.WriteLine("Marshaling managed exception"); - Console.WriteLine(" Exception: {0}", args.Exception); - Console.WriteLine(" Mode: {0}", args.ExceptionMode); + Console.WriteLine("Marshaling managed exception"); + Console.WriteLine(" Exception: {0}", args.Exception); + Console.WriteLine(" Mode: {0}", args.ExceptionMode); - }; - - Runtime.MarshalObjectiveCException += (object sender, MarshalObjectiveCExceptionEventArgs args) => - { - Console.WriteLine("Marshaling Objective-C exception"); - Console.WriteLine(" Exception: {0}", args.Exception); - Console.WriteLine(" Mode: {0}", args.ExceptionMode); - }; + }; - var mauiApp = CreateMauiApp(); - Services = mauiApp.Services; + Runtime.MarshalObjectiveCException += (object sender, MarshalObjectiveCExceptionEventArgs args) => + { + Console.WriteLine("Marshaling Objective-C exception"); + Console.WriteLine(" Exception: {0}", args.Exception); + Console.WriteLine(" Mode: {0}", args.ExceptionMode); + }; - SetEnvironmentVariables(); + var mauiApp = CreateMauiApp(); + Services = mauiApp.Services; - Options = Services.GetRequiredService(); - RunnerOptions = Services.GetRequiredService(); + SetEnvironmentVariables(); - return true; - } + Options = Services.GetRequiredService(); + RunnerOptions = Services.GetRequiredService(); - public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) - { - var tcs = new TaskCompletionSource(); + return true; + } - Window = new UIWindow(UIScreen.MainScreen.Bounds) - { - RootViewController = new MauiTestViewController(tcs.Task) - }; + public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + { + var tcs = new TaskCompletionSource(); - Window.MakeKeyAndVisible(); + Window = new UIWindow(UIScreen.MainScreen.Bounds) + { + RootViewController = new MauiTestViewController(tcs.Task) + }; - tcs.TrySetResult(); + Window.MakeKeyAndVisible(); - return true; - } + tcs.TrySetResult(); + return true; } -} \ No newline at end of file + +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestViewController.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestViewController.cs index a674ab3a18..2cb325eba4 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestViewController.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunner/iOS/MauiTestViewController.cs @@ -1,34 +1,32 @@ #nullable enable using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using ObjCRuntime; using UIKit; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; + +public class MauiTestViewController : UIViewController { - public class MauiTestViewController : UIViewController - { - Task? _task; + Task? _task; - public MauiTestViewController() - { - } + public MauiTestViewController() + { + } - public MauiTestViewController(Task task) - { - _task = task; - } + public MauiTestViewController(Task task) + { + _task = task; + } - public override async void ViewDidLoad() - { - base.ViewDidLoad(); + public override async void ViewDidLoad() + { + base.ViewDidLoad(); - if (_task is not null) - await _task; + if (_task is not null) + await _task; - var runner = MauiTestApplicationDelegate.Current.Services.GetRequiredService(); + var runner = MauiTestApplicationDelegate.Current.Services.GetRequiredService(); - await runner.RunTestsAsync(); - } + await runner.RunTestsAsync(); } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunnerOptions.cs b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunnerOptions.cs index 329760edc9..db0784cb42 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunnerOptions.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/HeadlessRunnerOptions.cs @@ -1,10 +1,9 @@ #nullable enable -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public class HeadlessRunnerOptions { - public class HeadlessRunnerOptions - { - public string TestResultsFilename { get; set; } = "TestResults.xml"; + public string TestResultsFilename { get; set; } = "TestResults.xml"; - public bool RequiresUIContext { get; set; } = true; - } + public bool RequiresUIContext { get; set; } = true; } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestDispatcher.cs b/test/MauiTestUtils/DeviceTests.Runners/TestDispatcher.cs index 1950a3baea..fc2ef59669 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestDispatcher.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/TestDispatcher.cs @@ -3,39 +3,38 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Dispatching; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public static class TestDispatcher { - public static class TestDispatcher - { - static IDispatcher? s_dispatcher; - static IDispatcherProvider? s_provider; + static IDispatcher? s_dispatcher; + static IDispatcherProvider? s_provider; - public static IDispatcherProvider Provider + public static IDispatcherProvider Provider + { + get { - get - { - if (s_provider is null) - s_provider = TestServices.Services.GetService(); + if (s_provider is null) + s_provider = TestServices.Services.GetService(); - if (s_provider is null) - throw new InvalidOperationException($"Test app did not provide a dispatcher."); + if (s_provider is null) + throw new InvalidOperationException($"Test app did not provide a dispatcher."); - return s_provider; - } + return s_provider; } + } - public static IDispatcher Current + public static IDispatcher Current + { + get { - get - { - if (s_dispatcher is null) - s_dispatcher = TestServices.Services.GetService(); + if (s_dispatcher is null) + s_dispatcher = TestServices.Services.GetService(); - if (s_dispatcher is null) - throw new InvalidOperationException($"Test app did not provide a dispatcher."); + if (s_dispatcher is null) + throw new InvalidOperationException($"Test app did not provide a dispatcher."); - return s_dispatcher; - } + return s_dispatcher; } } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestOptions.cs b/test/MauiTestUtils/DeviceTests.Runners/TestOptions.cs index 17f6237504..53dc8dc85b 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestOptions.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/TestOptions.cs @@ -2,19 +2,18 @@ using System.Collections.Generic; using System.Reflection; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public class TestOptions { - public class TestOptions - { - /// - /// The list of assemblies that contain tests. - /// - public List Assemblies { get; set; } = new List(); + /// + /// The list of assemblies that contain tests. + /// + public List Assemblies { get; set; } = new List(); - /// - /// The list of categories to skip in the form: - /// [category-name]=[skip-when-value] - /// - public List SkipCategories { get; set; } = new List(); - } -} \ No newline at end of file + /// + /// The list of categories to skip in the form: + /// [category-name]=[skip-when-value] + /// + public List SkipCategories { get; set; } = new List(); +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestServices.cs b/test/MauiTestUtils/DeviceTests.Runners/TestServices.cs index 8f5c726c74..ebc410f641 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestServices.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/TestServices.cs @@ -2,32 +2,31 @@ using System; using Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public static class TestServices { - public static class TestServices - { - static IServiceProvider? s_services = null; + static IServiceProvider? s_services = null; - public static IServiceProvider Services + public static IServiceProvider Services + { + get { - get + if (s_services is null) { - if (s_services is null) - { #if __ANDROID__ - s_services = MauiTestInstrumentation.Current?.Services ?? IPlatformApplication.Current?.Services; + s_services = MauiTestInstrumentation.Current?.Services ?? MauiApplication.Current.Services; #elif __IOS__ - s_services = MauiTestApplicationDelegate.Current?.Services ?? IPlatformApplication.Current?.Services; + s_services = MauiTestApplicationDelegate.Current?.Services ?? MauiUIApplicationDelegate.Current.Services; #elif WINDOWS - s_services = IPlatformApplication.Current?.Services; + s_services = MauiWinUIApplication.Current.Services; #endif - } + } - if (s_services is null) - throw new InvalidOperationException($"Test app could not find services."); + if (s_services is null) + throw new InvalidOperationException($"Test app could not find services."); - return s_services; - } + return s_services; } } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj b/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj index cc019e1265..f50c9bae78 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj +++ b/test/MauiTestUtils/DeviceTests.Runners/TestUtils.DeviceTests.Runners.csproj @@ -1,4 +1,4 @@ - + diff --git a/test/MauiTestUtils/DeviceTests.Runners/TestWindow.cs b/test/MauiTestUtils/DeviceTests.Runners/TestWindow.cs index 450ab97adb..bdf245428a 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/TestWindow.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/TestWindow.cs @@ -14,36 +14,36 @@ using PlatformView = System.Object; #endif -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners; + +public static class TestWindow { - public static class TestWindow - { - static PlatformView? s_platformWindow; + static PlatformView? s_platformWindow; - public static PlatformView PlatformWindow + public static PlatformView PlatformWindow + { + get { - get + if (s_platformWindow is null) { - if (s_platformWindow is null) - { #if __ANDROID__ - s_platformWindow = MauiTestInstrumentation.Current?.CurrentExecutionContext as PlatformView; + s_platformWindow = MauiTestInstrumentation.Current?.CurrentExecutionContext as PlatformView; #elif __IOS__ - s_platformWindow = MauiTestApplicationDelegate.Current?.Window; + s_platformWindow = MauiTestApplicationDelegate.Current?.Window; #endif - } + } - if (s_platformWindow is null) - { - var application = TestServices.Services.GetService(); - s_platformWindow = application?.Windows.FirstOrDefault()?.Handler?.PlatformView as PlatformView; - } + if (s_platformWindow is null) + { + var application = TestServices.Services.GetService(); + s_platformWindow = application?.Windows.FirstOrDefault()?.Handler?.PlatformView as PlatformView; + } - if (s_platformWindow is null) - throw new InvalidOperationException($"Test app did not provide a window."); + if (s_platformWindow is null) + throw new InvalidOperationException($"Test app did not provide a window."); - return s_platformWindow; - } + return s_platformWindow; } } -} \ No newline at end of file +} + diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/AssemblyRunInfo.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/AssemblyRunInfo.cs index ee2d30b48d..d42d8480b4 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/AssemblyRunInfo.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/AssemblyRunInfo.cs @@ -3,21 +3,20 @@ using System.Collections.Generic; using Xunit; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +public class AssemblyRunInfo { - public class AssemblyRunInfo + public AssemblyRunInfo(string assemblyFileName, TestAssemblyConfiguration configuration, IList testCases) { - public AssemblyRunInfo(string assemblyFileName, TestAssemblyConfiguration configuration, IList testCases) - { - AssemblyFileName = assemblyFileName ?? throw new ArgumentNullException(nameof(assemblyFileName)); - Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - TestCases = testCases ?? throw new ArgumentNullException(nameof(testCases)); - } + AssemblyFileName = assemblyFileName ?? throw new ArgumentNullException(nameof(assemblyFileName)); + Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + TestCases = testCases ?? throw new ArgumentNullException(nameof(testCases)); + } - public string AssemblyFileName { get; } + public string AssemblyFileName { get; } - public TestAssemblyConfiguration Configuration { get; } + public TestAssemblyConfiguration Configuration { get; } - public IList TestCases { get; } - } -} \ No newline at end of file + public IList TestCases { get; } +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Converters/RunStatusToColorConverter.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Converters/RunStatusToColorConverter.cs index b367a39294..d98bbf98c8 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Converters/RunStatusToColorConverter.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Converters/RunStatusToColorConverter.cs @@ -4,27 +4,26 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +class RunStatusToColorConverter : IValueConverter { - class RunStatusToColorConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - if (value is not RunStatus status || Application.Current == null) - return Colors.Red; - - return status switch - { - RunStatus.Ok => Application.Current.Resources["VisualRunnerSuccessfulTestsColor"], - RunStatus.Failed => Application.Current.Resources["VisualRunnerFailedTestsColor"], - RunStatus.NoTests => Application.Current.Resources["VisualRunnerNoTestsColor"], - RunStatus.NotRun => Application.Current.Resources["VisualRunnerNotRunTestsColor"], - RunStatus.Skipped => Application.Current.Resources["VisualRunnerSkippedTestsColor"], - _ => throw new ArgumentOutOfRangeException(nameof(value)), - }; - } + if (value is not RunStatus status || Application.Current == null) + return Colors.Red; - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => - throw new NotImplementedException(); + return status switch + { + RunStatus.Ok => Application.Current.Resources["VisualRunnerSuccessfulTestsColor"], + RunStatus.Failed => Application.Current.Resources["VisualRunnerFailedTestsColor"], + RunStatus.NoTests => Application.Current.Resources["VisualRunnerNoTestsColor"], + RunStatus.NotRun => Application.Current.Resources["VisualRunnerNotRunTestsColor"], + RunStatus.Skipped => Application.Current.Resources["VisualRunnerSkippedTestsColor"], + _ => throw new ArgumentOutOfRangeException(nameof(value)), + }; } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => + throw new NotImplementedException(); } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/DeviceRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/DeviceRunner.cs index 1085a7b969..c96107821b 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/DeviceRunner.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/DeviceRunner.cs @@ -8,172 +8,171 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Maui.Controls.Xaml; -using Microsoft.Maui.Storage; using Xunit; [assembly: XamlCompilation(XamlCompilationOptions.Compile)] -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +public class DeviceRunner : ITestListener, ITestRunner { - public class DeviceRunner : ITestListener, ITestRunner - { - readonly SynchronizationContext context = SynchronizationContext.Current; - readonly AsyncLock executionLock = new AsyncLock(); - readonly ITestNavigation _navigation; - readonly TestRunLogger _logger; - volatile bool cancelled; + readonly SynchronizationContext context = SynchronizationContext.Current; + readonly AsyncLock executionLock = new AsyncLock(); + readonly ITestNavigation _navigation; + readonly TestRunLogger _logger; + volatile bool cancelled; - public DeviceRunner(IReadOnlyCollection testAssemblies, ITestNavigation navigation, ILogger logger) - { - TestAssemblies = testAssemblies; - _navigation = navigation; - _logger = new TestRunLogger(logger); - } + public DeviceRunner(IReadOnlyCollection testAssemblies, ITestNavigation navigation, ILogger logger) + { + TestAssemblies = testAssemblies; + _navigation = navigation; + _logger = new TestRunLogger(logger); + } - public IReadOnlyCollection TestAssemblies { get; } + public IReadOnlyCollection TestAssemblies { get; } - public void RecordResult(TestResultViewModel result) - { - _logger.LogTestResult(result); - } + public void RecordResult(TestResultViewModel result) + { + _logger.LogTestResult(result); + } - public Task RunAsync(TestCaseViewModel test) - { - return RunAsync(new[] { test }); - } + public Task RunAsync(TestCaseViewModel test) + { + return RunAsync(new[] { test }); + } - public Task RunAsync(IEnumerable tests, string message = null) - { - var groups = tests - .GroupBy(t => t.AssemblyFileName) - .Select(g => new AssemblyRunInfo( - g.Key, - GetConfiguration(Path.GetFileNameWithoutExtension(g.Key)), - g.ToList())) - .ToList(); - - return RunAsync(groups, message); - } + public Task RunAsync(IEnumerable tests, string message = null) + { + var groups = tests + .GroupBy(t => t.AssemblyFileName) + .Select(g => new AssemblyRunInfo( + g.Key, + GetConfiguration(Path.GetFileNameWithoutExtension(g.Key)), + g.ToList())) + .ToList(); + + return RunAsync(groups, message); + } - public async Task RunAsync(IReadOnlyList runInfos, string message = null) + public async Task RunAsync(IReadOnlyList runInfos, string message = null) + { + using (await executionLock.LockAsync()) { - using (await executionLock.LockAsync()) + if (message == null) { - if (message == null) - { - message = runInfos.Count > 1 || runInfos.FirstOrDefault()?.TestCases.Count > 1 - ? "Run Multiple Tests" - : runInfos.FirstOrDefault()?.TestCases.FirstOrDefault()?.DisplayName; - } + message = runInfos.Count > 1 || runInfos.FirstOrDefault()?.TestCases.Count > 1 + ? "Run Multiple Tests" + : runInfos.FirstOrDefault()?.TestCases.FirstOrDefault()?.DisplayName; + } - _logger.LogTestStart(message); + _logger.LogTestStart(message); - try - { - await RunTests(() => runInfos); - } - finally - { - _logger.LogTestComplete(); - } + try + { + await RunTests(() => runInfos); + } + finally + { + _logger.LogTestComplete(); } } + } - public event Action OnDiagnosticMessage; + public event Action OnDiagnosticMessage; + + public Task> DiscoverAsync() + { + var tcs = new TaskCompletionSource>(); - public Task> DiscoverAsync() + RunAsync(() => { - var tcs = new TaskCompletionSource>(); + try + { + var runInfos = DiscoverTestsInAssemblies(); + var list = runInfos.Select(ri => new TestAssemblyViewModel(ri, _navigation, this)).ToList(); - RunAsync(() => + tcs.SetResult(list); + } + catch (Exception e) { - try - { - var runInfos = DiscoverTestsInAssemblies(); - var list = runInfos.Select(ri => new TestAssemblyViewModel(ri, _navigation, this)).ToList(); + tcs.SetException(e); + } + }); - tcs.SetResult(list); - } - catch (Exception e) - { - tcs.SetException(e); - } - }); + return tcs.Task; + } - return tcs.Task; - } + IEnumerable DiscoverTestsInAssemblies() + { + var result = new List(); - IEnumerable DiscoverTestsInAssemblies() + try { - var result = new List(); - - try + foreach (var assm in TestAssemblies) { - foreach (var assm in TestAssemblies) - { #if WINDOWS - var nameWithoutExt = assm.GetName().Name; - var assemblyFileName = FileSystemUtils.PlatformGetFullAppPackageFilePath($"{nameWithoutExt}.dll"); + var nameWithoutExt = assm.GetName().Name; + var assemblyFileName = Storage.FileSystemUtils.PlatformGetFullAppPackageFilePath($"{nameWithoutExt}.dll"); #elif ANDROID - // this is required to exist, but is not used - var assemblyFileName = assm.GetName().Name + ".dll"; - assemblyFileName = Path.Combine(Android.App.Application.Context.CacheDir.AbsolutePath, assemblyFileName); - if (!File.Exists(assemblyFileName)) - File.Create(assemblyFileName).Close(); + // this is required to exist, but is not used + var assemblyFileName = assm.GetName().Name + ".dll"; + assemblyFileName = Path.Combine(Android.App.Application.Context.CacheDir.AbsolutePath, assemblyFileName); + if (!File.Exists(assemblyFileName)) + File.Create(assemblyFileName).Close(); #else - var assemblyFileName = assm.Location; + var assemblyFileName = assm.Location; #endif - var configuration = GetConfiguration(assemblyFileName); - var discoveryOptions = TestFrameworkOptions.ForDiscovery(configuration); + var configuration = GetConfiguration(assemblyFileName); + var discoveryOptions = TestFrameworkOptions.ForDiscovery(configuration); - try - { - if (cancelled) - break; - - using (var framework = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName, null, false)) - using (var sink = new TestDiscoverySink(() => cancelled)) - { - framework.Find(false, sink, discoveryOptions); - sink.Finished.WaitOne(); - - result.Add(new AssemblyRunInfo( - assemblyFileName, - configuration, - sink.TestCases.Select(tc => new TestCaseViewModel(assemblyFileName, tc)).ToList())); - } - } - catch (Exception e) + try + { + if (cancelled) + break; + + using (var framework = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName, null, false)) + using (var sink = new TestDiscoverySink(() => cancelled)) { - Debug.WriteLine(e); + framework.Find(false, sink, discoveryOptions); + sink.Finished.WaitOne(); + + result.Add(new AssemblyRunInfo( + assemblyFileName, + configuration, + sink.TestCases.Select(tc => new TestCaseViewModel(assemblyFileName, tc)).ToList())); } } + catch (Exception e) + { + Debug.WriteLine(e); + } } - catch (Exception e) - { - Debug.WriteLine(e); - } - - return result; + } + catch (Exception e) + { + Debug.WriteLine(e); } - static TestAssemblyConfiguration GetConfiguration(string assemblyName) + return result; + } + + static TestAssemblyConfiguration GetConfiguration(string assemblyName) + { + var stream = GetConfigurationStreamForAssembly(assemblyName); + if (stream != null) { - var stream = GetConfigurationStreamForAssembly(assemblyName); - if (stream != null) + using (stream) { - using (stream) - { - return ConfigReader.Load(stream); - } + return ConfigReader.Load(stream); } - - return new TestAssemblyConfiguration(); } - static Stream GetConfigurationStreamForAssembly(string assemblyName) - { + return new TestAssemblyConfiguration(); + } + + static Stream GetConfigurationStreamForAssembly(string assemblyName) + { #if __ANDROID__ var assets = Android.App.Application.Context.Assets; var allAssets = assets.List(string.Empty); @@ -185,156 +184,155 @@ static Stream GetConfigurationStreamForAssembly(string assemblyName) return assets.Open("xunit.runner.json"); #else - // See if there's a directory with the assm name. this might be the case for appx - if (Directory.Exists(assemblyName)) + // See if there's a directory with the assm name. this might be the case for appx + if (Directory.Exists(assemblyName)) + { + if (File.Exists(Path.Combine(assemblyName, $"{assemblyName}.xunit.runner.json"))) { - if (File.Exists(Path.Combine(assemblyName, $"{assemblyName}.xunit.runner.json"))) - { - return File.OpenRead(Path.Combine(assemblyName, $"{assemblyName}.xunit.runner.json")); - } - - if (File.Exists(Path.Combine(assemblyName, "xunit.runner.json"))) - { - return File.OpenRead(Path.Combine(assemblyName, "xunit.runner.json")); - } + return File.OpenRead(Path.Combine(assemblyName, $"{assemblyName}.xunit.runner.json")); } - // Fallback to working dir - - // look for a file called assemblyName.xunit.runner.json first - if (File.Exists($"{assemblyName}.xunit.runner.json")) + if (File.Exists(Path.Combine(assemblyName, "xunit.runner.json"))) { - return File.OpenRead($"{assemblyName}.xunit.runner.json"); + return File.OpenRead(Path.Combine(assemblyName, "xunit.runner.json")); } + } - if (File.Exists("xunit.runner.json")) - { - return File.OpenRead("xunit.runner.json"); - } -#endif + // Fallback to working dir - return null; + // look for a file called assemblyName.xunit.runner.json first + if (File.Exists($"{assemblyName}.xunit.runner.json")) + { + return File.OpenRead($"{assemblyName}.xunit.runner.json"); } - Task RunTests(Func> testCaseAccessor) + if (File.Exists("xunit.runner.json")) { - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + return File.OpenRead("xunit.runner.json"); + } +#endif - void Handler() - { - var toDispose = new List(); + return null; + } - try - { - cancelled = false; - var assemblies = testCaseAccessor(); - var parallelizeAssemblies = assemblies.All(runInfo => runInfo.Configuration.ParallelizeAssemblyOrDefault); + Task RunTests(Func> testCaseAccessor) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - if (parallelizeAssemblies) - { - assemblies - .Select(runInfo => RunTestsInAssemblyAsync(toDispose, runInfo)) - .ToList() - .ForEach(@event => @event.WaitOne()); - } - else - { - foreach (var runInfo in assemblies) - { - RunTestsInAssembly(toDispose, runInfo); - } - } - } - catch (Exception e) + void Handler() + { + var toDispose = new List(); + + try + { + cancelled = false; + var assemblies = testCaseAccessor(); + var parallelizeAssemblies = assemblies.All(runInfo => runInfo.Configuration.ParallelizeAssemblyOrDefault); + + if (parallelizeAssemblies) { - tcs.SetException(e); + assemblies + .Select(runInfo => RunTestsInAssemblyAsync(toDispose, runInfo)) + .ToList() + .ForEach(@event => @event.WaitOne()); } - finally + else { - toDispose.ForEach(disposable => disposable.Dispose()); - tcs.TrySetResult(null); + foreach (var runInfo in assemblies) + { + RunTestsInAssembly(toDispose, runInfo); + } } } + catch (Exception e) + { + tcs.SetException(e); + } + finally + { + toDispose.ForEach(disposable => disposable.Dispose()); + tcs.TrySetResult(null); + } + } - RunAsync(Handler); + RunAsync(Handler); - return tcs.Task; - } + return tcs.Task; + } - void RunTestsInAssembly(List toDispose, AssemblyRunInfo runInfo) - { - if (cancelled) - return; + void RunTestsInAssembly(List toDispose, AssemblyRunInfo runInfo) + { + if (cancelled) + return; - var assemblyFileName = runInfo.AssemblyFileName; + var assemblyFileName = runInfo.AssemblyFileName; - var longRunningSeconds = runInfo.Configuration.LongRunningTestSecondsOrDefault; + var longRunningSeconds = runInfo.Configuration.LongRunningTestSecondsOrDefault; - var controller = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName); + var controller = new XunitFrontController(AppDomainSupport.Denied, assemblyFileName); - lock (toDispose) - toDispose.Add(controller); + 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 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 executionOptions = TestFrameworkOptions.ForExecution(runInfo.Configuration); - var diagSink = new DiagnosticMessageSink(d => context.Post(_ => OnDiagnosticMessage?.Invoke(d), null), runInfo.AssemblyFileName, executionOptions.GetDiagnosticMessagesOrDefault()); + var diagSink = new DiagnosticMessageSink(d => context.Post(_ => OnDiagnosticMessage?.Invoke(d), null), runInfo.AssemblyFileName, executionOptions.GetDiagnosticMessagesOrDefault()); - var deviceExecSink = new DeviceExecutionSink(xunitTestCases, this, context); + var deviceExecSink = new DeviceExecutionSink(xunitTestCases, this, context); - IExecutionSink resultsSink = new DelegatingExecutionSummarySink(deviceExecSink, () => cancelled); - if (longRunningSeconds > 0) - resultsSink = new DelegatingLongRunningTestDetectionSink(resultsSink, TimeSpan.FromSeconds(longRunningSeconds), diagSink); + 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)); + 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(); + controller.RunTests(xunitTestCases.Select(tc => tc.Value.TestCase).ToList(), resultsSink, executionOptions); + resultsSink.Finished.WaitOne(); - deviceExecSink.OnMessage(new TestAssemblyExecutionFinished(assm, executionOptions, resultsSink.ExecutionSummary)); - } + deviceExecSink.OnMessage(new TestAssemblyExecutionFinished(assm, executionOptions, resultsSink.ExecutionSummary)); + } - ManualResetEvent RunTestsInAssemblyAsync(List toDispose, AssemblyRunInfo runInfo) - { - var @event = new ManualResetEvent(false); + ManualResetEvent RunTestsInAssemblyAsync(List toDispose, AssemblyRunInfo runInfo) + { + var @event = new ManualResetEvent(false); - void Handler() + void Handler() + { + try { - try - { - RunTestsInAssembly(toDispose, runInfo); - } - finally - { - @event.Set(); - } + RunTestsInAssembly(toDispose, runInfo); } + finally + { + @event.Set(); + } + } - RunAsync(Handler); + RunAsync(Handler); - return @event; - } + return @event; + } - static async void RunAsync(Action action) - { - var task = Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); + static async void RunAsync(Action action) + { + var task = Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - try - { - await task; - } - catch (Exception e) + try + { + await task; + } + catch (Exception e) + { + if (Debugger.IsAttached) { - if (Debugger.IsAttached) - { - Debugger.Break(); - Debug.WriteLine(e); - } + Debugger.Break(); + Debug.WriteLine(e); } } } diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestListener.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestListener.cs index a5cc43f767..d113bd0b43 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestListener.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestListener.cs @@ -1,8 +1,7 @@ #nullable enable -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +public interface ITestListener { - public interface ITestListener - { - void RecordResult(TestResultViewModel result); - } + void RecordResult(TestResultViewModel result); } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestRunner.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestRunner.cs index f3caf9bdb9..2bf88e4a70 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestRunner.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/ITestRunner.cs @@ -3,18 +3,17 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +interface ITestRunner { - interface ITestRunner - { - Task> DiscoverAsync(); + Task> DiscoverAsync(); - Task RunAsync(TestCaseViewModel test); + Task RunAsync(TestCaseViewModel test); - Task RunAsync(IEnumerable tests, string? message = null); + Task RunAsync(IEnumerable tests, string? message = null); - Task RunAsync(IReadOnlyList runInfos, string? message = null); + Task RunAsync(IReadOnlyList runInfos, string? message = null); - event Action OnDiagnosticMessage; - } -} \ No newline at end of file + event Action OnDiagnosticMessage; +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml index 2a9bde3b38..af7b2cbc7b 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml @@ -1,6 +1,6 @@  diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml.cs index e7af7c74f5..730767d687 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/MauiVisualRunnerApp.xaml.cs @@ -3,36 +3,35 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +partial class MauiVisualRunnerApp : Application { - partial class MauiVisualRunnerApp : Application - { - readonly TestOptions _options; - readonly ILogger _logger; + readonly TestOptions _options; + readonly ILogger _logger; - public MauiVisualRunnerApp(TestOptions options, ILogger logger) - { - _options = options; - _logger = logger; + public MauiVisualRunnerApp(TestOptions options, ILogger logger) + { + _options = options; + _logger = logger; - InitializeComponent(); - } + InitializeComponent(); + } - protected override Window CreateWindow(IActivationState? activationState) - { - var hp = new HomePage(); + protected override Window CreateWindow(IActivationState? activationState) + { + var hp = new HomePage(); - var nav = new TestNavigator(hp.Navigation); + var nav = new TestNavigator(hp.Navigation); - var runner = new DeviceRunner(_options.Assemblies, nav, _logger); + var runner = new DeviceRunner(_options.Assemblies, nav, _logger); - var vm = new HomeViewModel(nav, runner); + var vm = new HomeViewModel(nav, runner); - hp.BindingContext = vm; + hp.BindingContext = vm; - var navPage = new NavigationPage(hp); + var navPage = new NavigationPage(hp); - return new Window(navPage); - } + return new Window(navPage); } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/INavigation.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/INavigation.cs index 864f0973dd..ffd2c834b8 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/INavigation.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/INavigation.cs @@ -1,10 +1,9 @@ #nullable enable using System.Threading.Tasks; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +public interface ITestNavigation { - public interface ITestNavigation - { - Task NavigateTo(PageType page, object? dataContext = null); - } + Task NavigateTo(PageType page, object? dataContext = null); } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/Navigator.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/Navigator.cs index 3db271b9f3..76dbc844d4 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/Navigator.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/Navigator.cs @@ -4,31 +4,30 @@ using Microsoft.Maui.Controls; using Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +class TestNavigator : ITestNavigation { - class TestNavigator : ITestNavigation - { - readonly INavigation _navigation; + readonly INavigation _navigation; - public TestNavigator(INavigation navigation) - { - _navigation = navigation; - } + public TestNavigator(INavigation navigation) + { + _navigation = navigation; + } - public Task NavigateTo(PageType page, object? dataContext = null) + public Task NavigateTo(PageType page, object? dataContext = null) + { + ContentPage p = page switch { - ContentPage p = page switch - { - PageType.Home => new HomePage(), - PageType.AssemblyTestList => new TestAssemblyPage(), - PageType.TestResult => new TestResultPage(), - PageType.Credits => new CreditsPage(), - _ => throw new ArgumentOutOfRangeException(nameof(page)), - }; + PageType.Home => new HomePage(), + PageType.AssemblyTestList => new TestAssemblyPage(), + PageType.TestResult => new TestResultPage(), + PageType.Credits => new CreditsPage(), + _ => throw new ArgumentOutOfRangeException(nameof(page)), + }; - p.BindingContext = dataContext; + p.BindingContext = dataContext; - return _navigation.PushAsync(p); - } + return _navigation.PushAsync(p); } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/PageType.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/PageType.cs index 6930a7b832..68579d2fb8 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/PageType.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Navigation/PageType.cs @@ -1,12 +1,11 @@ #nullable enable -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner; + +public enum PageType { - public enum PageType - { - Home, - AssemblyTestList, - TestResult, - Credits - } -} \ No newline at end of file + Home, + AssemblyTestList, + TestResult, + Credits +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml index 24c625045f..54339403a7 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml @@ -1,6 +1,6 @@  diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml.cs index e95fc8f9d9..9814859dbc 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/CreditsPage.xaml.cs @@ -2,20 +2,19 @@ using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.Controls; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages; + +partial class CreditsPage : ContentPage { - partial class CreditsPage : ContentPage + public CreditsPage() { - public CreditsPage() - { - InitializeComponent(); - } + InitializeComponent(); + } - void OnNavigating(object? sender, WebNavigatingEventArgs e) - { - Browser.OpenAsync(e.Url); + void OnNavigating(object? sender, WebNavigatingEventArgs e) + { + Browser.OpenAsync(e.Url); - e.Cancel = true; - } + e.Cancel = true; } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/HomePage.xaml.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/HomePage.xaml.cs index 90e111d05f..184f037f06 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/HomePage.xaml.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/HomePage.xaml.cs @@ -1,70 +1,22 @@ #nullable enable -using System; -using System.Diagnostics; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.Controls; -using Microsoft.Maui.TestUtils.DeviceTests.Runners.HeadlessRunner; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages; + +partial class HomePage : ContentPage { - partial class HomePage : ContentPage + public HomePage() { - public HomePage() - { - InitializeComponent(); - - this.Loaded += HomePage_Loaded; - } - - bool hasRunHeadless = false; - - private async void HomePage_Loaded(object? sender, EventArgs e) - { - string? testResultsFile = null; - -#if WINDOWS - var cliArgs = Environment.GetCommandLineArgs(); - if (cliArgs.Length > 1) - { - testResultsFile = HeadlessTestRunner.TestResultsFile = ControlsHeadlessTestRunner.TestResultsFile = cliArgs.Skip(1).FirstOrDefault(); - ControlsHeadlessTestRunner.LoopCount = int.Parse(cliArgs.Skip(2).FirstOrDefault() ?? "-1"); - } -#endif - - if (!string.IsNullOrEmpty(testResultsFile) && !hasRunHeadless) - { - hasRunHeadless = true; - -#if !WINDOWS - var headlessRunner = Handler!.MauiContext!.Services.GetRequiredService(); - - await headlessRunner.RunTestsAsync(); -#else - if (cliArgs.Length >= 3) - { - var headlessRunner = Handler!.MauiContext!.Services.GetRequiredService(); - await headlessRunner.RunTestsAsync(); - } - else - { - var headlessRunner = Handler!.MauiContext!.Services.GetRequiredService(); - await headlessRunner.RunTestsAsync(); - } -#endif - - Process.GetCurrentProcess().Kill(); - } - } + InitializeComponent(); + } - protected override void OnAppearing() - { - base.OnAppearing(); + protected override void OnAppearing() + { + base.OnAppearing(); - assemblyList.SelectedItem = null; + assemblyList.SelectedItem = null; - if (BindingContext is ViewModelBase vm) - vm.OnAppearing(); - } + if (BindingContext is ViewModelBase vm) + vm.OnAppearing(); } } \ No newline at end of file diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestAssemblyPage.xaml.cs b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestAssemblyPage.xaml.cs index 1599ee395a..5c9828f8aa 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestAssemblyPage.xaml.cs +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestAssemblyPage.xaml.cs @@ -1,23 +1,22 @@ #nullable enable using Microsoft.Maui.Controls; -namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages +namespace Microsoft.Maui.TestUtils.DeviceTests.Runners.VisualRunner.Pages; + +partial class TestAssemblyPage : ContentPage { - partial class TestAssemblyPage : ContentPage + public TestAssemblyPage() { - public TestAssemblyPage() - { - InitializeComponent(); - } + InitializeComponent(); + } - protected override void OnAppearing() - { - base.OnAppearing(); + protected override void OnAppearing() + { + base.OnAppearing(); - testsList.SelectedItem = null; + testsList.SelectedItem = null; - if (BindingContext is ViewModelBase vm) - vm.OnAppearing(); - } + if (BindingContext is ViewModelBase vm) + vm.OnAppearing(); } -} \ No newline at end of file +} diff --git a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestResultPage.xaml b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestResultPage.xaml index 8c5d8b949d..49f5201734 100644 --- a/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestResultPage.xaml +++ b/test/MauiTestUtils/DeviceTests.Runners/VisualRunner/Pages/TestResultPage.xaml @@ -16,9 +16,9 @@ -