From f46b373fac5c3feb93b6cf6f3bcb74aa2c3341da Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Mon, 14 Feb 2022 19:20:00 +0100 Subject: [PATCH] Add unit tests for the artifact post processing (#3352) Add unit tests for the artifact post processing --- .../Helpers/FileHelper.cs | 70 ++-- .../Helpers/Interfaces/IFileHelper.cs | 12 + .../PublicAPI/PublicAPI.Shipped.txt | 2 + .../PublicAPI/net/PublicAPI.Shipped.txt | 2 + .../ArtifactProcessingManager.cs | 57 ++-- .../CommandLine/CommandLineOptions.cs | 2 +- .../ArtifactProcessingCollectModeProcessor.cs | 9 +- ...ifactProcessingPostProcessModeProcessor.cs | 9 +- ...tPlatform.CrossPlatEngine.UnitTests.csproj | 1 + .../PostProcessing/ArtifactProcessingTests.cs | 302 ++++++++++++++++++ ...ifactProcessingCollectModeProcessorTest.cs | 44 +++ ...tProcessingPostProcessModeProcessorTest.cs | 75 +++++ .../TestSessionCorrelationIdProcessorTests.cs | 44 +++ 13 files changed, 535 insertions(+), 94 deletions(-) create mode 100644 test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs create mode 100644 test/vstest.console.UnitTests/Processors/ArtifactProcessingCollectModeProcessorTest.cs create mode 100644 test/vstest.console.UnitTests/Processors/ArtifactProcessingPostProcessModeProcessorTest.cs create mode 100644 test/vstest.console.UnitTests/Processors/TestSessionCorrelationIdProcessorTests.cs diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs index e3517d47ce..4c628de611 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/FileHelper.cs @@ -24,40 +24,27 @@ public class FileHelper : IFileHelper /// public DirectoryInfo CreateDirectory(string path) - { - return Directory.CreateDirectory(path); - } + => Directory.CreateDirectory(path); /// public string GetCurrentDirectory() - { - return Directory.GetCurrentDirectory(); - } + => Directory.GetCurrentDirectory(); /// public bool Exists(string path) - { - return File.Exists(path); - } + => File.Exists(path); /// public bool DirectoryExists(string path) - { - return Directory.Exists(path); - } + => Directory.Exists(path); /// public Stream GetStream(string filePath, FileMode mode, FileAccess access = FileAccess.ReadWrite) - { - return new FileStream(filePath, mode, access); - } + => new FileStream(filePath, mode, access); /// public Stream GetStream(string filePath, FileMode mode, FileAccess access, FileShare share) - { - return new FileStream(filePath, mode, access, share); - } - + => new FileStream(filePath, mode, access, share); /// public IEnumerable EnumerateFiles( @@ -79,40 +66,29 @@ public IEnumerable EnumerateFiles( /// public FileAttributes GetFileAttributes(string path) - { - return new FileInfo(path).Attributes; - } + => new FileInfo(path).Attributes; /// public Version GetFileVersion(string path) - { - var currentFileVersion = FileVersionInfo.GetVersionInfo(path)?.FileVersion; - return Version.TryParse(currentFileVersion, out var currentVersion) ? currentVersion : DefaultFileVersion; - } + => Version.TryParse(FileVersionInfo.GetVersionInfo(path)?.FileVersion, out var currentVersion) ? + currentVersion : + DefaultFileVersion; /// public void CopyFile(string sourcePath, string destinationPath) - { - File.Copy(sourcePath, destinationPath); - } + => File.Copy(sourcePath, destinationPath); /// public void MoveFile(string sourcePath, string destinationPath) - { - File.Move(sourcePath, destinationPath); - } + => File.Move(sourcePath, destinationPath); /// public void WriteAllTextToFile(string filePath, string content) - { - File.WriteAllText(filePath, content); - } + => File.WriteAllText(filePath, content); /// public string GetFullPath(string path) - { - return Path.GetFullPath(path); - } + => Path.GetFullPath(path); /// public void DeleteEmptyDirectroy(string dirPath) @@ -133,20 +109,20 @@ public void DeleteEmptyDirectroy(string dirPath) /// public string[] GetFiles(string path, string searchPattern, SearchOption searchOption) - { - return Directory.GetFiles(path, searchPattern, searchOption); - } + => Directory.GetFiles(path, searchPattern, searchOption); /// public void Delete(string path) - { - File.Delete(path); - } + => File.Delete(path); public void DeleteDirectory(string directoryPath, bool recursive) - { - Directory.Delete(directoryPath, recursive); - } + => Directory.Delete(directoryPath, recursive); + + public string GetTempPath() + => Path.GetTempPath(); + + public long GetFileLength(string path) + => new FileInfo(path).Length; } #endif diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs index 7c128a1885..956fbc4fb0 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IFileHelper.cs @@ -157,4 +157,16 @@ public interface IFileHelper /// /// void Delete(string path); + + /// + /// Get temporary file path + /// + /// + public string GetTempPath(); + + /// + /// Get file length + /// + /// + public long GetFileLength(string path); } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt index 12a6594564..8ebabecdc0 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt @@ -71,6 +71,8 @@ Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.Cop Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.Delete(string path) -> void Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.DeleteEmptyDirectroy(string directoryPath) -> void Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.DeleteDirectory(string directoryPath, bool recursive) -> void +Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.GetTempPath() -> string +Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.GetFileLength(string path) -> long Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.DirectoryExists(string path) -> bool Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.Exists(string path) -> bool Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper.GetCurrentDirectory() -> string diff --git a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt index d94451d668..ec37ebb800 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt @@ -57,6 +57,8 @@ Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.CreateDirectory Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.Delete(string path) -> void Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.DeleteEmptyDirectroy(string dirPath) -> void Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.DeleteDirectory(string directoryPath, bool recursive) -> void +Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.GetTempPath() -> string +Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.GetFileLength(string path) -> long Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.DirectoryExists(string path) -> bool Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.EnumerateFiles(string directory, System.IO.SearchOption searchOption, params string[] endsWithSearchPatterns) -> System.Collections.Generic.IEnumerable Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.Exists(string path) -> bool diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs index e9b7477a22..181eb26e7a 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/PostProcessing/ArtifactProcessingManager.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#nullable disable - namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.ArtifactProcessing; using System; @@ -31,11 +29,11 @@ internal class ArtifactProcessingManager : IArtifactProcessingManager private const string RunsettingsFileName = "runsettings.xml"; private const string ExecutionCompleteFileName = "executionComplete.json"; - private readonly string _testSessionCorrelationId; + private readonly string? _testSessionCorrelationId; private readonly IFileHelper _fileHelper; private readonly ITestRunAttachmentsProcessingManager _testRunAttachmentsProcessingManager; - private readonly string _testSessionProcessArtifactFolder; - private readonly string _processArtifactFolder; + private readonly string? _testSessionProcessArtifactFolder; + private readonly string? _processArtifactFolder; private readonly IDataSerializer _dataSerialized; private readonly ITestRunAttachmentsProcessingEventsHandler _testRunAttachmentsProcessingEventsHandler; private readonly IFeatureFlag _featureFlag; @@ -49,28 +47,29 @@ public ArtifactProcessingManager(string testSessionCorrelationId) : FeatureFlag.Instance) { } - public ArtifactProcessingManager(string testSessionCorrelationId, - IFileHelper fileHelper, - ITestRunAttachmentsProcessingManager testRunAttachmentsProcessingManager, - IDataSerializer dataSerialized, - ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler, - IFeatureFlag featureFlag) + public ArtifactProcessingManager(string? testSessionCorrelationId, + IFileHelper fileHelper!!, + ITestRunAttachmentsProcessingManager testRunAttachmentsProcessingManager!!, + IDataSerializer dataSerialized!!, + ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler!!, + IFeatureFlag featureFlag!!) { + _fileHelper = fileHelper; + _testRunAttachmentsProcessingManager = testRunAttachmentsProcessingManager; + _dataSerialized = dataSerialized; + _testRunAttachmentsProcessingEventsHandler = testRunAttachmentsProcessingEventsHandler; + _featureFlag = featureFlag; + // We don't validate for null, it's expected, we'll have testSessionCorrelationId only in case of .NET SDK run. if (testSessionCorrelationId is not null) { _testSessionCorrelationId = testSessionCorrelationId; - _processArtifactFolder = Path.Combine(Path.GetTempPath(), _testSessionCorrelationId); + _processArtifactFolder = Path.Combine(_fileHelper.GetTempPath(), _testSessionCorrelationId); _testSessionProcessArtifactFolder = Path.Combine(_processArtifactFolder, $"{Process.GetCurrentProcess().Id}_{Guid.NewGuid()}"); } - _fileHelper = fileHelper; - _testRunAttachmentsProcessingManager = testRunAttachmentsProcessingManager; - _dataSerialized = dataSerialized; - _testRunAttachmentsProcessingEventsHandler = testRunAttachmentsProcessingEventsHandler; - _featureFlag = featureFlag; } - public void CollectArtifacts(TestRunCompleteEventArgs testRunCompleteEventArgs, string runSettingsXml) + public void CollectArtifacts(TestRunCompleteEventArgs testRunCompleteEventArgs!!, string runSettingsXml!!) { if (!_featureFlag.IsEnabled(FeatureFlag.ARTIFACTS_POSTPROCESSING)) { @@ -159,14 +158,12 @@ private async Task DataCollectorsAttachmentsPostProcessing(TestArtifacts[] testA { // We take the biggest runsettings in size, it should be the one with more configuration. // In future we can think to merge...but it's not easy for custom config, we could break something. - string runsettingsFile = testArtifacts + string? runsettingsFile = testArtifacts .SelectMany(x => x.Artifacts.Where(x => x.Type == ArtifactType.Runsettings)) - .Select(x => new FileInfo(x.FileName)) - .OrderByDescending(x => x.Length) - .FirstOrDefault()? - .FullName; + .OrderByDescending(x => _fileHelper.GetFileLength(x.FileName)) + .FirstOrDefault()?.FileName; - string runsettingsXml = null; + string? runsettingsXml = null; if (runsettingsFile is not null) { using var artifactStream = _fileHelper.GetStream(runsettingsFile, FileMode.Open, FileAccess.Read); @@ -190,11 +187,11 @@ private async Task DataCollectorsAttachmentsPostProcessing(TestArtifacts[] testA string executionCompleteMessage = await streamReader.ReadToEndAsync(); EqtTrace.Verbose($"ArtifactProcessingManager.MergeDataCollectorAttachments: ExecutionComplete message \n{executionCompleteMessage}"); TestRunCompleteEventArgs eventArgs = _dataSerialized.DeserializePayload(_dataSerialized.DeserializeMessage(executionCompleteMessage)); - foreach (var invokedDataCollector in eventArgs?.InvokedDataCollectors) + foreach (var invokedDataCollector in eventArgs?.InvokedDataCollectors ?? Enumerable.Empty()) { invokedDataCollectors.Add(invokedDataCollector); } - foreach (var attachmentSet in eventArgs?.AttachmentSets) + foreach (var attachmentSet in eventArgs?.AttachmentSets ?? Enumerable.Empty()) { attachments.Add(attachmentSet); } @@ -204,7 +201,7 @@ await _testRunAttachmentsProcessingManager.ProcessTestRunAttachmentsAsync(runset new RequestData() { IsTelemetryOptedIn = IsTelemetryOptedIn(), - ProtocolConfig = ObjectModel.Constants.DefaultProtocolConfig + ProtocolConfig = Constants.DefaultProtocolConfig }, attachments, invokedDataCollectors, @@ -219,11 +216,11 @@ private TestArtifacts[] LoadTestArtifacts() => _fileHelper.GetFiles(_processArti .Select(testSessionArtifact => new TestArtifacts(testSessionArtifact.Key, testSessionArtifact.Select(x => ParseArtifact(x.Artifact)).Where(x => x is not null).ToArray())) .ToArray(); - private static Artifact ParseArtifact(string fileName) => + private static Artifact? ParseArtifact(string fileName!!) => Path.GetFileName(fileName) switch { - RunsettingsFileName => new Artifact(fileName, ArtifactType.ExecutionComplete), - ExecutionCompleteFileName => new Artifact(fileName, ArtifactType.Runsettings), + RunsettingsFileName => new Artifact(fileName, ArtifactType.Runsettings), + ExecutionCompleteFileName => new Artifact(fileName, ArtifactType.ExecutionComplete), _ => null }; diff --git a/src/vstest.console/CommandLine/CommandLineOptions.cs b/src/vstest.console/CommandLine/CommandLineOptions.cs index 6d92e2d097..26b119b513 100644 --- a/src/vstest.console/CommandLine/CommandLineOptions.cs +++ b/src/vstest.console/CommandLine/CommandLineOptions.cs @@ -81,7 +81,7 @@ internal static CommandLineOptions Instance /// /// Default constructor. /// - protected CommandLineOptions() + internal CommandLineOptions() { BatchSize = DefaultBatchSize; TestStatsEventTimeout = _defaultRetrievalTimeout; diff --git a/src/vstest.console/Processors/ArtifactProcessingCollectModeProcessor.cs b/src/vstest.console/Processors/ArtifactProcessingCollectModeProcessor.cs index 348c608cf1..b085ba3637 100644 --- a/src/vstest.console/Processors/ArtifactProcessingCollectModeProcessor.cs +++ b/src/vstest.console/Processors/ArtifactProcessingCollectModeProcessor.cs @@ -32,7 +32,7 @@ public Lazy Metadata { if (_metadata == null) { - _metadata = new Lazy(() => new ArtifactProcessingCollectModeProcessorCapabilities(CommandLineOptions.Instance)); + _metadata = new Lazy(() => new ArtifactProcessingCollectModeProcessorCapabilities()); } return _metadata; @@ -63,13 +63,6 @@ public Lazy Executor internal class ArtifactProcessingCollectModeProcessorCapabilities : BaseArgumentProcessorCapabilities { - private readonly CommandLineOptions _commandLineOptions; - - public ArtifactProcessingCollectModeProcessorCapabilities(CommandLineOptions options) - { - _commandLineOptions = options ?? throw new ArgumentNullException(nameof(options)); - } - public override string CommandName => ArtifactProcessingCollectModeProcessor.CommandName; public override bool AllowMultiple => false; diff --git a/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs b/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs index c23182c1e3..1693e0d058 100644 --- a/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs +++ b/src/vstest.console/Processors/ArtifactProcessingPostProcessModeProcessor.cs @@ -36,7 +36,7 @@ public Lazy Metadata { if (_metadata == null) { - _metadata = new Lazy(() => new ArtifactProcessingPostProcessModeProcessorCapabilities(CommandLineOptions.Instance)); + _metadata = new Lazy(() => new ArtifactProcessingPostProcessModeProcessorCapabilities()); } return _metadata; @@ -73,13 +73,6 @@ public static bool ContainsPostProcessCommand(string[] args, IFeatureFlag featur internal class ArtifactProcessingPostProcessModeProcessorCapabilities : BaseArgumentProcessorCapabilities { - private readonly CommandLineOptions _commandLineOptions; - - public ArtifactProcessingPostProcessModeProcessorCapabilities(CommandLineOptions options) - { - _commandLineOptions = options ?? throw new ArgumentNullException(nameof(options)); - } - public override string CommandName => ArtifactProcessingPostProcessModeProcessor.CommandName; public override bool AllowMultiple => false; diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Microsoft.TestPlatform.CrossPlatEngine.UnitTests.csproj b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Microsoft.TestPlatform.CrossPlatEngine.UnitTests.csproj index a8aab0afa3..f9e9226d18 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Microsoft.TestPlatform.CrossPlatEngine.UnitTests.csproj +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Microsoft.TestPlatform.CrossPlatEngine.UnitTests.csproj @@ -25,6 +25,7 @@ + diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs new file mode 100644 index 0000000000..9930111e69 --- /dev/null +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/PostProcessing/ArtifactProcessingTests.cs @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace TestPlatform.CrossPlatEngine.UnitTests; + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.TestPlatform.Common.Utilities; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.ArtifactProcessing; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; +using Microsoft.VisualStudio.TestPlatform.Utilities; +using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +[TestClass] +public class ArtifactProcessingTests +{ + private readonly Mock _fileHelperMock = new(); + private readonly Mock _testRunAttachmentsProcessingManagerMock = new(); + private readonly Mock _testRunAttachmentsProcessingEventsHandlerMock = new(); + private readonly Mock _featureFlagMock = new(); + private readonly Mock _dataSerializer = new(); + private readonly Mock _testRunStatistics = new(); + private ArtifactProcessingManager _artifactProcessingManager; + + public ArtifactProcessingTests() + { + _featureFlagMock.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + _fileHelperMock.Setup(x => x.GetTempPath()).Returns("/tmp"); + + _artifactProcessingManager = + new ArtifactProcessingManager(Guid.NewGuid().ToString(), + _fileHelperMock.Object, + _testRunAttachmentsProcessingManagerMock.Object, + _dataSerializer.Object, + _testRunAttachmentsProcessingEventsHandlerMock.Object, + _featureFlagMock.Object); + } + + [TestMethod] + public void CollectArtifacts_NullSessionIdShouldReturn() + { + // arrange + _artifactProcessingManager = + new ArtifactProcessingManager(null, + _fileHelperMock.Object, + _testRunAttachmentsProcessingManagerMock.Object, + _dataSerializer.Object, + _testRunAttachmentsProcessingEventsHandlerMock.Object, + _featureFlagMock.Object); + + // act + var testRunCompleteEventArgs = new TestRunCompleteEventArgs(_testRunStatistics.Object, + false, + false, + null, + new Collection() + { + new AttachmentSet(new Uri("//sample"),"") + }, + TimeSpan.Zero); + + _artifactProcessingManager.CollectArtifacts(testRunCompleteEventArgs, string.Empty); + + //assert + _fileHelperMock.Verify(x => x.WriteAllTextToFile(It.IsAny(), It.IsAny()), Times.Never); + } + + [TestMethod] + public void CollectArtifacts_ShouldSerializeToDisk() + { + // arrange + var testRunCompleteEventArgs = new TestRunCompleteEventArgs(_testRunStatistics.Object, + false, + false, + null, + new Collection() + { + new AttachmentSet(new Uri("//sample"),"") + }, + TimeSpan.Zero); + + // act + _artifactProcessingManager.CollectArtifacts(testRunCompleteEventArgs, string.Empty); + + // assert + _fileHelperMock.Verify(x => x.CreateDirectory(It.IsAny()), Times.Once); + _fileHelperMock.Verify(x => x.WriteAllTextToFile(It.IsAny(), It.IsAny()), Times.Exactly(2)); + _dataSerializer.Verify(x => x.SerializePayload(It.IsAny(), It.IsAny()), Times.Once); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_NullSessionIdShouldReturn() + { + // arrange + _artifactProcessingManager = + new ArtifactProcessingManager(null, + _fileHelperMock.Object, + _testRunAttachmentsProcessingManagerMock.Object, + _dataSerializer.Object, + _testRunAttachmentsProcessingEventsHandlerMock.Object, + _featureFlagMock.Object); + + // act + await _artifactProcessingManager.PostProcessArtifactsAsync(); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_NoArtifactsShouldReturn() + { + // arrange + _fileHelperMock.Setup(x => x.DirectoryExists(It.IsAny())).Returns(false); + + // act + await _artifactProcessingManager.PostProcessArtifactsAsync(); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Never); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_ShouldRunPostProcessing() + { + // arrange + _fileHelperMock.Setup(x => x.DirectoryExists(It.IsAny())).Returns(true); + _fileHelperMock.Setup(x => x.GetFiles(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, string pattern, SearchOption so) => new string[2] { "/tmp/sessionId/runsettings.xml", "/tmp/sessionId/executionComplete.json" }); + _fileHelperMock.Setup(x => x.GetStream(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, FileMode mode, FileAccess access) => + { + if (path.EndsWith("runsettings.xml")) + { + return new MemoryStream(Encoding.UTF8.GetBytes(RunSettingsProviderExtensions.EmptyRunSettings)); + } + + if (path.EndsWith("executionComplete.json")) + { + var testRunCompleteEventArgs = new TestRunCompleteEventArgs(null, + false, + false, + null, + new Collection() { new AttachmentSet(new Uri("attachment://dummy"), "attachment") }, + TimeSpan.Zero); + + string serializedEventArgs = JsonDataSerializer.Instance.SerializePayload(MessageType.ExecutionComplete, testRunCompleteEventArgs); + return new MemoryStream(Encoding.UTF8.GetBytes(serializedEventArgs)); + } + + Assert.Fail(); + throw new Exception("Unexpected"); + }); + _dataSerializer.Setup(x => x.SerializePayload(It.IsAny(), It.IsAny())).Returns((string message, object payload) + => JsonDataSerializer.Instance.SerializePayload(message, payload)); + _dataSerializer.Setup(x => x.DeserializeMessage(It.IsAny())).Returns((string rawMessage) + => JsonDataSerializer.Instance.DeserializeMessage(rawMessage)); + _dataSerializer.Setup(x => x.DeserializePayload(It.IsAny())).Returns((Message message) + => JsonDataSerializer.Instance.DeserializePayload(message)); + + // act + await _artifactProcessingManager.PostProcessArtifactsAsync(); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Once); + _testRunAttachmentsProcessingManagerMock.Verify(x => x.ProcessTestRunAttachmentsAsync(It.IsAny(), + It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny(), + It.IsAny()), Times.Once); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_NullRunSettings_ShouldRunPostProcessing() + { + // arrange + _fileHelperMock.Setup(x => x.DirectoryExists(It.IsAny())).Returns(true); + _fileHelperMock.Setup(x => x.GetFiles(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, string pattern, SearchOption so) => new string[1] { "/tmp/sessionId/executionComplete.json" }); + _fileHelperMock.Setup(x => x.GetStream(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, FileMode mode, FileAccess access) => + { + if (path.EndsWith("executionComplete.json")) + { + var testRunCompleteEventArgs = new TestRunCompleteEventArgs(null, + false, + false, + null, + new Collection() { new AttachmentSet(new Uri("attachment://dummy"), "attachment") }, + TimeSpan.Zero); + + string serializedEventArgs = JsonDataSerializer.Instance.SerializePayload(MessageType.ExecutionComplete, testRunCompleteEventArgs); + return new MemoryStream(Encoding.UTF8.GetBytes(serializedEventArgs)); + } + + Assert.Fail(); + throw new Exception("Unexpected"); + }); + _dataSerializer.Setup(x => x.SerializePayload(It.IsAny(), It.IsAny())).Returns((string message, object payload) + => JsonDataSerializer.Instance.SerializePayload(message, payload)); + _dataSerializer.Setup(x => x.DeserializeMessage(It.IsAny())).Returns((string rawMessage) + => JsonDataSerializer.Instance.DeserializeMessage(rawMessage)); + _dataSerializer.Setup(x => x.DeserializePayload(It.IsAny())).Returns((Message message) + => JsonDataSerializer.Instance.DeserializePayload(message)); + + // act + await _artifactProcessingManager.PostProcessArtifactsAsync(); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Once); + _testRunAttachmentsProcessingManagerMock.Verify(x => x.ProcessTestRunAttachmentsAsync(It.IsAny(), + It.IsAny(), It.IsAny>(), It.IsAny>(), + It.IsAny(), It.IsAny()), Times.Once); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_EmptyInvokedDataCollectors_ShouldRunPostProcessing() + { + // arrange + _fileHelperMock.Setup(x => x.DirectoryExists(It.IsAny())).Returns(true); + _fileHelperMock.Setup(x => x.GetFiles(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, string pattern, SearchOption so) => new string[1] { "/tmp/sessionId/runsettings.xml" }); + _fileHelperMock.Setup(x => x.GetStream(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, FileMode mode, FileAccess access) => + { + if (path.EndsWith("runsettings.xml")) + { + return new MemoryStream(Encoding.UTF8.GetBytes(RunSettingsProviderExtensions.EmptyRunSettings)); + } + + Assert.Fail(); + throw new Exception("Unexpected"); + }); + _dataSerializer.Setup(x => x.SerializePayload(It.IsAny(), It.IsAny())).Returns((string message, object payload) + => JsonDataSerializer.Instance.SerializePayload(message, payload)); + _dataSerializer.Setup(x => x.DeserializeMessage(It.IsAny())).Returns((string rawMessage) + => JsonDataSerializer.Instance.DeserializeMessage(rawMessage)); + _dataSerializer.Setup(x => x.DeserializePayload(It.IsAny())).Returns((Message message) + => JsonDataSerializer.Instance.DeserializePayload(message)); + + // act + await _artifactProcessingManager.PostProcessArtifactsAsync(); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Once); + _testRunAttachmentsProcessingManagerMock.Verify(x => x.ProcessTestRunAttachmentsAsync(It.IsAny(), + It.IsAny(), It.IsAny>(), It.IsAny>(), + It.IsAny(), It.IsAny()), Times.Once); + } + + [TestMethod] + public async Task PostProcessArtifactsAsync_DeserializationException_ShouldStopPostProcessing() + { + // arrange + _fileHelperMock.Setup(x => x.DirectoryExists(It.IsAny())).Returns(true); + _fileHelperMock.Setup(x => x.GetFiles(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, string pattern, SearchOption so) => new string[1] { "/tmp/sessionId/executionComplete.json" }); + _fileHelperMock.Setup(x => x.GetStream(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((string path, FileMode mode, FileAccess access) => + { + if (path.EndsWith("executionComplete.json")) + { + var testRunCompleteEventArgs = new TestRunCompleteEventArgs(null, + false, + false, + null, + new Collection() { new AttachmentSet(new Uri("attachment://dummy"), "attachment") }, + TimeSpan.Zero); + + string serializedEventArgs = JsonDataSerializer.Instance.SerializePayload(MessageType.ExecutionComplete, testRunCompleteEventArgs); + return new MemoryStream(Encoding.UTF8.GetBytes(serializedEventArgs)); + } + + Assert.Fail(); + throw new Exception("Unexpected"); + }); + _dataSerializer.Setup(x => x.SerializePayload(It.IsAny(), It.IsAny())).Returns((string message, object payload) + => JsonDataSerializer.Instance.SerializePayload(message, payload)); + _dataSerializer.Setup(x => x.DeserializeMessage(It.IsAny())).Returns((string rawMessage) => throw new Exception("Malformed json")); + + // act + await Assert.ThrowsExceptionAsync(() => _artifactProcessingManager.PostProcessArtifactsAsync()); + + // assert + _fileHelperMock.Verify(x => x.DeleteDirectory(It.IsAny(), It.IsAny()), Times.Once); + _testRunAttachmentsProcessingManagerMock.Verify(x => x.ProcessTestRunAttachmentsAsync(It.IsAny(), + It.IsAny(), It.IsAny>(), It.IsAny>(), + It.IsAny(), It.IsAny()), Times.Never); + } +} diff --git a/test/vstest.console.UnitTests/Processors/ArtifactProcessingCollectModeProcessorTest.cs b/test/vstest.console.UnitTests/Processors/ArtifactProcessingCollectModeProcessorTest.cs new file mode 100644 index 0000000000..572570d83f --- /dev/null +++ b/test/vstest.console.UnitTests/Processors/ArtifactProcessingCollectModeProcessorTest.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors; + +using System; + +using TestPlatform.CommandLine.Processors; + +using TestTools.UnitTesting; + +[TestClass] +public class ArtifactProcessingCollectModeProcessorTest +{ + [TestMethod] + public void ProcessorExecutorInitialize_ShouldFailIfNullCommandOption() => + Assert.ThrowsException(() => new ArtifactProcessingCollectModeProcessorExecutor(null)); + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldNotFailIfNullArg() + { + ArtifactProcessingCollectModeProcessorExecutor artifactProcessingCollectModeProcessorExecutor = new(new CommandLineOptions()); + artifactProcessingCollectModeProcessorExecutor.Initialize(null); + } + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldSetCommandOption() + { + var commandOptions = new CommandLineOptions(); + ArtifactProcessingCollectModeProcessorExecutor artifactProcessingCollectModeProcessorExecutor = new(commandOptions); + artifactProcessingCollectModeProcessorExecutor.Initialize(null); + Assert.AreEqual(ArtifactProcessingMode.Collect, commandOptions.ArtifactProcessingMode); + } + + [TestMethod] + public void ProcessorCapabilities() + { + ArtifactProcessingCollectModeProcessorCapabilities processorCapabilities = new(); + Assert.IsNull(processorCapabilities.HelpContentResourceName); + Assert.AreEqual(ArgumentProcessorPriority.CliRunSettings, processorCapabilities.Priority); + Assert.AreEqual(HelpContentPriority.None, processorCapabilities.HelpPriority); + Assert.AreEqual("/ArtifactsProcessingMode-Collect", processorCapabilities.CommandName); + } +} diff --git a/test/vstest.console.UnitTests/Processors/ArtifactProcessingPostProcessModeProcessorTest.cs b/test/vstest.console.UnitTests/Processors/ArtifactProcessingPostProcessModeProcessorTest.cs new file mode 100644 index 0000000000..ed1322c26a --- /dev/null +++ b/test/vstest.console.UnitTests/Processors/ArtifactProcessingPostProcessModeProcessorTest.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors; + +using System; + +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; +using Microsoft.VisualStudio.TestPlatform.Utilities; + +using Moq; + +using TestPlatform.CommandLine.Processors; + +using TestTools.UnitTesting; + +[TestClass] +public class ArtifactProcessingPostProcessModeProcessorTest +{ + private readonly Mock _artifactProcessingManagerMock = new(); + private readonly Mock _featureFlagMock = new(); + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldFailIfNullCtor() + { + Assert.ThrowsException(() => new ArtifactProcessingPostProcessModeProcessorExecutor(null, _artifactProcessingManagerMock.Object)); + Assert.ThrowsException(() => new ArtifactProcessingPostProcessModeProcessorExecutor(new CommandLineOptions(), null)); + } + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldNotFailIfNullArg() + { + ArtifactProcessingPostProcessModeProcessorExecutor artifactProcessingPostProcessModeProcessorExecutor = new(new CommandLineOptions(), _artifactProcessingManagerMock.Object); + artifactProcessingPostProcessModeProcessorExecutor.Initialize(null); + } + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldSetCommandOption() + { + var commandOptions = new CommandLineOptions(); + ArtifactProcessingPostProcessModeProcessorExecutor artifactProcessingPostProcessModeProcessorExecutor = new(commandOptions, _artifactProcessingManagerMock.Object); + artifactProcessingPostProcessModeProcessorExecutor.Initialize(null); + Assert.AreEqual(ArtifactProcessingMode.PostProcess, commandOptions.ArtifactProcessingMode); + } + + [TestMethod] + public void ProcessorCapabilities() + { + ArtifactProcessingPostProcessModeProcessorCapabilities processorCapabilities = new(); + Assert.IsNull(processorCapabilities.HelpContentResourceName); + Assert.AreEqual(ArgumentProcessorPriority.Normal, processorCapabilities.Priority); + Assert.AreEqual(HelpContentPriority.None, processorCapabilities.HelpPriority); + Assert.AreEqual("/ArtifactsProcessingMode-PostProcess", processorCapabilities.CommandName); + } + + [TestMethod] + public void ProcessorExecutorInitialize_ExceptionShouldNotBubbleUp() + { + _artifactProcessingManagerMock.Setup(x => x.PostProcessArtifactsAsync()).Callback(() => throw new Exception()); + ArtifactProcessingPostProcessModeProcessorExecutor artifactProcessingPostProcessModeProcessorExecutor = new(new CommandLineOptions(), _artifactProcessingManagerMock.Object); + artifactProcessingPostProcessModeProcessorExecutor.Initialize(null); + Assert.AreEqual(ArgumentProcessorResult.Fail, artifactProcessingPostProcessModeProcessorExecutor.Execute()); + } + + [TestMethod] + public void ArtifactProcessingPostProcessMode_ContainsPostProcessCommand() + { + _featureFlagMock.Setup(x => x.IsEnabled(It.IsAny())).Returns(true); + Assert.IsTrue(ArtifactProcessingPostProcessModeProcessor.ContainsPostProcessCommand(new string[] { "--artifactsProcessingMode-postprocess" }, _featureFlagMock.Object)); + Assert.IsTrue(ArtifactProcessingPostProcessModeProcessor.ContainsPostProcessCommand(new string[] { "--ARTIfactsProcessingMode-postprocess" }, _featureFlagMock.Object)); + Assert.IsFalse(ArtifactProcessingPostProcessModeProcessor.ContainsPostProcessCommand(new string[] { "-ARTIfactsProcessingMode-postprocess" }, _featureFlagMock.Object)); + Assert.IsFalse(ArtifactProcessingPostProcessModeProcessor.ContainsPostProcessCommand(new string[] { "--ARTIfactsProcessingMode-postproces" }, _featureFlagMock.Object)); + Assert.IsFalse(ArtifactProcessingPostProcessModeProcessor.ContainsPostProcessCommand(null, _featureFlagMock.Object)); + } +} diff --git a/test/vstest.console.UnitTests/Processors/TestSessionCorrelationIdProcessorTests.cs b/test/vstest.console.UnitTests/Processors/TestSessionCorrelationIdProcessorTests.cs new file mode 100644 index 0000000000..ed24ae1232 --- /dev/null +++ b/test/vstest.console.UnitTests/Processors/TestSessionCorrelationIdProcessorTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests.Processors; + +using System; + +using TestPlatform.CommandLine.Processors; + +using TestTools.UnitTesting; + +[TestClass] +public class TestSessionCorrelationIdProcessorTests +{ + [TestMethod] + public void ProcessorExecutorInitialize_ShouldFailIfNullCommandOption() => + Assert.ThrowsException(() => new TestSessionCorrelationIdProcessorModeProcessorExecutor(null)); + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldFailIfNullSession() + { + TestSessionCorrelationIdProcessorModeProcessorExecutor testSessionCorrelationIdProcessor = new(new CommandLineOptions()); + Assert.ThrowsException(() => testSessionCorrelationIdProcessor.Initialize(null)); + } + + [TestMethod] + public void ProcessorExecutorInitialize_ShouldSetCommandOption() + { + var commandOptions = new CommandLineOptions(); + TestSessionCorrelationIdProcessorModeProcessorExecutor testSessionCorrelationIdProcessor = new(commandOptions); + testSessionCorrelationIdProcessor.Initialize("sessionId"); + Assert.IsNotNull(commandOptions.TestSessionCorrelationId); + } + + [TestMethod] + public void ProcessorCapabilities() + { + TestSessionCorrelationIdProcessorCapabilities processorCapabilities = new(); + Assert.IsNull(processorCapabilities.HelpContentResourceName); + Assert.AreEqual(ArgumentProcessorPriority.CliRunSettings, processorCapabilities.Priority); + Assert.AreEqual(HelpContentPriority.None, processorCapabilities.HelpPriority); + Assert.AreEqual("/TestSessionCorrelationId", processorCapabilities.CommandName); + } +}