diff --git a/src/MSTestX.Adapter/GlobalUsings.cs b/src/MSTestX.Adapter/GlobalUsings.cs new file mode 100644 index 0000000..05d435f --- /dev/null +++ b/src/MSTestX.Adapter/GlobalUsings.cs @@ -0,0 +1 @@ +global using Resource = Microsoft.VisualStudio.TestTools.UnitTesting.Resource; \ No newline at end of file diff --git a/src/MSTestX.Adapter/MSTestX.Adapter.csproj b/src/MSTestX.Adapter/MSTestX.Adapter.csproj index f503037..5eb8cac 100644 --- a/src/MSTestX.Adapter/MSTestX.Adapter.csproj +++ b/src/MSTestX.Adapter/MSTestX.Adapter.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;net6.0 Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter MSTestX.TestAdapter @@ -15,10 +15,11 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb true Unit Test, Xamarin, Android, iOS, MSTest, VSTest, TestFX, MSTest.TestAdapter - 2.2.10.1 + 2.2.10.2 true ./../../nupkg The cross-platform adapter to discover and execute MSTest Framework based tests. This is a .NET Standard 2.0 compilation of the MSTest.TestAdapter for reusing the test adapter on other platforms, like Xamarin Android and iOS. + 10 @@ -51,6 +52,10 @@ PlatformServices\%(RecursiveDir)%(Filename)%(Extension) + + + PlatformServices\Services\%(Filename)%(Extension) + diff --git a/src/TestAppRunner/TestAppRunner.Maui/App.cs b/src/TestAppRunner/TestAppRunner.Maui/App.cs index 8b5b763..d80e487 100644 --- a/src/TestAppRunner/TestAppRunner.Maui/App.cs +++ b/src/TestAppRunner/TestAppRunner.Maui/App.cs @@ -18,7 +18,7 @@ protected override void OnSettingsMenuLoaded(List> menuIte menuItems.Add(new Tuple("Custom action", () => Current.MainPage.DisplayAlert("Hello!", "You clicked a custom action", "OK"))); menuItems.Add(new Tuple("Run two specific tests", async () => { - var tests= TestCases?.Where(t => t.DisplayName == "TestOK" || t.DisplayName == "MoreTests_1"); + var tests= TestCases?.Where(t => t.DisplayName == "TestOK" || t.DisplayName == "TestAttachments"); try { var results = await RunTestsAsync(tests); @@ -32,7 +32,7 @@ protected override void OnSettingsMenuLoaded(List> menuIte })); menuItems.Add(new Tuple("Custom test list", () => { - var tests = TestCases?.Where(t => t.DisplayName == "TestOK" || t.DisplayName == "MoreTests_1"); + var tests = TestCases?.Where(t => t.DisplayName == "TestOK" || t.DisplayName == "TestAttachments"); NavigateToTestList("Custom Test List", tests); })); base.OnSettingsMenuLoaded(menuItems); diff --git a/src/TestAppRunner/TestAppRunner.Maui/MauiProgram.cs b/src/TestAppRunner/TestAppRunner.Maui/MauiProgram.cs index badd526..4f9a175 100644 --- a/src/TestAppRunner/TestAppRunner.Maui/MauiProgram.cs +++ b/src/TestAppRunner/TestAppRunner.Maui/MauiProgram.cs @@ -10,6 +10,13 @@ public static MauiApp CreateMauiApp() builder .UseTestApp((testOptions) => { +#if __IOS__ + var logsdir = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "UnitTests"); +#elif __ANDROID__ + var logsdir = System.IO.Path.Combine(Microsoft.Maui.ApplicationModel.Platform.CurrentActivity.ApplicationContext.FilesDir.Path, "UnitTests"); +#elif WINDOWS + var logsdir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnitTests"); +#endif testOptions.TestAssemblies = new System.Reflection.Assembly[] { typeof(UnitTests.AttachmentTests).Assembly }; // configure default timeout testOptions.SettingsXml = @"" + @@ -19,6 +26,11 @@ public static MauiApp CreateMauiApp() "30000" + "" + ""; + if (!System.IO.Directory.Exists(logsdir)) + System.IO.Directory.CreateDirectory(logsdir); + + testOptions.TrxOutputPath = System.IO.Path.Combine(logsdir, $"UnitTests.trx"); + testOptions.ProgressLogPath = System.IO.Path.Combine(logsdir, $"UnitTest.log"); return testOptions; }) .ConfigureFonts(fonts => diff --git a/src/TestAppRunner/TestAppRunner/App.xaml.cs b/src/TestAppRunner/TestAppRunner/App.xaml.cs index 515aaf5..033c9a4 100644 --- a/src/TestAppRunner/TestAppRunner/App.xaml.cs +++ b/src/TestAppRunner/TestAppRunner/App.xaml.cs @@ -6,6 +6,7 @@ using TestAppRunner.Views; using Microsoft.Maui.Controls; using Microsoft.Maui.Controls.Xaml; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; [assembly: XamlCompilation (XamlCompilationOptions.Compile)] namespace MSTestX @@ -64,6 +65,7 @@ public void Initialize(TestOptions settings = null) TestRunnerVM.Instance.Initialize(); } #endif + public IRunSettings TestRunSettings => TestRunnerVM.Instance.Settings; /// /// Navigate to a page with a custom set of tests. @@ -110,7 +112,7 @@ protected override void OnResume() /// public System.Threading.Tasks.Task> RunTestsAsync(IEnumerable testCases, Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter.IRunSettings settings = null) { - return TestRunnerVM.Instance.Run(testCases, settings); + return TestRunnerVM.Instance.Run(testCases, settings ?? TestRunnerVM.Instance.Settings); } /// diff --git a/src/TestAppRunner/TestAppRunner/TrxWriter.cs b/src/TestAppRunner/TestAppRunner/TrxWriter.cs index bd1cd98..eeace6d 100644 --- a/src/TestAppRunner/TestAppRunner/TrxWriter.cs +++ b/src/TestAppRunner/TestAppRunner/TrxWriter.cs @@ -10,7 +10,8 @@ internal class TrxWriter : TestLoggerEvents, ITestLogger, ITestLoggerWithParamet public TrxWriter(string trxOutputPath) { logger = new Microsoft.VisualStudio.TestPlatform.Extensions.TrxLogger.TrxLogger(); - var parameters = new Dictionary() { { "TestRunDirectory", "." } }; + var testRunDirectory = new FileInfo(trxOutputPath).Directory.FullName; + var parameters = new Dictionary() { { "TestRunDirectory", testRunDirectory } }; if (!string.IsNullOrEmpty(trxOutputPath)) parameters.Add("LogFileName", trxOutputPath); logger.Initialize(this, parameters); diff --git a/src/TestAppRunner/TestAppRunner/ViewModels/TestResultVM.cs b/src/TestAppRunner/TestAppRunner/ViewModels/TestResultVM.cs index 56d7fe2..7a1c466 100644 --- a/src/TestAppRunner/TestAppRunner/ViewModels/TestResultVM.cs +++ b/src/TestAppRunner/TestAppRunner/ViewModels/TestResultVM.cs @@ -69,9 +69,9 @@ public IEnumerable Attachments { get { - if(result != null && result.Attachments != null && result.Attachments.Any()) - return result.Attachments.SelectMany(t => t.Attachments); - return null; + if (result != null && result.Attachments != null && result.Attachments.Any()) + return result.Attachments.Where(t => t.Attachments is not null).SelectMany(t => t.Attachments).Where(a => a is not null); + return Enumerable.Empty(); } } diff --git a/src/TestAppRunner/TestAppRunner/ViewModels/TestRunnerVM.cs b/src/TestAppRunner/TestAppRunner/ViewModels/TestRunnerVM.cs index db2aae0..7a67c29 100644 --- a/src/TestAppRunner/TestAppRunner/ViewModels/TestRunnerVM.cs +++ b/src/TestAppRunner/TestAppRunner/ViewModels/TestRunnerVM.cs @@ -379,11 +379,22 @@ private async Task> Run_Internal(IEnumerable t { try { +#if DEBUG + File.Delete(Settings.TrxOutputPath); +#endif trxWriter?.OnTestRunComplete(new Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.TestRunCompleteEventArgs(null, false, false, null, null, TimeSpan.Zero)); +#if DEBUG + var trx = File.ReadAllText(Settings.TrxOutputPath); + System.Diagnostics.Debug.WriteLine(trx); +#endif } - catch(PlatformNotSupportedException) // Throws due to https://github.com/microsoft/vstest/issues/4736. However it's thrown after TRX is written + catch (PlatformNotSupportedException) // Throws due to https://github.com/microsoft/vstest/issues/4736. However it's thrown after TRX is written { Debug.Assert(File.Exists(Settings.TrxOutputPath)); +#if DEBUG + var trx = File.ReadAllText(Settings.TrxOutputPath); + System.Diagnostics.Debug.WriteLine(trx); +#endif } trxWriter = null; Logger.Log($"TRXREPORT LOCATION: {Settings.TrxOutputPath}"); @@ -492,6 +503,14 @@ private static string ComputerName #endif void ITestExecutionRecorder.RecordResult(TestResult testResult) { + var execId = GetProperty("ExecutionId", testResult, Guid.Empty); + if (execId == Guid.Empty) + { + var prop = testResult.Properties.Where(p => p.Id == "ExecutionId").FirstOrDefault(); + if (prop != null) + testResult.SetPropertyValue(prop, Guid.NewGuid()); + } + results?.Add(testResult); var innerResultsCount = GetProperty("InnerResultsCount", testResult, 0); var parentExecId = GetProperty("ParentExecId", testResult, Guid.Empty); diff --git a/src/TestAppRunner/TestAppRunner/Views/AllTestsPage.xaml.cs b/src/TestAppRunner/TestAppRunner/Views/AllTestsPage.xaml.cs index 553ed7f..dde8c61 100644 --- a/src/TestAppRunner/TestAppRunner/Views/AllTestsPage.xaml.cs +++ b/src/TestAppRunner/TestAppRunner/Views/AllTestsPage.xaml.cs @@ -1,4 +1,5 @@ -using TestAppRunner.ViewModels; +using System.IO.Compression; +using TestAppRunner.ViewModels; namespace TestAppRunner.Views { @@ -110,17 +111,71 @@ private void RunFailed_Clicked() private void Save_Report_Clicked() { - string path = Path.Combine(Path.GetTempPath(), DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".trx"); + string filename = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + string path = Path.Combine(Path.GetTempPath(), filename); + bool hasAttachments = false; + var tests = TestRunnerVM.Instance.Tests.Select(t => t.Result).Where(r => r is not null); try { - TrxWriter.GenerateReport(path, TestRunnerVM.Instance.Tests.Select(t => t.Result).Where(r => r is not null)); + if (!string.IsNullOrEmpty(TestRunnerVM.Instance.Settings.TestRunResultsDirectory) && + Path.Exists(TestRunnerVM.Instance.Settings.TestRunResultsDirectory)) + { + var folders = new DirectoryInfo(TestRunnerVM.Instance.Settings.TestRunResultsDirectory); + foreach (var test in tests.Where(t => t.Attachments is not null)) + { + foreach (var a in test.Attachments.Where(a => a is not null)) + { + foreach (var a2 in a.Attachments.Where(a2 => a2 is not null)) + { + if (File.Exists(a2.Uri.OriginalString)) + { + hasAttachments = true; + goto exit_loop; + } + } + } + } + } + exit_loop: + TrxWriter.GenerateReport(path + ".trx", tests); } catch (PlatformNotSupportedException) // Throws due to https://github.com/microsoft/vstest/issues/4736. However it's thrown after TRX is written { System.Diagnostics.Debug.Assert(File.Exists(path)); } + if(hasAttachments) + { + // Create zip instead + if (Directory.Exists(TestRunnerVM.Instance.Settings.TestRunResultsDirectory)) + { + using (ZipArchive archive = ZipFile.Open(path + ".zip", ZipArchiveMode.Create)) + { + archive.CreateEntryFromFile(path + ".trx", filename + ".trx"); + foreach (var test in tests.Where(t => t.Attachments is not null)) + { + foreach (var a in test.Attachments.Where(a => a is not null)) + { + foreach (var a2 in a.Attachments.Where(a2 => a2 is not null)) + { + string file = a2.Uri.OriginalString; + if (file.StartsWith(TestRunnerVM.Instance.Settings.TestRunResultsDirectory)) + { + var entryName = file.Substring(TestRunnerVM.Instance.Settings.TestRunResultsDirectory.Length).TrimStart('/'); + var executionId = test.GetPropertyValue(test.Properties.FirstOrDefault(t=>t.Id == "ExecutionId"))?.ToString(); + var entrypath = Path.Combine("In", executionId, Environment.MachineName, entryName); + archive.CreateEntryFromFile(file, entrypath); + } + } + } + } + } + Microsoft.Maui.ApplicationModel.DataTransfer.Share.RequestAsync( + new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFileRequest("TRX Test Report", new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFile(path + ".zip"))); + return; + } + } Microsoft.Maui.ApplicationModel.DataTransfer.Share.RequestAsync( - new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFileRequest("TRX Test Report", new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFile(path))); + new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFileRequest("TRX Test Report", new Microsoft.Maui.ApplicationModel.DataTransfer.ShareFile(path + ".trx"))); } private void StopRun_Clicked() {