From 5aa11a3fa1a2943038e01933232c192e5217bdf5 Mon Sep 17 00:00:00 2001 From: "Shiva Shankar T (VSTT)" Date: Tue, 27 Mar 2018 12:02:24 +0530 Subject: [PATCH 1/4] Added an option to deploy the test source dependencies and satellite dlls by default. This option will be on by default. It can turned off in which case deployment would just deploy the test source location and skip the test results folder if the test results folder is within it. Added UTs and E2E tests for the change --- TestFx.sln | 29 +++- .../PlatformServices.Desktop.csproj | 5 +- .../Services/DesktopTestDeployment.cs | 4 +- ...gsProvider.cs => MSTestAdapterSettings.cs} | 84 +++-------- .../Services/MSTestSettingsProvider.cs | 61 ++++++++ .../Utilities/DeploymentItemUtility.cs | 10 +- .../Utilities/DeploymentResult.cs | 31 ++++ .../Utilities/DeploymentUtility.cs | 136 ++++++++---------- .../Utilities/FileUtility.cs | 49 ++++--- .../Utilities/IAppDomain.cs | 5 +- .../Utilities/Validate.cs | 40 ++++++ .../Smoke.E2E.Tests/DeploymentTests.cs | 36 +++++ .../Smoke.E2E.Tests/Smoke.E2E.Tests.csproj | 1 + .../DeploymentTestProject/DeploymentFile.xml | 1 + .../DeploymentTestProject.csproj | 36 +++++ .../DeploymentTestProject/EmptyDataFile.xml | 1 + .../Properties/AssemblyInfo1.cs | 33 +++++ .../TestCaseDeploymentFile.xml | 1 + .../DeploymentTestProject/UnitTest1.cs | 31 ++++ .../Services/MSTestAdapterSettingsTests.cs | 45 ++++++ .../Utilities/DeploymentUtilityTests.cs | 59 ++++++-- .../Utilities/FileUtilityTests.cs | 82 +++++++++++ 22 files changed, 594 insertions(+), 186 deletions(-) rename src/Adapter/PlatformServices.Desktop/Services/{DesktopSettingsProvider.cs => MSTestAdapterSettings.cs} (85%) create mode 100644 src/Adapter/PlatformServices.Desktop/Services/MSTestSettingsProvider.cs create mode 100644 src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs create mode 100644 src/Adapter/PlatformServices.Desktop/Utilities/Validate.cs create mode 100644 test/E2ETests/Smoke.E2E.Tests/DeploymentTests.cs create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/DeploymentFile.xml create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/DeploymentTestProject.csproj create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/EmptyDataFile.xml create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/Properties/AssemblyInfo1.cs create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/TestCaseDeploymentFile.xml create mode 100644 test/E2ETests/TestAssets/DeploymentTestProject/UnitTest1.cs diff --git a/TestFx.sln b/TestFx.sln index 1206914631..a280130b3d 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -170,7 +170,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParallelClassesTestProject" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataSourceTestProject", "test\E2ETests\TestAssets\DataSourceTestProject\DataSourceTestProject.csproj", "{5A4967CD-B527-4D43-81C2-4CA90EE10222}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoNotParallelizeTestProject", "test\E2ETests\TestAssets\DoNotParallelizeTestProject\DoNotParallelizeTestProject.csproj", "{8080DE48-CFD9-4F33-908A-8B71108CA223}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DoNotParallelizeTestProject", "test\E2ETests\TestAssets\DoNotParallelizeTestProject\DoNotParallelizeTestProject.csproj", "{8080DE48-CFD9-4F33-908A-8B71108CA223}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeploymentTestProject", "test\E2ETests\TestAssets\DeploymentTestProject\DeploymentTestProject.csproj", "{3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -961,6 +963,30 @@ Global {8080DE48-CFD9-4F33-908A-8B71108CA223}.Release|x64.Build.0 = Release|Any CPU {8080DE48-CFD9-4F33-908A-8B71108CA223}.Release|x86.ActiveCfg = Release|Any CPU {8080DE48-CFD9-4F33-908A-8B71108CA223}.Release|x86.Build.0 = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|ARM.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|x64.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|x64.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|x86.ActiveCfg = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Debug|x86.Build.0 = Debug|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|Any CPU.Build.0 = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|ARM.ActiveCfg = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|ARM.Build.0 = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|x64.ActiveCfg = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|x64.Build.0 = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|x86.ActiveCfg = Release|Any CPU + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1017,6 +1043,7 @@ Global {CD0CA7CD-CED3-45FF-9F36-B1C8DF7A9220} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {5A4967CD-B527-4D43-81C2-4CA90EE10222} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {8080DE48-CFD9-4F33-908A-8B71108CA223} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} + {3FCE3987-7C8C-4E12-B54A-C6EC2D9FC7A6} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} diff --git a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj index a455be386a..e8857c293a 100644 --- a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj +++ b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj @@ -59,7 +59,9 @@ + + @@ -77,7 +79,7 @@ - + @@ -93,6 +95,7 @@ + diff --git a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestDeployment.cs b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestDeployment.cs index 0dbcbe3892..85aa229103 100644 --- a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestDeployment.cs +++ b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestDeployment.cs @@ -140,8 +140,6 @@ public bool Deploy(IEnumerable tests, IRunContext runContext, IFramewo return false; } - var isDeploymentDone = false; - using (new SuspendCodeCoverage()) { // Group the tests by source @@ -153,7 +151,7 @@ group test by test.Source into testGroup foreach (var group in testsBySource) { // do the deployment - isDeploymentDone = this.deploymentUtility.Deploy(@group.Tests, @group.Source, runContext, frameworkHandle, ref runDirectories) || isDeploymentDone; + this.deploymentUtility.Deploy(@group.Tests, @group.Source, runContext, frameworkHandle, RunDirectories); } // Update the runDirectories diff --git a/src/Adapter/PlatformServices.Desktop/Services/DesktopSettingsProvider.cs b/src/Adapter/PlatformServices.Desktop/Services/MSTestAdapterSettings.cs similarity index 85% rename from src/Adapter/PlatformServices.Desktop/Services/DesktopSettingsProvider.cs rename to src/Adapter/PlatformServices.Desktop/Services/MSTestAdapterSettings.cs index a492c45993..77977c7a36 100644 --- a/src/Adapter/PlatformServices.Desktop/Services/DesktopSettingsProvider.cs +++ b/src/Adapter/PlatformServices.Desktop/Services/MSTestAdapterSettings.cs @@ -1,81 +1,17 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// 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.MSTestAdapter.PlatformServices { using System; using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Xml; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; - using ISettingsProvider = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ISettingsProvider; - -#pragma warning disable SA1649 // File name must match first type name - - /// - /// Class to read settings from the runsettings xml for the desktop. - /// - public class MSTestSettingsProvider : ISettingsProvider -#pragma warning restore SA1649 // File name must match first type name - { - /// - /// Member variable for Adapter settings - /// - private static MSTestAdapterSettings settings; - - /// - /// Gets settings provided to the adapter. - /// - public static MSTestAdapterSettings Settings - { - get - { - if (settings == null) - { - settings = new MSTestAdapterSettings(); - } - - return settings; - } - } - - /// - /// Load the settings from the reader. - /// - /// Reader to load the settings from. - public void Load(XmlReader reader) - { - ValidateArg.NotNull(reader, "reader"); - - settings = MSTestAdapterSettings.ToSettings(reader); - } - - public IDictionary GetProperties(string source) - { - return TestDeployment.GetDeploymentInformation(source); - } - - /// - /// Reset the settings to its default. - /// Used for testing purposes. - /// - internal static void Reset() - { - settings = null; - } - } - - /// - /// Adapter Settings for the run - /// -#pragma warning disable SA1402 public class MSTestAdapterSettings -#pragma warning restore SA1402 { /// /// Initializes a new instance of the class. @@ -84,6 +20,7 @@ public MSTestAdapterSettings() { this.DeleteDeploymentDirectoryAfterTestRunIsComplete = true; this.DeploymentEnabled = true; + this.DeployTestSourceDependencies = true; this.SearchDirectories = new List(); } @@ -97,6 +34,11 @@ public MSTestAdapterSettings() /// public bool DeleteDeploymentDirectoryAfterTestRunIsComplete { get; private set; } + /// + /// Gets a value indicating whether the test source references are to deployed + /// + public bool DeployTestSourceDependencies { get; private set; } + /// /// Gets list of paths recursive or non recursive paths. /// @@ -107,7 +49,6 @@ public MSTestAdapterSettings() /// /// Reader to load the settings from. /// An instance of the class - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Reviewed. Suppression is OK here.")] public static MSTestAdapterSettings ToSettings(XmlReader reader) { ValidateArg.NotNull(reader, "reader"); @@ -116,6 +57,7 @@ public static MSTestAdapterSettings ToSettings(XmlReader reader) // // // true + // true // true // // @@ -151,6 +93,16 @@ public static MSTestAdapterSettings ToSettings(XmlReader reader) break; } + case "DEPLOYTESTSOURCEDEPENDENCIES": + { + if (bool.TryParse(reader.ReadInnerXml(), out result)) + { + settings.DeployTestSourceDependencies = result; + } + + break; + } + case "DELETEDEPLOYMENTDIRECTORYAFTERTESTRUNISCOMPLETE": { if (bool.TryParse(reader.ReadInnerXml(), out result)) diff --git a/src/Adapter/PlatformServices.Desktop/Services/MSTestSettingsProvider.cs b/src/Adapter/PlatformServices.Desktop/Services/MSTestSettingsProvider.cs new file mode 100644 index 0000000000..e35849e700 --- /dev/null +++ b/src/Adapter/PlatformServices.Desktop/Services/MSTestSettingsProvider.cs @@ -0,0 +1,61 @@ +// 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.MSTestAdapter.PlatformServices +{ + using System.Collections.Generic; + using System.Xml; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + + using ISettingsProvider = Interface.ISettingsProvider; + + /// + /// Class to read settings from the runsettings xml for the desktop. + /// + public class MSTestSettingsProvider : ISettingsProvider + { + /// + /// Member variable for Adapter settings + /// + private static MSTestAdapterSettings settings; + + /// + /// Gets settings provided to the adapter. + /// + public static MSTestAdapterSettings Settings + { + get + { + if (settings == null) + { + settings = new MSTestAdapterSettings(); + } + + return settings; + } + } + + /// + /// Reset the settings to its default. + /// + public static void Reset() + { + settings = null; + } + + /// + /// Load the settings from the reader. + /// + /// Reader to load the settings from. + public void Load(XmlReader reader) + { + ValidateArg.NotNull(reader, "reader"); + settings = MSTestAdapterSettings.ToSettings(reader); + } + + public IDictionary GetProperties(string source) + { + return TestDeployment.GetDeploymentInformation(source); + } + } +} diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentItemUtility.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentItemUtility.cs index d175586d18..bc6fed91da 100644 --- a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentItemUtility.cs +++ b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentItemUtility.cs @@ -7,6 +7,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Uti using System.Collections.Generic; using System.Diagnostics; using System.Globalization; + using System.IO; using System.Linq; using System.Reflection; @@ -91,14 +92,14 @@ internal bool IsValidDeploymentItem(string sourcePath, string relativeOutputDire return false; } - if (sourcePath.IndexOfAny(System.IO.Path.GetInvalidPathChars()) != -1 || - relativeOutputDirectory.IndexOfAny(System.IO.Path.GetInvalidPathChars()) != -1) + if (sourcePath.IndexOfAny(Path.GetInvalidPathChars()) != -1 || + relativeOutputDirectory.IndexOfAny(Path.GetInvalidPathChars()) != -1) { warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentItemContainsInvalidCharacters, sourcePath, relativeOutputDirectory); return false; } - if (System.IO.Path.IsPathRooted(relativeOutputDirectory)) + if (Path.IsPathRooted(relativeOutputDirectory)) { warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentItemOutputDirectoryMustBeRelative, relativeOutputDirectory); return false; @@ -123,18 +124,15 @@ internal bool HasDeploymentItems(TestCase testCase) internal IList GetDeploymentItems(IEnumerable tests) { List allDeploymentItems = new List(); - foreach (var test in tests) { KeyValuePair[] items = this.GetDeploymentItems(test); - if (items == null || items.Length == 0) { continue; } IList deploymentItemsToBeAdded = this.FromKeyValuePairs(items); - foreach (var deploymentItemToBeAdded in deploymentItemsToBeAdded) { this.AddDeploymentItem(allDeploymentItems, deploymentItemToBeAdded); diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs new file mode 100644 index 0000000000..cf67e117e2 --- /dev/null +++ b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs @@ -0,0 +1,31 @@ +// 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.MSTestAdapter.PlatformServices +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; + + internal class DeploymentResult + { + public DeploymentResult(bool success, TestRunDirectories directories) + { + this.Success = success; + this.RunDirectories = directories; + } + + /// + /// Gets a value indicating whether the deployment was successful + /// + public bool Success { get; private set; } + + /// + /// Gets a value for the RunDirectories created for the deployment + /// + public TestRunDirectories RunDirectories { get; private set; } + } +} diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs index 277b16cacc..db87d17c4a 100644 --- a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs +++ b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs @@ -32,28 +32,24 @@ internal class DeploymentUtility private FileUtility fileUtility; private AssemblyUtility assemblyUtility; - internal DeploymentUtility() + public DeploymentUtility() : this(new DeploymentItemUtility(new ReflectionUtility()), new AssemblyUtility(), new FileUtility()) { } - internal DeploymentUtility( - DeploymentItemUtility deploymentItemUtility, - AssemblyUtility assemblyUtility, - FileUtility fileUtility) + public DeploymentUtility(DeploymentItemUtility deploymentItemUtility, AssemblyUtility assemblyUtility, FileUtility fileUtility) { this.deploymentItemUtility = deploymentItemUtility; this.assemblyUtility = assemblyUtility; this.fileUtility = fileUtility; } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "4#", Justification = "Used internally.")] - internal bool Deploy(IEnumerable tests, string source, IRunContext runContext, ITestExecutionRecorder testExecutionRecorder, ref TestRunDirectories testRunDirectories) + public bool Deploy(IEnumerable tests, string source, IRunContext runContext, ITestExecutionRecorder testExecutionRecorder, TestRunDirectories runDirectories) { IList deploymentItems = this.deploymentItemUtility.GetDeploymentItems(tests); // we just deploy source if there are no deployment items for current source but there are deployment items for other sources - return this.Deploy(source, runContext, testExecutionRecorder, deploymentItems, ref testRunDirectories); + return this.Deploy(source, runContext, testExecutionRecorder, deploymentItems, runDirectories); } /// @@ -61,10 +57,10 @@ internal bool Deploy(IEnumerable tests, string source, IRunContext run /// /// The run context. /// TestRunDirectories instance. - internal TestRunDirectories CreateDeploymentDirectories(IRunContext runContext) + public TestRunDirectories CreateDeploymentDirectories(IRunContext runContext) { - var tempDirectory = this.GetTestResultsDirectory(runContext); - var rootDeploymentDirectory = this.GetRootDeploymentDirectory(tempDirectory); + var resultsDirectory = this.GetTestResultsDirectory(runContext); + var rootDeploymentDirectory = this.GetRootDeploymentDirectory(resultsDirectory); var result = new TestRunDirectories(rootDeploymentDirectory); var inDirectory = result.InDirectory; @@ -121,13 +117,12 @@ private static void LogWarnings(ITestExecutionRecorder testExecutionRecorder, IE } } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "5#", Justification = "Used internally.")] - private bool Deploy(string source, IRunContext runContext, ITestExecutionRecorder testExecutionRecorder, IList deploymentItems, ref TestRunDirectories testRunDirectories) + private bool Deploy(string source, IRunContext runContext, ITestExecutionRecorder testExecutionRecorder, IList deploymentItems, TestRunDirectories runDirectories) { + ValidateArg.NotNull(runDirectories, "runDirectories"); if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("MSTestExecutor: Found that deployment items for source {0} are: ", source); - foreach (var item in deploymentItems) { EqtTrace.Info("MSTestExecutor: SourcePath: - {0}", item.SourcePath); @@ -135,18 +130,11 @@ private bool Deploy(string source, IRunContext runContext, ITestExecutionRecorde } // Do the deployment. - IEnumerable warnings; - - var runDirectories = testRunDirectories ?? this.CreateDeploymentDirectories(runContext); - - ValidateArg.NotNull(runDirectories, "runDirectories"); EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "MSTestExecutor: Using deployment directory {0} for source {1}.", runDirectories.OutDirectory, source); - - this.Deploy(new List(deploymentItems), source, runDirectories.OutDirectory, out warnings); + var warnings = this.Deploy(new List(deploymentItems), source, runDirectories.OutDirectory, this.GetTestResultsDirectory(runContext)); // Log warnings LogWarnings(testExecutionRecorder, warnings); - return deploymentItems != null && deploymentItems.Count > 0; } @@ -156,27 +144,37 @@ private bool Deploy(string source, IRunContext runContext, ITestExecutionRecorde /// The deployment item. /// The test source. /// The deployment directory. - /// Warnings. + /// Root results directory + /// Returns a list of deployment warnings [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - private void Deploy(IList deploymentItems, string testSource, string deploymentDirectory, out IEnumerable deploymentWarnings) + private IEnumerable Deploy(IList deploymentItems, string testSource, string deploymentDirectory, string resultsDirectory) { - Debug.Assert(!string.IsNullOrEmpty(deploymentDirectory), "Deployment directory is null/empty"); - Debug.Assert(this.fileUtility.DoesDirectoryExist(deploymentDirectory), "Deployment directory " + deploymentDirectory + " does not exist"); - Debug.Assert(!string.IsNullOrEmpty(testSource), "TestSource directory is null/empty"); - Debug.Assert(this.fileUtility.DoesFileExist(testSource), "TestSource " + testSource + " does not exist."); + Validate.IsFalse(string.IsNullOrWhiteSpace(deploymentDirectory), "Deployment directory is null or empty"); + Validate.IsTrue(this.fileUtility.DoesDirectoryExist(deploymentDirectory), $"Deployment directory {deploymentDirectory} does not exist"); + Validate.IsFalse(string.IsNullOrWhiteSpace(testSource), "TestSource directory is null/empty"); + Validate.IsTrue(this.fileUtility.DoesFileExist(testSource), $"TestSource {testSource} does not exist."); testSource = Path.GetFullPath(testSource); - var warnings = new List(); - // Get the referenced assemblies. - this.ProcessNewStorage(testSource, deploymentItems, warnings); + if (MSTestSettingsProvider.Settings.DeployTestSourceDependencies) + { + EqtTrace.Info("Adding the references and satellite assemblies to the deploymentitems list"); + + // Get the referenced assemblies. + this.ProcessNewStorage(testSource, deploymentItems, warnings); - // Get the satellite assemblies - var satelliteItems = this.GetSatellites(deploymentItems, testSource, warnings); - foreach (var satelliteItem in satelliteItems) + // Get the satellite assemblies + var satelliteItems = this.GetSatellites(deploymentItems, testSource, warnings); + foreach (var satelliteItem in satelliteItems) + { + this.deploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem); + } + } + else if (File.Exists(testSource)) { - this.deploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem); + EqtTrace.Info("Adding the test source directory to the deploymentitems list"); + this.deploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(Path.GetDirectoryName(testSource))); } // Maps relative to Out dir destination -> source and used to determine if there are conflicted items. @@ -185,7 +183,7 @@ private void Deploy(IList deploymentItems, string testSource, st // Copy the deployment items. (As deployment item can correspond to directories as well, so each deployment item may map to n files) foreach (var deploymentItem in deploymentItems) { - Debug.Assert(deploymentItem != null, "deploymentItem should not be null."); + ValidateArg.NotNull(deploymentItem, "deploymentItem should not be null."); // Validate the output directory. if (!this.IsOutputDirectoryValid(deploymentItem, deploymentDirectory, warnings)) @@ -194,8 +192,7 @@ private void Deploy(IList deploymentItems, string testSource, st } // Get the files corresponding to this deployment item - bool itemIsDirectory; - var deploymentItemFiles = this.GetFullPathToFilesCorrespondingToDeploymentItem(deploymentItem, testSource, warnings, out itemIsDirectory); + var deploymentItemFiles = this.GetFullPathToFilesCorrespondingToDeploymentItem(deploymentItem, testSource, resultsDirectory, warnings, out bool itemIsDirectory); if (deploymentItemFiles == null) { continue; @@ -213,8 +210,7 @@ private void Deploy(IList deploymentItems, string testSource, st filesToDeploy.Add(deploymentItemFile); // Find dependencies of test deployment items and deploy them at the same time as master file. - if (deploymentItem.OriginType == DeploymentItemOriginType.PerTestDeployment - && + if (deploymentItem.OriginType == DeploymentItemOriginType.PerTestDeployment && this.assemblyUtility.IsAssemblyExtension(Path.GetExtension(deploymentItemFile))) { this.AddDependenciesOfDeploymentItem(deploymentItemFile, filesToDeploy, warnings); @@ -264,8 +260,7 @@ private void Deploy(IList deploymentItems, string testSource, st destToSource.Add(relativeDestination, fileToDeploy); // Now, finally we can copy the file... - string warning; - destination = this.fileUtility.CopyFileOverwrite(fileToDeploy, destination, out warning); + destination = this.fileUtility.CopyFileOverwrite(fileToDeploy, destination, out string warning); if (!string.IsNullOrEmpty(warning)) { warnings.Add(warning); @@ -298,7 +293,7 @@ private void Deploy(IList deploymentItems, string testSource, st } // foreach itemFile. } - deploymentWarnings = warnings; + return warnings; } private void AddDependenciesOfDeploymentItem(string deploymentItemFile, IList filesToDeploy, IList warnings) @@ -362,54 +357,49 @@ private void AddDependencies(string testSource, string configFile, IList /// Deployment Item. /// The test source. + /// Results directory which should be skipped for deployment /// Warnings. /// Is this a directory. /// Paths to items to deploy. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - private string[] GetFullPathToFilesCorrespondingToDeploymentItem(DeploymentItem deploymentItem, string testSource, IList warnings, out bool isDirectory) + private string[] GetFullPathToFilesCorrespondingToDeploymentItem(DeploymentItem deploymentItem, string testSource, string resultsDirectory, IList warnings, out bool isDirectory) { Debug.Assert(deploymentItem != null, "deploymentItem should not be null."); Debug.Assert(!string.IsNullOrEmpty(testSource), "testsource should not be null or empty."); try { - string directory; - isDirectory = this.IsDeploymentItemSourceADirectory(deploymentItem, testSource, out directory); - + isDirectory = this.IsDeploymentItemSourceADirectory(deploymentItem, testSource, out string directory); if (isDirectory) { - return this.fileUtility.AddFilesFromDirectory(directory, false).ToArray(); + return this.fileUtility.AddFilesFromDirectory( + directory, (deployDirectory) => string.Equals(deployDirectory, resultsDirectory, StringComparison.OrdinalIgnoreCase), false).ToArray(); } - string fileName; - if (!this.IsDeploymentItemSourceAFile(deploymentItem.SourcePath, testSource, out fileName)) + if (this.IsDeploymentItemSourceAFile(deploymentItem.SourcePath, testSource, out string fileName)) { - // If file/directory is not found, then try removing the prefix and see if it is present. - string fileOrDirNameOnly = - Path.GetFileName( - deploymentItem.SourcePath.TrimEnd( - new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar })); - if (!this.IsDeploymentItemSourceAFile(fileOrDirNameOnly, testSource, out fileName)) - { - string message = string.Format(CultureInfo.CurrentCulture, Resource.CannotFindFile, fileName); - throw new FileNotFoundException(message, fileName); - } + return new[] { fileName }; } - return new[] { fileName }; + // If file/directory is not found, then try removing the prefix and see if it is present. + string fileOrDirNameOnly = Path.GetFileName(deploymentItem.SourcePath.TrimEnd( + new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar })); + if (this.IsDeploymentItemSourceAFile(fileOrDirNameOnly, testSource, out fileName)) + { + return new[] { fileName }; + } + + string message = string.Format(CultureInfo.CurrentCulture, Resource.CannotFindFile, fileName); + throw new FileNotFoundException(message, fileName); } catch (Exception e) { warnings.Add(string.Format( - CultureInfo.CurrentCulture, - Resource.DeploymentErrorFailedToGetFileForDeploymentItem, - deploymentItem, - e.GetType(), - e.Message)); - - isDirectory = false; - return null; + CultureInfo.CurrentCulture, Resource.DeploymentErrorFailedToGetFileForDeploymentItem, deploymentItem, e.GetType(), e.Message)); } + + isDirectory = false; + return null; } private bool IsDeploymentItemSourceAFile(string deploymentItemSourcePath, string testSource, out string file) @@ -631,15 +621,15 @@ private void ProcessNewStorage(string testSource, IList deployme /// The test results directory. private string GetTestResultsDirectory(IRunContext runContext) { - var tempDirectory = (!string.IsNullOrEmpty(runContext?.TestRunDirectory)) ? + var resultsDirectory = (!string.IsNullOrEmpty(runContext?.TestRunDirectory)) ? runContext.TestRunDirectory : null; - if (string.IsNullOrEmpty(tempDirectory)) + if (string.IsNullOrEmpty(resultsDirectory)) { - tempDirectory = Path.GetFullPath(Path.Combine(Path.GetTempPath(), TestRunDirectories.DefaultDeploymentRootDirectory)); + resultsDirectory = Path.GetFullPath(Path.Combine(Path.GetTempPath(), TestRunDirectories.DefaultDeploymentRootDirectory)); } - return tempDirectory; + return resultsDirectory; } /// diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/FileUtility.cs b/src/Adapter/PlatformServices.Desktop/Utilities/FileUtility.cs index e078eff134..9a4826293e 100644 --- a/src/Adapter/PlatformServices.Desktop/Utilities/FileUtility.cs +++ b/src/Adapter/PlatformServices.Desktop/Utilities/FileUtility.cs @@ -17,12 +17,12 @@ internal class FileUtility { private AssemblyUtility assemblyUtility; - internal FileUtility() + public FileUtility() { this.assemblyUtility = new AssemblyUtility(); } - internal virtual void CreateDirectoryIfNotExists(string directory) + public virtual void CreateDirectoryIfNotExists(string directory) { Debug.Assert(!string.IsNullOrEmpty(directory), "directory"); @@ -37,7 +37,7 @@ internal virtual void CreateDirectoryIfNotExists(string directory) /// /// The file Name. /// The fileName devoid of any invalid characters. - internal string ReplaceInvalidFileNameCharacters(string fileName) + public string ReplaceInvalidFileNameCharacters(string fileName) { Debug.Assert(!string.IsNullOrEmpty(fileName), "fileName"); @@ -52,9 +52,7 @@ internal string ReplaceInvalidFileNameCharacters(string fileName) /// The directory where to check. /// The original directory (that we would add [1],[2],.. in the end of if needed) name to check. /// A unique directory name. - internal virtual string GetNextIterationDirectoryName( - string parentDirectoryName, - string originalDirectoryName) + public virtual string GetNextIterationDirectoryName(string parentDirectoryName, string originalDirectoryName) { Debug.Assert(!string.IsNullOrEmpty(parentDirectoryName), "parentDirectoryName"); Debug.Assert(!string.IsNullOrEmpty(originalDirectoryName), "originalDirectoryName"); @@ -99,7 +97,7 @@ internal virtual string GetNextIterationDirectoryName( /// throw on error when specified to abort the run on error. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - internal virtual string CopyFileOverwrite(string source, string destination, out string warning) + public virtual string CopyFileOverwrite(string source, string destination, out string warning) { Debug.Assert(!string.IsNullOrEmpty(source), "source should not be null."); Debug.Assert(!string.IsNullOrEmpty(destination), "destination should not be null."); @@ -122,7 +120,6 @@ internal virtual string CopyFileOverwrite(string source, string destination, out catch (Exception e) { warning = string.Format(CultureInfo.CurrentCulture, Resource.DeploymentErrorFailedToCopyWithOverwrite, source, destination, e.GetType(), e.GetExceptionMessage()); - return string.Empty; } } @@ -137,7 +134,7 @@ internal virtual string CopyFileOverwrite(string source, string destination, out /// Destination relative to the root of deployment dir. /// Original file of destinationFile, i.e. the file copied to deployment dir. /// destToSource map. - internal string FindAndDeployPdb(string destinationFile, string relativeDestination, string sourceFile, Dictionary destToSource) + public string FindAndDeployPdb(string destinationFile, string relativeDestination, string sourceFile, Dictionary destToSource) { Debug.Assert(!string.IsNullOrEmpty(destinationFile), "destination should not be null or empty."); Debug.Assert(!string.IsNullOrEmpty(relativeDestination), "relative destination path should not be null or empty."); @@ -161,8 +158,7 @@ internal string FindAndDeployPdb(string destinationFile, string relativeDestinat try { pdbDestination = Path.Combine(Path.GetDirectoryName(destinationFile), Path.GetFileName(pdbSource)); - relativePdbDestination = Path.Combine( - Path.GetDirectoryName(relativeDestination), Path.GetFileName(pdbDestination)); + relativePdbDestination = Path.Combine(Path.GetDirectoryName(relativeDestination), Path.GetFileName(pdbDestination)); } catch (ArgumentException ex) { @@ -196,7 +192,12 @@ internal string FindAndDeployPdb(string destinationFile, string relativeDestinat return null; } - internal virtual List AddFilesFromDirectory(string directoryPath, bool ignoreIOExceptions) + public virtual List AddFilesFromDirectory(string directoryPath, bool ignoreIOExceptions) + { + return this.AddFilesFromDirectory(directoryPath, null, ignoreIOExceptions); + } + + public virtual List AddFilesFromDirectory(string directoryPath, Func ignoreDirectory, bool ignoreIOExceptions) { var fileContents = new List(); @@ -215,7 +216,12 @@ internal virtual List AddFilesFromDirectory(string directoryPath, bool i foreach (var subDirectoryPath in this.GetDirectoriesInADirectory(directoryPath)) { - var subDirectoryContents = this.AddFilesFromDirectory(subDirectoryPath, true); + if (ignoreDirectory != null && ignoreDirectory(subDirectoryPath)) + { + continue; + } + + var subDirectoryContents = this.AddFilesFromDirectory(subDirectoryPath, ignoreDirectory, true); if (subDirectoryContents?.Count > 0) { fileContents.AddRange(subDirectoryContents); @@ -225,7 +231,7 @@ internal virtual List AddFilesFromDirectory(string directoryPath, bool i return fileContents; } - internal string TryConvertPathToRelative(string path, string rootDir) + public string TryConvertPathToRelative(string path, string rootDir) { Debug.Assert(!string.IsNullOrEmpty(path), "path should not be null or empty."); Debug.Assert(!string.IsNullOrEmpty(rootDir), "rootDir should not be null or empty."); @@ -244,10 +250,9 @@ internal string TryConvertPathToRelative(string path, string rootDir) /// /// The root directory to clear. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle all kinds of user exceptions and message appropriately.")] - internal virtual void DeleteDirectories(string filePath) + public virtual void DeleteDirectories(string filePath) { - Debug.Assert(filePath != null, "filePath"); - + Validate.IsFalse(string.IsNullOrWhiteSpace(filePath), "Invalid filePath provided"); try { var root = new DirectoryInfo(filePath); @@ -259,27 +264,27 @@ internal virtual void DeleteDirectories(string filePath) } } - internal virtual bool DoesDirectoryExist(string deploymentDirectory) + public virtual bool DoesDirectoryExist(string deploymentDirectory) { return Directory.Exists(deploymentDirectory); } - internal virtual bool DoesFileExist(string testSource) + public virtual bool DoesFileExist(string testSource) { return File.Exists(testSource); } - internal virtual void SetAttributes(string path, FileAttributes fileAttributes) + public virtual void SetAttributes(string path, FileAttributes fileAttributes) { File.SetAttributes(path, fileAttributes); } - internal virtual string[] GetFilesInADirectory(string directoryPath) + public virtual string[] GetFilesInADirectory(string directoryPath) { return Directory.GetFiles(directoryPath); } - internal virtual string[] GetDirectoriesInADirectory(string directoryPath) + public virtual string[] GetDirectoriesInADirectory(string directoryPath) { return Directory.GetDirectories(directoryPath); } diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/IAppDomain.cs b/src/Adapter/PlatformServices.Desktop/Utilities/IAppDomain.cs index 58680a4377..b03c07cb0b 100644 --- a/src/Adapter/PlatformServices.Desktop/Utilities/IAppDomain.cs +++ b/src/Adapter/PlatformServices.Desktop/Utilities/IAppDomain.cs @@ -24,9 +24,6 @@ internal interface IAppDomain /// Evidence that establishes the identity of the code that runs in the application domain. Pass null to use the evidence of the current application domain. /// An object that contains application domain initialization information. /// The newly created application domain. - AppDomain CreateDomain( - string friendlyName, - Evidence securityInfo, - AppDomainSetup info); + AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup info); } } diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/Validate.cs b/src/Adapter/PlatformServices.Desktop/Utilities/Validate.cs new file mode 100644 index 0000000000..c7602e7f1f --- /dev/null +++ b/src/Adapter/PlatformServices.Desktop/Utilities/Validate.cs @@ -0,0 +1,40 @@ +// 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.MSTestAdapter.PlatformServices.Utilities +{ + using System; + + internal class Validate + { + /// + /// Throws an exception if the condition is not false + /// + /// Condition to evaluate + /// Error Message to be used in the exception thrown + public static void IsFalse(bool condition, string errorMessage) + { + if (!condition) + { + return; + } + + throw new InvalidOperationException(errorMessage); + } + + /// + /// Throws an exception if the condition is not true + /// + /// Condition to evaluate + /// Error Message to be used in the exception thrown + public static void IsTrue(bool condition, string errorMessage) + { + if (condition) + { + return; + } + + throw new InvalidOperationException(errorMessage); + } + } +} \ No newline at end of file diff --git a/test/E2ETests/Smoke.E2E.Tests/DeploymentTests.cs b/test/E2ETests/Smoke.E2E.Tests/DeploymentTests.cs new file mode 100644 index 0000000000..d1270f6c34 --- /dev/null +++ b/test/E2ETests/Smoke.E2E.Tests/DeploymentTests.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MSTestAdapter.Smoke.E2ETests +{ + using Microsoft.MSTestV2.CLIAutomation; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class DeploymentTests : CLITestBase + { + private const string TestAssembly = "DeploymentTestProject.dll"; + private const string RunSetting = + @" + + false + + "; + + [TestMethod] + public void ValidateTestSourceDependencyDeployment() + { + this.InvokeVsTestForExecution(new string[] { TestAssembly }); + this.ValidatePassedTestsContain("DeploymentTestProject.UnitTest1.FailIfFilePresent", "DeploymentTestProject.UnitTest1.PassIfDeclaredFilesPresent"); + this.ValidateFailedTestsContain("DeploymentTestProject.dll", "DeploymentTestProject.UnitTest1.PassIfFilePresent"); + } + + [TestMethod] + public void ValidateTestSourceLocationDeployment() + { + this.InvokeVsTestForExecution(new string[] { TestAssembly }, RunSetting); + this.ValidatePassedTestsContain("DeploymentTestProject.UnitTest1.PassIfFilePresent", "DeploymentTestProject.UnitTest1.PassIfDeclaredFilesPresent"); + this.ValidateFailedTestsContain("DeploymentTestProject.dll", "DeploymentTestProject.UnitTest1.FailIfFilePresent"); + } + } +} diff --git a/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj b/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj index 6005ad1c50..4996151629 100644 --- a/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj +++ b/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj @@ -40,6 +40,7 @@ + diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentFile.xml b/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentFile.xml new file mode 100644 index 0000000000..7dde50ed73 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentFile.xml @@ -0,0 +1 @@ + diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentTestProject.csproj b/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentTestProject.csproj new file mode 100644 index 0000000000..1d8021bb51 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/DeploymentTestProject.csproj @@ -0,0 +1,36 @@ + + + + ..\..\..\..\ + + + + net452 + false + false + $(TestFxRoot)artifacts\TestAssets\ + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/EmptyDataFile.xml b/test/E2ETests/TestAssets/DeploymentTestProject/EmptyDataFile.xml new file mode 100644 index 0000000000..7dde50ed73 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/EmptyDataFile.xml @@ -0,0 +1 @@ + diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/Properties/AssemblyInfo1.cs b/test/E2ETests/TestAssets/DeploymentTestProject/Properties/AssemblyInfo1.cs new file mode 100644 index 0000000000..37b7284044 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/Properties/AssemblyInfo1.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +//[assembly: AssemblyTitle("DeploymentTestProject.Properties")] +//[assembly: AssemblyDescription("")] +//[assembly: AssemblyConfiguration("")] +//[assembly: AssemblyCompany("")] +//[assembly: AssemblyProduct("DeploymentTestProject.Properties")] +//[assembly: AssemblyCopyright("Copyright © 2018")] +//[assembly: AssemblyTrademark("")] +//[assembly: AssemblyCulture("")] + +//// Setting ComVisible to false makes the types in this assembly not visible +//// to COM components. If you need to access a type in this assembly from +//// COM, set the ComVisible attribute to true on that type. +//[assembly: ComVisible(false)] + +//// The following GUID is for the ID of the typelib if this project is exposed to COM +//[assembly: Guid("f03a1d29-b9bb-4b29-b2fc-7993f64c895b")] + +//// Version information for an assembly consists of the following four values: +//// +//// Major Version +//// Minor Version +//// Build Number +//// Revision +//// +//[assembly: AssemblyVersion("1.0.0.0")] +//[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/TestCaseDeploymentFile.xml b/test/E2ETests/TestAssets/DeploymentTestProject/TestCaseDeploymentFile.xml new file mode 100644 index 0000000000..7dde50ed73 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/TestCaseDeploymentFile.xml @@ -0,0 +1 @@ + diff --git a/test/E2ETests/TestAssets/DeploymentTestProject/UnitTest1.cs b/test/E2ETests/TestAssets/DeploymentTestProject/UnitTest1.cs new file mode 100644 index 0000000000..8109354496 --- /dev/null +++ b/test/E2ETests/TestAssets/DeploymentTestProject/UnitTest1.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DeploymentTestProject +{ + [DeploymentItem("DeploymentFile.xml")] + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void PassIfFilePresent() + { + Assert.IsTrue(File.Exists("EmptyDataFile.xml")); + } + + [TestMethod] + public void FailIfFilePresent() + { + Assert.IsFalse(File.Exists("EmptyDataFile.xml")); + } + + [DeploymentItem("TestCaseDeploymentFile.xml")] + [TestMethod] + public void PassIfDeclaredFilesPresent() + { + Assert.IsTrue(File.Exists("DeploymentFile.xml")); + Assert.IsTrue(File.Exists("TestCaseDeploymentFile.xml")); + } + } +} diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/MSTestAdapterSettingsTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/MSTestAdapterSettingsTests.cs index a620b76ce7..e7cd44a483 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/MSTestAdapterSettingsTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/MSTestAdapterSettingsTests.cs @@ -255,6 +255,51 @@ public void DeploymentEnabledShouldBeConsumedFromRunSettingsWhenSpecified() } #endregion + + #region DeployTestSourceDependencies tests + + [TestMethod] + public void DeployTestSourceDependenciesIsEnabledByDefault() + { + string runSettingxml = + @" + "; + StringReader stringReader = new StringReader(runSettingxml); + XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); + reader.Read(); + MSTestAdapterSettings adapterSettings = MSTestAdapterSettings.ToSettings(reader); + Assert.AreEqual(true, adapterSettings.DeployTestSourceDependencies); + } + + [TestMethod] + public void DeployTestSourceDependenciesWhenFalse() + { + string runSettingxml = + @" + False + "; + StringReader stringReader = new StringReader(runSettingxml); + XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); + reader.Read(); + MSTestAdapterSettings adapterSettings = MSTestAdapterSettings.ToSettings(reader); + Assert.AreEqual(false, adapterSettings.DeployTestSourceDependencies); + } + + [TestMethod] + public void DeployTestSourceDependenciesWhenTrue() + { + string runSettingxml = + @" + True + "; + StringReader stringReader = new StringReader(runSettingxml); + XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); + reader.Read(); + MSTestAdapterSettings adapterSettings = MSTestAdapterSettings.ToSettings(reader); + Assert.AreEqual(true, adapterSettings.DeployTestSourceDependencies); + } + + #endregion } public class TestableMSTestAdapterSettings : MSTestAdapterSettings diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs index 9e8a168e3a..5e3ec779f4 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs @@ -10,14 +10,14 @@ namespace MSTestAdapter.PlatformServices.Desktop.UnitTests.Utilities using System.Collections.Generic; using System.IO; using System.Reflection; - + using System.Xml; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; - + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Moq; using Assert = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.Assert; @@ -143,7 +143,7 @@ public void DeployShouldReturnFalseWhenNoDeploymentItemsOnTestCase() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); } [TestMethod] @@ -169,7 +169,7 @@ public void DeployShouldDeploySourceAndItsConfigFile() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. string warning; @@ -216,7 +216,7 @@ public void DeployShouldDeployDependentFiles() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. string warning; @@ -255,7 +255,7 @@ public void DeployShouldDeploySatelliteAssemblies() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. string warning; @@ -296,7 +296,7 @@ public void DeployShouldNotDeployIfOutputDirectoryIsInvalid() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. string warning; @@ -339,6 +339,8 @@ public void DeployShouldDeployContentsOfADirectoryIfSpecified() .Returns(new string[] { }); this.mockFileUtility.Setup( fu => fu.AddFilesFromDirectory(DefaultDeploymentItemPath, It.IsAny())).Returns(directoryContentFiles); + this.mockFileUtility.Setup( + fu => fu.AddFilesFromDirectory(DefaultDeploymentItemPath, It.IsAny>(), It.IsAny())).Returns(directoryContentFiles); this.mockAssemblyUtility.Setup( au => au.GetSatelliteAssemblies(It.IsAny())) .Returns(new List { }); @@ -350,7 +352,7 @@ public void DeployShouldDeployContentsOfADirectoryIfSpecified() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. string warning; @@ -364,6 +366,43 @@ public void DeployShouldDeployContentsOfADirectoryIfSpecified() Times.Once); } + [TestMethod] + public void FileUtilityTests() + { + } + + [TestMethod] + public void DeployTestRunSourceOptionShouldDeployAllFilesAndDirectoriesPresentInSourceDirectory() + { + // Setup + TestRunDirectories testRunDirectories; + var testCase = this.GetTestCaseAndTestRunDirectories(DefaultDeploymentItemPath, DefaultDeploymentItemOutputDirectory, out testRunDirectories); + string runSettingxml = @"False"; + StringReader stringReader = new StringReader(runSettingxml); + XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); + MSTestSettingsProvider mstestSettingsProvider = new MSTestSettingsProvider(); + mstestSettingsProvider.Load(reader); + + // Setup mocks. + this.mockFileUtility.Setup(fu => fu.DoesDirectoryExist(It.Is(s => !s.EndsWith(".dll")))).Returns(true); + this.mockFileUtility.Setup(fu => fu.DoesFileExist(It.IsAny())).Returns(true); + List directoryContentFiles = new List { "dummy1.txt", "dummy2.txt" }; + this.mockFileUtility.Setup(fu => fu.AddFilesFromDirectory(DefaultDeploymentItemPath, It.IsAny>(), It.IsAny())).Returns(directoryContentFiles); + this.mockAssemblyUtility.Setup(au => au.GetFullPathToDependentAssemblies(It.IsAny(), It.IsAny(), out this.warnings)).Returns(new string[] { }); + this.mockAssemblyUtility.Setup(au => au.GetSatelliteAssemblies(It.IsAny())).Returns(new List { }); + + // Act + Assert.IsTrue( + this.deploymentUtility.Deploy( + new List { testCase }, + testCase.Source, + this.mockRunContext.Object, + this.mocktestExecutionRecorder.Object, + testRunDirectories)); + + // Validate + } + [TestMethod] public void DeployShouldDeployPdbWithSourceIfPdbFileIsPresentInSourceDirectory() { @@ -395,7 +434,7 @@ public void DeployShouldDeployPdbWithSourceIfPdbFileIsPresentInSourceDirectory() testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. var sourceFile = Assembly.GetExecutingAssembly().GetName().Name + ".dll"; @@ -452,7 +491,7 @@ public void DeployShouldNotDeployPdbFileOfAssemblyIfPdbFileIsNotPresentInAssembl testCase.Source, this.mockRunContext.Object, this.mocktestExecutionRecorder.Object, - ref testRunDirectories)); + testRunDirectories)); // Assert. this.mockFileUtility.Verify( diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/FileUtilityTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/FileUtilityTests.cs index feac8400c7..45efa6f766 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/FileUtilityTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/FileUtilityTests.cs @@ -6,6 +6,7 @@ namespace MSTestAdapter.PlatformServices.Desktop.UnitTests.Utilities extern alias FrameworkV1; using System; + using System.IO; using System.Linq; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using Moq; @@ -95,6 +96,87 @@ public void AddFilesInADirectoryShouldReturnAllFilesUnderSubFoldersEvenIfAFolder CollectionAssert.AreEqual(expectedFiles, files); } + [TestMethod] + public void AddFilesWithIgnoreDirectory() + { + // Setup + var allFiles = new string[] + { + "c:\\MainClock\\Results\\tickmain.trx", "c:\\MainClock\\Results\\Run1\\tock.tick.txt", + "c:\\MainClock\\tickmain.txt", "c:\\MainClock\\tock.tick.txt", + "c:\\MainClock\\Folder1\\tick.txt", "c:\\MainClock\\Folder1\\tock.tick.txt", + "c:\\MainClock\\Folder2\\backup\\Data.csv", + }; + + this.fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((directory) => + { + var directories = allFiles.Where(file => IsFileUnderDirectory(directory, file)).Select((file) => Path.GetDirectoryName(file)).Distinct(); + return directories.ToArray(); + }); + + this.fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns((directory) => + { + return allFiles.Where((file) => Path.GetDirectoryName(file).Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct().ToArray(); + }); + + // Act + var files = this.fileUtility.Object.AddFilesFromDirectory("C:\\MainClock", (directory) => directory.Contains("Results"), false); + + // Validate + foreach (var sourceFile in allFiles) + { + Console.WriteLine($"File to validate {sourceFile}"); + if (sourceFile.Contains("Results")) + { + Assert.IsFalse(files.Any((file) => file.Contains("Results")), $"{sourceFile} returned in the list from AddFilesFromDirectory"); + } + else + { + Assert.IsTrue(files.Any((file) => file.Equals(sourceFile, StringComparison.OrdinalIgnoreCase)), $"{sourceFile} not returned in the list from AddFilesFromDirectory"); + } + } + } + + [TestMethod] + public void AddFilesWithNoIgnoreDirectory() + { + // Setup + var allFiles = new string[] + { + "c:\\MainClock\\Results\\tickmain.trx", "c:\\MainClock\\Results\\Run1\\tock.tick.txt", + "c:\\MainClock\\tickmain.txt", "c:\\MainClock\\tock.tick.txt", + "c:\\MainClock\\Folder1\\tick.txt", "c:\\MainClock\\Folder1\\tock.tick.txt", + "c:\\MainClock\\Folder2\\backup\\Data.csv", + }; + + this.fileUtility.Setup(fu => fu.GetDirectoriesInADirectory(It.IsAny())).Returns((directory) => + { + var directories = allFiles.Where(file => IsFileUnderDirectory(directory, file)).Select((file) => Path.GetDirectoryName(file)).Distinct(); + return directories.ToArray(); + }); + + this.fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns((directory) => + { + return allFiles.Where((file) => Path.GetDirectoryName(file).Equals(directory, StringComparison.OrdinalIgnoreCase)).Distinct().ToArray(); + }); + + // Act + var files = this.fileUtility.Object.AddFilesFromDirectory("C:\\MainClock", false); + + // Validate + foreach (var sourceFile in allFiles) + { + Assert.IsTrue(files.Any((file) => file.Equals(sourceFile, StringComparison.OrdinalIgnoreCase)), $"{sourceFile} not returned in the list from AddFilesFromDirectory"); + } + } + + private static bool IsFileUnderDirectory(string directory, string fileName) + { + string fileDirectory = Path.GetDirectoryName(fileName); + return fileDirectory.StartsWith(directory, StringComparison.OrdinalIgnoreCase) && + !directory.Equals(fileDirectory, StringComparison.OrdinalIgnoreCase); + } + private void SetupMockFileAPIs(string[] files) { this.fileUtility.Setup(fu => fu.GetFilesInADirectory(It.IsAny())).Returns((string dp) => From b9f6f6557466bf674f9e340d9a233cc3142f33d0 Mon Sep 17 00:00:00 2001 From: "Shiva Shankar T (VSTT)" Date: Tue, 27 Mar 2018 13:38:17 +0530 Subject: [PATCH 2/4] Removed couple of redundant tests --- .../Utilities/DeploymentUtilityTests.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs index 5e3ec779f4..6e41edd409 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Utilities/DeploymentUtilityTests.cs @@ -366,43 +366,6 @@ public void DeployShouldDeployContentsOfADirectoryIfSpecified() Times.Once); } - [TestMethod] - public void FileUtilityTests() - { - } - - [TestMethod] - public void DeployTestRunSourceOptionShouldDeployAllFilesAndDirectoriesPresentInSourceDirectory() - { - // Setup - TestRunDirectories testRunDirectories; - var testCase = this.GetTestCaseAndTestRunDirectories(DefaultDeploymentItemPath, DefaultDeploymentItemOutputDirectory, out testRunDirectories); - string runSettingxml = @"False"; - StringReader stringReader = new StringReader(runSettingxml); - XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); - MSTestSettingsProvider mstestSettingsProvider = new MSTestSettingsProvider(); - mstestSettingsProvider.Load(reader); - - // Setup mocks. - this.mockFileUtility.Setup(fu => fu.DoesDirectoryExist(It.Is(s => !s.EndsWith(".dll")))).Returns(true); - this.mockFileUtility.Setup(fu => fu.DoesFileExist(It.IsAny())).Returns(true); - List directoryContentFiles = new List { "dummy1.txt", "dummy2.txt" }; - this.mockFileUtility.Setup(fu => fu.AddFilesFromDirectory(DefaultDeploymentItemPath, It.IsAny>(), It.IsAny())).Returns(directoryContentFiles); - this.mockAssemblyUtility.Setup(au => au.GetFullPathToDependentAssemblies(It.IsAny(), It.IsAny(), out this.warnings)).Returns(new string[] { }); - this.mockAssemblyUtility.Setup(au => au.GetSatelliteAssemblies(It.IsAny())).Returns(new List { }); - - // Act - Assert.IsTrue( - this.deploymentUtility.Deploy( - new List { testCase }, - testCase.Source, - this.mockRunContext.Object, - this.mocktestExecutionRecorder.Object, - testRunDirectories)); - - // Validate - } - [TestMethod] public void DeployShouldDeployPdbWithSourceIfPdbFileIsPresentInSourceDirectory() { From 10f5b681e73f0845f33d28f507cbf22e4d5b235e Mon Sep 17 00:00:00 2001 From: Shiva Shankar Date: Wed, 31 Oct 2018 16:37:01 +0530 Subject: [PATCH 3/4] Minor review comment updated. --- .../PlatformServices.Desktop/Utilities/DeploymentUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs index db87d17c4a..c5338cc829 100644 --- a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs +++ b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs @@ -171,7 +171,7 @@ private IEnumerable Deploy(IList deploymentItems, string this.deploymentItemUtility.AddDeploymentItem(deploymentItems, satelliteItem); } } - else if (File.Exists(testSource)) + else { EqtTrace.Info("Adding the test source directory to the deploymentitems list"); this.deploymentItemUtility.AddDeploymentItem(deploymentItems, new DeploymentItem(Path.GetDirectoryName(testSource))); From 8425a5548f501292bd96fe86ebc96512f8653fec Mon Sep 17 00:00:00 2001 From: Shiva Shankar Date: Wed, 31 Oct 2018 16:38:06 +0530 Subject: [PATCH 4/4] Removed and unused file --- .../PlatformServices.Desktop.csproj | 1 - .../Utilities/DeploymentResult.cs | 31 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs diff --git a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj index e8857c293a..4496d6a4b8 100644 --- a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj +++ b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj @@ -61,7 +61,6 @@ - diff --git a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs deleted file mode 100644 index cf67e117e2..0000000000 --- a/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentResult.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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.MSTestAdapter.PlatformServices -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment; - - internal class DeploymentResult - { - public DeploymentResult(bool success, TestRunDirectories directories) - { - this.Success = success; - this.RunDirectories = directories; - } - - /// - /// Gets a value indicating whether the deployment was successful - /// - public bool Success { get; private set; } - - /// - /// Gets a value for the RunDirectories created for the deployment - /// - public TestRunDirectories RunDirectories { get; private set; } - } -}