diff --git a/TestFx.sln b/TestFx.sln index 2a38fb3edb..61f330377d 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -172,6 +172,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataSourceTestProject", "te EndProject 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 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompatTestProject", "test\E2ETests\TestAssets\CompatTestProject\CompatTestProject.csproj", "{2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProjectForAssemblyResolution", "test\ComponentTests\TestAssets\TestProjectForAssemblyResolution\TestProjectForAssemblyResolution.csproj", "{0B057B99-DCDD-417A-BC19-3E63DDD86F24}" @@ -965,6 +967,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 {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1069,6 +1095,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} {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {0B057B99-DCDD-417A-BC19-3E63DDD86F24} = {1899187D-8B9C-40C2-9F04-9E9A76C9A919} EndGlobalSection diff --git a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj index a455be386a..4496d6a4b8 100644 --- a/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj +++ b/src/Adapter/PlatformServices.Desktop/PlatformServices.Desktop.csproj @@ -59,6 +59,7 @@ + @@ -77,7 +78,7 @@ - + @@ -93,6 +94,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 bcbbb10e1c..aee1ec0dde 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/DeploymentUtility.cs b/src/Adapter/PlatformServices.Desktop/Utilities/DeploymentUtility.cs index 277b16cacc..c5338cc829 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 { - 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 }; + } + + // 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 }; } - 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 c92ea8e0f0..4078045120 100644 --- a/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj +++ b/test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj @@ -42,6 +42,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..6e41edd409 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; @@ -395,7 +397,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 +454,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) =>