diff --git a/TestFx.sln b/TestFx.sln index c5799f1c56..2a38fb3edb 100644 --- a/TestFx.sln +++ b/TestFx.sln @@ -172,7 +172,9 @@ 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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompatTestProject", "test\E2ETests\TestAssets\CompatTestProject\CompatTestProject.csproj", "{2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}" +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}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -987,6 +989,30 @@ Global {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Release|x64.Build.0 = Release|Any CPU {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Release|x86.ActiveCfg = Release|Any CPU {2D2C5B73-F1F1-47C8-BC5C-A172E9BB3D16}.Release|x86.Build.0 = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|Any CPU.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|ARM.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|ARM.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|x64.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|x64.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|x86.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Code Analysis Debug|x86.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|ARM.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|ARM.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|x64.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|x64.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|x86.ActiveCfg = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Debug|x86.Build.0 = Debug|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|Any CPU.Build.0 = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|ARM.ActiveCfg = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|ARM.Build.0 = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|x64.ActiveCfg = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|x64.Build.0 = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|x86.ActiveCfg = Release|Any CPU + {0B057B99-DCDD-417A-BC19-3E63DDD86F24}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1044,6 +1070,7 @@ Global {5A4967CD-B527-4D43-81C2-4CA90EE10222} = {D53BD452-F69F-4FB3-8B98-386EDA28A4C8} {8080DE48-CFD9-4F33-908A-8B71108CA223} = {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 GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {31E0F4D5-975A-41CC-933E-545B2201FAF9} diff --git a/src/Adapter/MSTest.CoreAdapter/Discovery/AssemblyEnumeratorWrapper.cs b/src/Adapter/MSTest.CoreAdapter/Discovery/AssemblyEnumeratorWrapper.cs index 7849e196a5..76de782081 100644 --- a/src/Adapter/MSTest.CoreAdapter/Discovery/AssemblyEnumeratorWrapper.cs +++ b/src/Adapter/MSTest.CoreAdapter/Discovery/AssemblyEnumeratorWrapper.cs @@ -145,7 +145,7 @@ private ICollection GetTestsInIsolation(string fullFilePath, IR typeof(AssemblyEnumerator), new object[] { MSTestSettings.CurrentSettings }) as AssemblyEnumerator; // After loading adapter reset the child-domain's appbase to point to test source location - isolationHost.UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver(); + isolationHost.UpdateAppBaseToTestSourceLocation(); return assemblyEnumerator.EnumerateAssembly(fullFilePath, out warnings); } diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs b/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs index e6c0df533f..c62710a013 100644 --- a/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs +++ b/src/Adapter/MSTest.CoreAdapter/Execution/TestExecutionManager.cs @@ -223,7 +223,7 @@ private void ExecuteTestsInSource(IEnumerable tests, IRunContext runCo new object[] { MSTestSettings.CurrentSettings }) as UnitTestRunner; // After loading adapter reset the chils-domain's appbase to point to test source location - isolationHost.UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver(); + isolationHost.UpdateAppBaseToTestSourceLocation(); PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("Created unit-test runner {0}", source); diff --git a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestSourceHost.cs b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestSourceHost.cs index 18118a733f..dc6c4d3ecc 100644 --- a/src/Adapter/PlatformServices.Desktop/Services/DesktopTestSourceHost.cs +++ b/src/Adapter/PlatformServices.Desktop/Services/DesktopTestSourceHost.cs @@ -36,7 +36,10 @@ public class TestSourceHost : ITestSourceHost /// private AssemblyResolver childDomainAssemblyResolver; - private List cachedResolutionPaths; + /// + /// Determines whether child-appdomain needs to be created based on DisableAppDomain Flag set in runsettings + /// + private bool isAppDomainCreationDisabled; private string sourceFileName; private IRunSettings runSettings; @@ -68,6 +71,17 @@ internal TestSourceHost(string sourceFileName, IRunSettings runSettings, IFramew // Set the environment context. this.SetContext(sourceFileName); + + // Set isAppDomainCreationDisabled flag + this.AppDomainCreationDisabledInRunSettings(); + } + + internal AppDomain AppDomain + { + get + { + return this.domain; + } } /// @@ -75,35 +89,63 @@ internal TestSourceHost(string sourceFileName, IRunSettings runSettings, IFramew /// public void SetupHost() { - if (this.AppDomainCreationDisabledInRunSettings()) + List resolutionPaths = this.GetResolutionPaths(this.sourceFileName, VSInstallationUtilities.IsCurrentProcessRunningInPortableMode()); + + if (EqtTrace.IsInfoEnabled) { - return; + EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths.ToArray())); } - // Setup app-domain - var appDomainSetup = new AppDomainSetup(); - - this.targetFrameworkVersion = this.GetTargetFrameworkVersionString(this.sourceFileName); - - if (EqtTrace.IsInfoEnabled) + // Case when DisableAppDomain setting is present in runsettings and no child-appdomain needs to be created + if (this.isAppDomainCreationDisabled) { - EqtTrace.Info("TestSourceHost: Creating app-domain for source {0} with application base path {1}.", this.sourceFileName, appDomainSetup.ApplicationBase); + this.parentDomainAssemblyResolver = new AssemblyResolver(resolutionPaths); + this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.parentDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName)); } - AppDomainUtilities.SetAppDomainFrameworkVersionBasedOnTestSource(appDomainSetup, this.targetFrameworkVersion); + // Create child-appdomain and set assembly resolver on it + else + { + // Setup app-domain + var appDomainSetup = new AppDomainSetup(); + this.targetFrameworkVersion = this.GetTargetFrameworkVersionString(this.sourceFileName); + AppDomainUtilities.SetAppDomainFrameworkVersionBasedOnTestSource(appDomainSetup, this.targetFrameworkVersion); + + // Temporarily set appbase to the location from where adapter should be picked up from. We will later reset this to test source location + // once adapter gets loaded in the child app domain. + appDomainSetup.ApplicationBase = Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location); + + var configFile = this.GetConfigFileForTestSource(this.sourceFileName); + AppDomainUtilities.SetConfigurationFile(appDomainSetup, configFile); + + EqtTrace.Info("DesktopTestSourceHost.SetupHost(): Creating app-domain for source {0} with application base path {1}.", this.sourceFileName, appDomainSetup.ApplicationBase); - // Temporarily set appbase to the location from where adapter should be picked up from. We will later reset this to test source location - // once adapter gets loaded in the child app domain. - appDomainSetup.ApplicationBase = Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location); + string domainName = string.Format("TestSourceHost: Enumering source ({0})", this.sourceFileName); + this.domain = this.appDomain.CreateDomain(domainName, null, appDomainSetup); - var configFile = this.GetConfigFileForTestSource(this.sourceFileName); - AppDomainUtilities.SetConfigurationFile(appDomainSetup, configFile); + // Load objectModel before creating assembly resolver otherwise in 3.5 process, we run into a recurive assembly resolution + // which is trigged by AppContainerUtilities.AttachEventToResolveWinmd method. + EqtTrace.SetupRemoteEqtTraceListeners(this.domain); - this.domain = this.appDomain.CreateDomain("TestSourceHost: Enumering assembly", null, appDomainSetup); + // Add an assembly resolver in the child app-domain... + Type assemblyResolverType = typeof(AssemblyResolver); + + EqtTrace.Info("DesktopTestSourceHost.SetupHost(): assemblyenumerator location: {0} , fullname: {1} ", assemblyResolverType.Assembly.Location, assemblyResolverType.FullName); + + var resolver = AppDomainUtilities.CreateInstance( + this.domain, + assemblyResolverType, + new object[] { resolutionPaths }); + + EqtTrace.Info( + "DesktopTestSourceHost.SetupHost(): resolver type: {0} , resolve type assembly: {1} ", + resolver.GetType().FullName, + resolver.GetType().Assembly.Location); + + this.childDomainAssemblyResolver = (AssemblyResolver)resolver; - // Load objectModel before creating assembly resolver otherwise in 3.5 process, we run into a recurive assembly resolution - // which is trigged by AppContainerUtilities.AttachEventToResolveWinmd method. - EqtTrace.SetupRemoteEqtTraceListeners(this.domain); + this.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(this.childDomainAssemblyResolver, Path.GetDirectoryName(this.sourceFileName)); + } } /// @@ -119,7 +161,7 @@ public void SetupHost() public object CreateInstanceForType(Type type, object[] args) { // Honour DisableAppDomain setting if it is present in runsettings - if (this.AppDomainCreationDisabledInRunSettings()) + if (this.isAppDomainCreationDisabled) { return Activator.CreateInstance(type, args); } @@ -153,10 +195,7 @@ public void Dispose() catch (Exception exception) { // This happens usually when a test spawns off a thread and fails to clean it up. - if (EqtTrace.IsErrorEnabled) - { - EqtTrace.Error("The app domain running tests could not be unloaded. Exception: {0}", exception); - } + EqtTrace.Error("DesktopTestSourceHost.Dispose(): The app domain running tests could not be unloaded. Exception: {0}", exception); if (this.frameworkHandle != null) { @@ -164,10 +203,7 @@ public void Dispose() // since we we have issues in unloading appdomain. We do so to avoid any assembly locking issues. this.frameworkHandle.EnableShutdownAfterTestRun = true; - if (EqtTrace.IsVerboseEnabled) - { - EqtTrace.Verbose("Notifying the test platform that the test host process should be shut down because the app domain running tests could not be unloaded successfully."); - } + EqtTrace.Verbose("DesktopTestSourceHost.Dispose(): Notifying the test platform that the test host process should be shut down because the app domain running tests could not be unloaded successfully."); } } @@ -180,126 +216,34 @@ public void Dispose() } /// - /// Updates child-domain's appbase to point to test source location and sets up - /// Assembly resolver for both parent and child appdomain + /// Updates child-domain's appbase to point to test source location. /// - public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver() + public void UpdateAppBaseToTestSourceLocation() { - List resolutionPaths = this.GetResolutionPaths(this.sourceFileName, VSInstallationUtilities.IsCurrentProcessRunningInPortableMode()); - - // Check if user specified any runsettings - MSTestAdapterSettings adapterSettings = MSTestSettingsProvider.Settings; - - if (resolutionPaths != null && resolutionPaths.Count > 0) - { - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("TestSourceHost: Creating assembly resolver with resolution paths {0}.", string.Join(",", resolutionPaths.ToArray())); - } - - // Adding adapter folder to resolution paths - if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location))) - { - resolutionPaths.Add(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); - } - - // Adding extensions folder to resolution paths - if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location))) - { - resolutionPaths.Add(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location)); - } - } - - // Case when DisableAppDomain setting is present in runsettings and no child-appdomain is created - if (this.AppDomainCreationDisabledInRunSettings()) + // Simply return if no child-appdomain was created + if (this.isAppDomainCreationDisabled) { - if (adapterSettings != null) - { - try - { - this.parentDomainAssemblyResolver = new AssemblyResolver(resolutionPaths); - this.parentDomainAssemblyResolver.AddSearchDirectoriesFromRunSetting(adapterSettings.GetDirectoryListWithRecursiveProperty(null)); - } - catch (Exception exception) - { - if (EqtTrace.IsErrorEnabled) - { - EqtTrace.Error(exception); - } - } - } + return; } - // Case when Child-appdomain was created successfully, update appbase and set assembly resolver on it - else if (this.domain != null) + // After adapter has been loaded, reset child-appdomains appbase. + // The below logic of preferential setting the appdomains appbase is needed because: + // 1. We set this to the location of the test source if it is built for Full CLR -> Ideally this needs to be done in all situations. + // 2. We set this to the location where the current adapter is being picked up from for UWP and .Net Core scenarios -> This needs to be + // different especially for UWP because we use the desktop adapter(from %temp%\VisualStudioTestExplorerExtensions) itself for test discovery + // in IDE scenarios. If the app base is set to the test source location, discovery will not work because we drop the + // UWP platform service assembly at the test source location and since CLR starts looking for assemblies from the app base location, + // there would be a mismatch of platform service assemblies during discovery. + if (this.targetFrameworkVersion.Contains(PlatformServices.Constants.DotNetFrameWorkStringPrefix)) { - // After adapter has been loaded, reset appdomains appbase. - // The below logic of preferential setting the appdomains appbase is needed because: - // 1. We set this to the location of the test source if it is built for Full CLR -> Ideally this needs to be done in all situations. - // 2. We set this to the location where the current adapter is being picked up from for UWP and .Net Core scenarios -> This needs to be - // different especially for UWP because we use the desktop adapter(from %temp%\VisualStudioTestExplorerExtensions) itself for test discovery - // in IDE scenarios. If the app base is set to the test source location, discovery will not work because we drop the - // UWP platform service assembly at the test source location and since CLR starts looking for assemblies from the app base location, - // there would be a mismatch of platform service assemblies during discovery. - if (this.targetFrameworkVersion.Contains(PlatformServices.Constants.DotNetFrameWorkStringPrefix)) - { - this.domain.SetData("APPBASE", Path.GetDirectoryName(this.sourceFileName) ?? Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); - } - else - { - this.domain.SetData("APPBASE", Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); - } - - // Add an assembly resolver in the child app-domain... - Type assemblyResolverType = typeof(AssemblyResolver); - - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("TestSourceHost: assemblyenumerator location: {0} , fullname: {1} ", assemblyResolverType.Assembly.Location, assemblyResolverType.FullName); - } - - var resolver = AppDomainUtilities.CreateInstance( - this.domain, - assemblyResolverType, - new object[] { resolutionPaths }); - - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info( - "TestSourceHost: resolver type: {0} , resolve type assembly: {1} ", - resolver.GetType().FullName, - resolver.GetType().Assembly.Location); - } - - this.childDomainAssemblyResolver = (AssemblyResolver)resolver; - - if (adapterSettings != null) - { - try - { - var additionalSearchDirectories = - adapterSettings.GetDirectoryListWithRecursiveProperty(this.domain.SetupInformation.ApplicationBase); - if (additionalSearchDirectories?.Count > 0) - { - this.childDomainAssemblyResolver.AddSearchDirectoriesFromRunSetting( - adapterSettings.GetDirectoryListWithRecursiveProperty(this.domain.SetupInformation.ApplicationBase)); - } - } - catch (Exception exception) - { - if (EqtTrace.IsErrorEnabled) - { - EqtTrace.Error(exception); - } - } - } + this.domain.SetData("APPBASE", Path.GetDirectoryName(this.sourceFileName) ?? Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); } - - // Log error when child-appdomain was expected to be created but wasn't created. else { - EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, "TestSourceHost.AppDomain: Failed to update domain's appbase and setup assembly resolver"); + this.domain.SetData("APPBASE", Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); } + + EqtTrace.Info("DesktopTestSourceHost.UpdateAppBaseToTestSourceLocation(): Updating domain's appbase path for source {0} to {1}.", this.sourceFileName, this.domain.SetupInformation.ApplicationBase); } /// @@ -314,106 +258,137 @@ public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver() /// /// A list of path. /// - internal List GetResolutionPaths(string sourceFileName, bool isPortableMode) + internal virtual List GetResolutionPaths(string sourceFileName, bool isPortableMode) { - if (this.cachedResolutionPaths == null || this.cachedResolutionPaths.Count <= 0) + List resolutionPaths = new List(); + + // Add path of test assembly in resolution path. Mostly will be used for resovling winmd. + resolutionPaths.Add(Path.GetDirectoryName(sourceFileName)); + + if (!isPortableMode) { - this.cachedResolutionPaths = new List(); + EqtTrace.Info("DesktopTestSourceHost.GetResolutionPaths(): Not running in portable mode"); - // Add path of test assembly in resolution path. Mostly will be used for resovling winmd. - this.cachedResolutionPaths.Add(Path.GetDirectoryName(sourceFileName)); + string pathToPublicAssemblies = VSInstallationUtilities.PathToPublicAssemblies; + if (!StringUtilities.IsNullOrWhiteSpace(pathToPublicAssemblies)) + { + resolutionPaths.Add(pathToPublicAssemblies); + } - if (!isPortableMode) + string pathToPrivateAssemblies = VSInstallationUtilities.PathToPrivateAssemblies; + if (!StringUtilities.IsNullOrWhiteSpace(pathToPrivateAssemblies)) { - if (EqtTrace.IsInfoEnabled) - { - EqtTrace.Info("TestSourceHost: Not running in portable mode"); - } + resolutionPaths.Add(pathToPrivateAssemblies); + } + } - string pathToPublicAssemblies = VSInstallationUtilities.PathToPublicAssemblies; - if (!StringUtilities.IsNullOrWhiteSpace(pathToPublicAssemblies)) - { - this.cachedResolutionPaths.Add(pathToPublicAssemblies); - } + // Adding adapter folder to resolution paths + if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location))) + { + resolutionPaths.Add(Path.GetDirectoryName(typeof(TestSourceHost).Assembly.Location)); + } - string pathToPrivateAssemblies = VSInstallationUtilities.PathToPrivateAssemblies; - if (!StringUtilities.IsNullOrWhiteSpace(pathToPrivateAssemblies)) - { - this.cachedResolutionPaths.Add(pathToPrivateAssemblies); - } - } + // Adding TestPlatform folder to resolution paths + if (!resolutionPaths.Contains(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location))) + { + resolutionPaths.Add(Path.GetDirectoryName(typeof(AssemblyHelper).Assembly.Location)); } - return this.cachedResolutionPaths; + return resolutionPaths; } - internal virtual string GetTargetFrameworkVersionString(string sourceFileName) + internal virtual string GetTargetFrameworkVersionString(string sourceFileName) + { + return AppDomainUtilities.GetTargetFrameworkVersionString(sourceFileName); + } + + private string GetConfigFileForTestSource(string sourceFileName) + { + return new DeploymentUtility().GetConfigFile(sourceFileName); + } + + /// + /// Sets context required for running tests. + /// + /// + /// source parameter used for setting context + /// + private void SetContext(string source) + { + if (string.IsNullOrEmpty(source)) { - return AppDomainUtilities.GetTargetFrameworkVersionString(sourceFileName); + return; } - private string GetConfigFileForTestSource(string sourceFileName) + Exception setWorkingDirectoryException = null; + this.currentDirectory = Environment.CurrentDirectory; + + try { - return new DeploymentUtility().GetConfigFile(sourceFileName); + Environment.CurrentDirectory = Path.GetDirectoryName(source); + EqtTrace.Info("MSTestExecutor: Changed the working directory to {0}", Environment.CurrentDirectory); } - - /// - /// Sets context required for running tests. - /// - /// - /// source parameter used for setting context - /// - private void SetContext(string source) + catch (IOException ex) { - if (string.IsNullOrEmpty(source)) - { - return; - } - - Exception setWorkingDirectoryException = null; - this.currentDirectory = Environment.CurrentDirectory; - - try - { - Environment.CurrentDirectory = Path.GetDirectoryName(source); - EqtTrace.InfoIf(EqtTrace.IsInfoEnabled, "MSTestExecutor: Changed the working directory to {0}", Environment.CurrentDirectory); - } - catch (IOException ex) - { - setWorkingDirectoryException = ex; - } - catch (System.Security.SecurityException ex) - { - setWorkingDirectoryException = ex; - } + setWorkingDirectoryException = ex; + } + catch (System.Security.SecurityException ex) + { + setWorkingDirectoryException = ex; + } - if (setWorkingDirectoryException != null) - { - EqtTrace.ErrorIf(EqtTrace.IsErrorEnabled, "MSTestExecutor.SetWorkingDirectory: Failed to set the working directory to '{0}'. {1}", Path.GetDirectoryName(source), setWorkingDirectoryException); - } + if (setWorkingDirectoryException != null) + { + EqtTrace.Error("MSTestExecutor.SetWorkingDirectory: Failed to set the working directory to '{0}'. {1}", Path.GetDirectoryName(source), setWorkingDirectoryException); } + } - /// - /// Resets the context as it was before calling SetContext() - /// - private void ResetContext() + /// + /// Resets the context as it was before calling SetContext() + /// + private void ResetContext() + { + if (!string.IsNullOrEmpty(this.currentDirectory)) { - if (!string.IsNullOrEmpty(this.currentDirectory)) - { - Environment.CurrentDirectory = this.currentDirectory; - } + Environment.CurrentDirectory = this.currentDirectory; } + } - private bool AppDomainCreationDisabledInRunSettings() + private void AppDomainCreationDisabledInRunSettings() + { + if (this.runSettings != null && MSTestAdapterSettings.IsAppDomainCreationDisabled(this.runSettings.SettingsXml)) { - if (this.runSettings != null && MSTestAdapterSettings.IsAppDomainCreationDisabled(this.runSettings.SettingsXml)) + this.isAppDomainCreationDisabled = true; + } + + this.isAppDomainCreationDisabled = false; + } + + private void AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(AssemblyResolver assemblyResolver, string baseDirectory) + { + // Check if user specified any adapter settings + MSTestAdapterSettings adapterSettings = MSTestSettingsProvider.Settings; + + if (adapterSettings != null) { - return true; + try + { + var additionalSearchDirectories = adapterSettings.GetDirectoryListWithRecursiveProperty(baseDirectory); + if (additionalSearchDirectories?.Count > 0) + { + assemblyResolver.AddSearchDirectoriesFromRunSetting(additionalSearchDirectories); + } + } + catch (Exception exception) + { + EqtTrace.Error( + "DesktopTestSourceHost.AddSearchDirectoriesSpecifiedInRunSettingsToAssemblyResolver(): Exception hit while trying to set assemly resolver for domain. Exception : {0} \n Message : {1}", + exception, + exception.Message); + } } - - return false; } - } +} #pragma warning restore SA1649 // SA1649FileNameMustMatchTypeName } diff --git a/src/Adapter/PlatformServices.Interface/ITestSourceHost.cs b/src/Adapter/PlatformServices.Interface/ITestSourceHost.cs index a75726c0de..6875bca5b8 100644 --- a/src/Adapter/PlatformServices.Interface/ITestSourceHost.cs +++ b/src/Adapter/PlatformServices.Interface/ITestSourceHost.cs @@ -29,9 +29,8 @@ public interface ITestSourceHost : IDisposable object CreateInstanceForType(Type type, object[] args); /// - /// Updates child-domain's appbase to point to test source location and sets up - /// Assembly resolver for both parent and child appdomain + /// Updates child-domain's appbase to point to test source location /// - void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver(); + void UpdateAppBaseToTestSourceLocation(); } } diff --git a/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10TestSourceHost.cs b/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10TestSourceHost.cs index 6ece2927cb..4deeb0da2c 100644 --- a/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10TestSourceHost.cs +++ b/src/Adapter/PlatformServices.Shared/netstandard1.0/Services/ns10TestSourceHost.cs @@ -55,10 +55,9 @@ public void Dispose() } /// - /// Updates child-domain's appbase to point to test source location and sets up - /// Assembly resolver for both parent and child appdomain + /// Updates child-domain's appbase to point to test source location /// - public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver() + public void UpdateAppBaseToTestSourceLocation() { // Do nothing. } diff --git a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/App.config b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/App.config new file mode 100644 index 0000000000..2d1bdf9d41 --- /dev/null +++ b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/App.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/DesktopTestSourceHostTests.cs b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/DesktopTestSourceHostTests.cs new file mode 100644 index 0000000000..4ce931e013 --- /dev/null +++ b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/DesktopTestSourceHostTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace PlatformServices.Desktop.ComponentTests +{ + extern alias FrameworkV1; + extern alias FrameworkV2; + + using System; + using System.Diagnostics; + using System.IO; + using System.Reflection; + using System.Xml; + using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; + using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; + using Moq; + using Assert = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.Assert; + using TestClass = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute; + using TestMethod = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; + + [TestClass] + public class DesktopTestSourceHostTests + { + private TestSourceHost testSourceHost; + private string testSource; + + public DesktopTestSourceHostTests() + { + var currentAssemblyPath = Path.GetDirectoryName(typeof(DesktopTestSourceHostTests).Assembly.Location); + var testAssetPath = + Path.Combine( + Directory.GetParent(Directory.GetParent(Directory.GetParent(currentAssemblyPath).FullName).FullName).FullName, + "artifacts", + "TestAssets"); + this.testSource = Path.Combine(testAssetPath, "DesktopTestProjectx86Debug.dll"); + } + + [TestMethod] + public void ParentDomainShouldHonourSearchDirectoriesSpecifiedInRunsettings() + { + string runSettingxml = + @" + + True + + + + + + + + + "; + + this.testSourceHost = new TestSourceHost(this.testSource, this.GetMockedIRunSettings(runSettingxml).Object, null); + this.testSourceHost.SetupHost(); + + // Loading TestProjectForAssemblyResolution.dll should not throw. + // It is present in specified in runsettings + Assembly.Load("TestProjectForAssemblyResolution"); + } + + [TestMethod] + public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRunsettings() + { + string runSettingxml = + @" + + False + + + + + + + + + "; + + this.testSourceHost = new TestSourceHost(this.testSource, this.GetMockedIRunSettings(runSettingxml).Object, null); + + this.testSourceHost.SetupHost(); + + // Creating instance of TestProjectForAssemblyResolution should not throw. + // It is present in specified in runsettings + AppDomainUtilities.CreateInstance(this.testSourceHost.AppDomain, typeof(TestProjectForAssemblyResolution), null); + } + + [TestMethod] + public void DisposeShouldUnloadChildAppDomain() + { + this.testSourceHost = new TestSourceHost(this.testSource, null, null); + this.testSourceHost.SetupHost(); + + // Check that child appdmon was indeed created + Assert.IsNotNull(this.testSourceHost.AppDomain); + this.testSourceHost.Dispose(); + + // Check that child-appdomain is now unloaded. + Assert.IsNull(this.testSourceHost.AppDomain); + } + + private Mock GetMockedIRunSettings(string runSettingxml) + { + var mockRunSettings = new Mock(); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + + StringReader stringReader = new StringReader(runSettingxml); + XmlReader reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings); + MSTestSettingsProvider mstestSettingsProvider = new MSTestSettingsProvider(); + reader.ReadToFollowing("MSTestV2"); + mstestSettingsProvider.Load(reader); + + return mockRunSettings; + } + } +} diff --git a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/PlatformServices.Desktop.Component.Tests.csproj b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/PlatformServices.Desktop.Component.Tests.csproj index 8fe086ae58..9a3f8b42cc 100644 --- a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/PlatformServices.Desktop.Component.Tests.csproj +++ b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/PlatformServices.Desktop.Component.Tests.csproj @@ -38,16 +38,38 @@ 4 + + ..\..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll + + + ..\..\..\packages\Microsoft.TestPlatform.ObjectModel.11.0.0\lib\net35\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll + True + + + ..\..\..\packages\Moq.4.8.2\lib\net45\Moq.dll + FrameworkV1 + + + ..\..\..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + + ..\..\..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll + + + + + Designer + @@ -55,6 +77,10 @@ {b0fce474-14bc-449a-91ea-a433342c0d63} PlatformServices.Desktop + + {bbc99a6b-4490-49dd-9c12-af2c1e95576e} + PlatformServices.Interface + {7252d9e3-267d-442c-96bc-c73aef3241d6} MSTest.Core @@ -64,6 +90,10 @@ {5c411bbf-fcc9-4430-ae08-07666d39fd04} SampleFrameworkExtensions + + {0b057b99-dcdd-417a-bc19-3e63ddd86f24} + TestProjectForAssemblyResolution + diff --git a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/packages.config b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/packages.config index 3644cdfa3c..d7e211d3bf 100644 --- a/test/ComponentTests/PlatformServices.Desktop.Component.Tests/packages.config +++ b/test/ComponentTests/PlatformServices.Desktop.Component.Tests/packages.config @@ -1,4 +1,9 @@  + + + + + \ No newline at end of file diff --git a/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/TestProjectForAssemblyResolution.csproj b/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/TestProjectForAssemblyResolution.csproj new file mode 100644 index 0000000000..052e8878de --- /dev/null +++ b/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/TestProjectForAssemblyResolution.csproj @@ -0,0 +1,23 @@ + + + + ..\..\..\..\ + + + + net451 + false + false + $(TestFxRoot)artifacts\TestAssets\ComponentTests + true + $(TestFxRoot)scripts\build\key.snk + true + + + + + + + + + diff --git a/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/UnitTest1.cs b/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/UnitTest1.cs new file mode 100644 index 0000000000..205eca7442 --- /dev/null +++ b/test/ComponentTests/TestAssets/TestProjectForAssemblyResolution/UnitTest1.cs @@ -0,0 +1,15 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace PlatformServices.Desktop.ComponentTests +{ + [TestClass] + [Serializable] + public class TestProjectForAssemblyResolution + { + [TestMethod] + public void TestMethod1() + { + } + } +} diff --git a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestSourceHostTests.cs b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestSourceHostTests.cs index dcc7b82c33..77a000d8a5 100644 --- a/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestSourceHostTests.cs +++ b/test/UnitTests/PlatformServices.Desktop.Unit.Tests/Services/DesktopTestSourceHostTests.cs @@ -15,6 +15,7 @@ namespace MSTestAdapter.PlatformServices.Desktop.UnitTests using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices; using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using Moq; @@ -55,6 +56,32 @@ public void GetResolutionPathsShouldNotAddPublicAndPrivateAssemblyPathInPortable Assert.AreEqual(result.Contains(VSInstallationUtilities.PathToPrivateAssemblies), false); } + [TestMethod] + public void GetResolutionPathsShouldAddAdapterFolderPath() + { + // Setup + TestSourceHost sut = new TestSourceHost(null, null, null); + + // Execute + List result = sut.GetResolutionPaths("DummyAssembly.dll", isPortableMode: false); + + // Assert + Assert.AreEqual(result.Contains(typeof(TestSourceHost).Assembly.Location), false); + } + + [TestMethod] + public void GetResolutionPathsShouldAddTestPlatformFolderPath() + { + // Setup + TestSourceHost sut = new TestSourceHost(null, null, null); + + // Execute + List result = sut.GetResolutionPaths("DummyAssembly.dll", isPortableMode: false); + + // Assert + Assert.AreEqual(result.Contains(typeof(AssemblyHelper).Assembly.Location), false); + } + [TestMethod] public void CreateInstanceForTypeShouldCreateTheTypeInANewAppDomain() { @@ -79,7 +106,7 @@ public void CreateInstanceForTypeShouldCreateTheTypeInANewAppDomain() } [TestMethod] - public void SetupHostShouldSetNewDomainsAppBaseToAdapterLocation() + public void SetupHostShouldSetChildDomainsAppBaseToAdapterLocation() { // Arrange DummyClass dummyclass = new DummyClass(); @@ -102,13 +129,69 @@ public void SetupHostShouldSetNewDomainsAppBaseToAdapterLocation() } } + [TestMethod] + public void SetupHostShouldHaveParentDomainsAppBaseSetToTestSourceLocation() + { + // Arrange + DummyClass dummyclass = new DummyClass(); + string runSettingxml = + @" + + True + + "; + + var location = typeof(TestSourceHost).Assembly.Location; + var mockRunSettings = new Mock(); + mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingxml); + + Mock sourceHost = new Mock(location, mockRunSettings.Object, null) { CallBase = true }; + + try + { + // Act + sourceHost.Object.SetupHost(); + var expectedObject = sourceHost.Object.CreateInstanceForType(typeof(DummyClass), null) as DummyClass; + + // Assert + Assert.AreEqual(Path.GetDirectoryName(typeof(DesktopTestSourceHostTests).Assembly.Location), expectedObject.AppDomainAppBase); + } + finally + { + sourceHost.Object.Dispose(); + } + } + + [TestMethod] + public void SetupHostShouldSetResolutionsPaths() + { + // Arrange + DummyClass dummyclass = new DummyClass(); + + var location = typeof(TestSourceHost).Assembly.Location; + Mock sourceHost = new Mock(location, null, null) { CallBase = true }; + + try + { + // Act + sourceHost.Object.SetupHost(); + + // Assert + sourceHost.Verify(sh => sh.GetResolutionPaths(location, It.IsAny()), Times.Once); + } + finally + { + sourceHost.Object.Dispose(); + } + } + /// /// This test should ideally be choosing a different path for the test source. Currently both the test source and the adapter /// are in the same location. However when we move to run these tests with the V2 itself, then this would be valid. /// Leaving the test running till then. /// [TestMethod] - public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolverShouldSetDomainsAppBaseToTestSourceLocationForFullCLRTestss() + public void UpdateAppBaseToTestSourceLocationShouldSetDomainsAppBaseToTestSourceLocationForFullCLRTestss() { // Arrange DummyClass dummyclass = new DummyClass(); @@ -124,7 +207,7 @@ public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolverShouldSetDo sourceHost.Object.SetupHost(); var expectedObject = sourceHost.Object.CreateInstanceForType(typeof(DummyClass), null) as DummyClass; - sourceHost.Object.UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver(); + sourceHost.Object.UpdateAppBaseToTestSourceLocation(); // Assert Assert.AreEqual(Path.GetDirectoryName(location), expectedObject.AppDomainAppBase); @@ -136,7 +219,7 @@ public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolverShouldSetDo } [TestMethod] - public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolverShouldSetDomainsAppBaseToAdaptersLocationForNonFullCLRTests() + public void UpdateAppBaseToTestSourceLocationShouldSetDomainsAppBaseToAdaptersLocationForNonFullCLRTests() { // Arrange DummyClass dummyclass = new DummyClass(); @@ -151,7 +234,7 @@ public void UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolverShouldSetDo // Act sourceHost.Object.SetupHost(); var expectedObject = sourceHost.Object.CreateInstanceForType(typeof(DummyClass), null) as DummyClass; - sourceHost.Object.UpdateAppBaseToTestSourceLocationAndSetupAssemblyResolver(); + sourceHost.Object.UpdateAppBaseToTestSourceLocation(); // Assert Assert.AreEqual(Path.GetDirectoryName(location), expectedObject.AppDomainAppBase);