diff --git a/NuGet.Config b/NuGet.Config index fd5ce235a..18d3d4945 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,6 +4,7 @@ + diff --git a/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/app.config b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/app.config new file mode 100644 index 000000000..25aeea673 --- /dev/null +++ b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/app.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/project.json b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/project.json new file mode 100644 index 000000000..a4ec663cf --- /dev/null +++ b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsAndConfig/project.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compile": [ + "../src/*.cs" + ], + "dependencies": { + "Newtonsoft.Json": "8.0.3", + "Microsoft.AspNet.Mvc": "3.0.50813.1", + "Unity.Mvc": "3.0.1304", + "dotnet-desktop-binding-redirects": "1.0.0-*" + }, + "frameworks": { + "net451": {} + } +} diff --git a/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsNoConfig/project.json b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsNoConfig/project.json new file mode 100644 index 000000000..a4ec663cf --- /dev/null +++ b/TestAssets/DesktopTestProjects/BindingRedirectSample/AppWithRedirectsNoConfig/project.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "compile": [ + "../src/*.cs" + ], + "dependencies": { + "Newtonsoft.Json": "8.0.3", + "Microsoft.AspNet.Mvc": "3.0.50813.1", + "Unity.Mvc": "3.0.1304", + "dotnet-desktop-binding-redirects": "1.0.0-*" + }, + "frameworks": { + "net451": {} + } +} diff --git a/TestAssets/DesktopTestProjects/BindingRedirectSample/src/Program.cs b/TestAssets/DesktopTestProjects/BindingRedirectSample/src/Program.cs new file mode 100644 index 000000000..a71a0aed5 --- /dev/null +++ b/TestAssets/DesktopTestProjects/BindingRedirectSample/src/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace BindingRedirects +{ + public class Program + { + private const string ExpectedNewtonSoftVersion = "8.0.0.0"; + + public static int Main(string[] args) + { + return VerifyJsonLoad(); + } + + private static int VerifyJsonLoad() + { + WriteLine("=======Verifying Redirected Newtonsoft.Json assembly load======="); + + int result = 0; + + try + { + var jsonAsm = typeof(Newtonsoft.Json.JsonConvert).Assembly; + var version = jsonAsm.GetName().Version.ToString(); + if (version != ExpectedNewtonSoftVersion) + { + WriteLine($"Failure - Newtonsoft.Json: ExpectedVersion - {ExpectedNewtonSoftVersion}, ActualVersion - {version}"); + result = -1; + } + } + catch (Exception ex) + { + WriteLine($"Failed to load type 'Newtonsoft.Json.JsonConvert'"); + throw ex; + } + + return result; + } + + private static void WriteLine(string str) + { + var currentAssembly = Assembly.GetExecutingAssembly().GetName().Name; + Console.WriteLine($"{currentAssembly}: {str}"); + } + } +} diff --git a/TestAssets/TestPackages/dotnet-desktop-binding-redirects/Program.cs b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/Program.cs new file mode 100644 index 000000000..a71a0aed5 --- /dev/null +++ b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace BindingRedirects +{ + public class Program + { + private const string ExpectedNewtonSoftVersion = "8.0.0.0"; + + public static int Main(string[] args) + { + return VerifyJsonLoad(); + } + + private static int VerifyJsonLoad() + { + WriteLine("=======Verifying Redirected Newtonsoft.Json assembly load======="); + + int result = 0; + + try + { + var jsonAsm = typeof(Newtonsoft.Json.JsonConvert).Assembly; + var version = jsonAsm.GetName().Version.ToString(); + if (version != ExpectedNewtonSoftVersion) + { + WriteLine($"Failure - Newtonsoft.Json: ExpectedVersion - {ExpectedNewtonSoftVersion}, ActualVersion - {version}"); + result = -1; + } + } + catch (Exception ex) + { + WriteLine($"Failed to load type 'Newtonsoft.Json.JsonConvert'"); + throw ex; + } + + return result; + } + + private static void WriteLine(string str) + { + var currentAssembly = Assembly.GetExecutingAssembly().GetName().Name; + Console.WriteLine($"{currentAssembly}: {str}"); + } + } +} diff --git a/TestAssets/TestPackages/dotnet-desktop-binding-redirects/dotnet-desktop-binding-redirects.xproj b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/dotnet-desktop-binding-redirects.xproj new file mode 100644 index 000000000..9574b745e --- /dev/null +++ b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/dotnet-desktop-binding-redirects.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 281f1cdb-8627-47c7-9bb1-cde8d30d93d1 + dotnet-desktop-binding-redirects + ..\artifacts\obj\$(MSBuildProjectName) + ..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + \ No newline at end of file diff --git a/TestAssets/TestPackages/dotnet-desktop-binding-redirects/project.json b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/project.json new file mode 100644 index 000000000..c256bbc20 --- /dev/null +++ b/TestAssets/TestPackages/dotnet-desktop-binding-redirects/project.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "Newtonsoft.Json": "5.0.0", + "NuGet.Protocol.Core.v3": "3.3.0" + }, + "frameworks": { + "net451": { + "frameworkAssemblies": { + "System.Configuration": "" + } + } + } +} + diff --git a/scripts/dotnet-cli-build/TestPackageProjects.cs b/scripts/dotnet-cli-build/TestPackageProjects.cs index ad228211f..ff7829be0 100644 --- a/scripts/dotnet-cli-build/TestPackageProjects.cs +++ b/scripts/dotnet-cli-build/TestPackageProjects.cs @@ -77,6 +77,16 @@ namespace Microsoft.DotNet.Cli.Build Frameworks = new [] { "net451", "netcoreapp1.0" } }, new TestPackageProject() + { + Name = "dotnet-desktop-binding-redirects", + IsTool = true, + Path = "TestAssets/TestPackages/dotnet-desktop-binding-redirects", + IsApplicable = CurrentPlatform.IsWindows, + VersionSuffix = s_testPackageBuildVersionSuffix, + Clean = true, + Frameworks = new [] { "net451" } + }, + new TestPackageProject() { Name = "dotnet-hello", IsTool = true, diff --git a/scripts/dotnet-cli-build/TestTargets.cs b/scripts/dotnet-cli-build/TestTargets.cs index 3295fa083..bb5cf4177 100644 --- a/scripts/dotnet-cli-build/TestTargets.cs +++ b/scripts/dotnet-cli-build/TestTargets.cs @@ -39,6 +39,11 @@ namespace Microsoft.DotNet.Cli.Build "Microsoft.Extensions.DependencyModel.Tests" }; + public static readonly string[] WindowsTestProjects = new[] + { + "binding-redirects.Tests" + }; + public static readonly dynamic[] ConditionalTestAssets = new[] { new { Path = "AppWithDirectDependencyDesktopAndPortable", Skip = new Func(() => !CurrentPlatform.IsWindows) } @@ -59,9 +64,9 @@ namespace Microsoft.DotNet.Cli.Build [Target(nameof(RestoreTestAssetPackages), nameof(BuildTestAssetPackages))] public static BuildTargetResult SetupTestPackages(BuildTargetContext c) => c.Success(); - [Target(nameof(RestoreTestAssetProjects), - nameof(RestoreDesktopTestAssetProjects), - nameof(BuildTestAssetProjects), + [Target(nameof(RestoreTestAssetProjects), + nameof(RestoreDesktopTestAssetProjects), + nameof(BuildTestAssetProjects), nameof(BuildDesktopTestAssetProjects))] public static BuildTargetResult SetupTestProjects(BuildTargetContext c) => c.Success(); @@ -223,7 +228,7 @@ namespace Microsoft.DotNet.Cli.Build foreach (var packageProject in TestPackageProjects.Projects.Where(p => p.IsApplicable && p.Clean)) { Rmdir(Path.Combine(Dirs.NuGetPackages, packageProject.Name)); - if(packageProject.IsTool) + if (packageProject.IsTool) { Rmdir(Path.Combine(Dirs.NuGetPackages, ".tools", packageProject.Name)); } @@ -276,7 +281,7 @@ namespace Microsoft.DotNet.Cli.Build var configuration = c.BuildContext.Get("Configuration"); - foreach (var testProject in TestProjects) + foreach (var testProject in GetTestProjects()) { c.Info($"Building tests: {testProject}"); dotnet.Build("--configuration", configuration) @@ -307,7 +312,7 @@ namespace Microsoft.DotNet.Cli.Build // Run the tests and set the VS vars in the environment when running them var failingTests = new List(); - foreach (var project in TestProjects) + foreach (var project in GetTestProjects()) { c.Info($"Running tests in: {project}"); var result = dotnet.Test("--configuration", configuration, "-xml", $"{project}-testResults.xml", "-notrait", "category=failing") @@ -354,6 +359,19 @@ namespace Microsoft.DotNet.Cli.Build return c.Success(); } + private static IEnumerable GetTestProjects() + { + List testProjects = new List(); + testProjects.AddRange(testProjects); + + if (CurrentPlatform.IsWindows) + { + testProjects.AddRange(WindowsTestProjects); + } + + return testProjects; + } + private static BuildTargetResult BuildTestAssets(BuildTargetContext c, string testAssetsRoot, DotNetCli dotnet, string framework) { CleanBinObj(c, testAssetsRoot); diff --git a/src/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Microsoft.DotNet.Cli.Utils/Constants.cs index 0550983ba..d1411c7c3 100644 --- a/src/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -12,6 +12,7 @@ namespace Microsoft.DotNet.Cli.Utils public static readonly string ProjectFileName = "project.json"; public static readonly string ExeSuffix = CurrentPlatform == Platform.Windows ? ".exe" : string.Empty; + public static readonly string ConfigSuffix = ".config"; // Priority order of runnable suffixes to look for and run public static readonly string[] RunnableSuffixes = CurrentPlatform == Platform.Windows diff --git a/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs b/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs index 4e1b36c85..2a6e6b66d 100644 --- a/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs +++ b/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs @@ -52,15 +52,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common internal static void GenerateBindingRedirects(string configFile, AssemblyRedirect[] bindingRedirects) { - XDocument configRoot = null; - - if (File.Exists(configFile)) - { - configRoot = XDocument.Load(configFile); - } - - configRoot = configRoot ?? new XDocument(); - + XDocument configRoot = File.Exists(configFile) ? XDocument.Load(configFile) : new XDocument(); var configuration = GetOrAddElement(configRoot, ConfigurationElementName); var runtime = GetOrAddElement(configuration, RuntimeElementName); var assemblyBindings = GetOrAddElement(runtime, AssemblyBindingElementName); @@ -93,10 +85,15 @@ namespace Microsoft.DotNet.Cli.Compiler.Common assemblyBindings.Add(dependencyElement); } - dependencyElement.Add(new XElement(BindingRedirectElementName, - new XAttribute(OldVersionAttributeName, redirect.From.Version), - new XAttribute(NewVersionAttributeName, redirect.To.Version) - )); + bool redirectExists = dependencyElement.Elements(BindingRedirectElementName).Any(element => IsSameRedirect(redirect, element)); + + if (!redirectExists) + { + dependencyElement.Add(new XElement(BindingRedirectElementName, + new XAttribute(OldVersionAttributeName, redirect.From.Version), + new XAttribute(NewVersionAttributeName, redirect.To.Version) + )); + } } private static bool IsSameAssembly(AssemblyRedirect redirect, XElement dependentAssemblyElement) @@ -111,6 +108,16 @@ namespace Microsoft.DotNet.Cli.Compiler.Common (string)identity.Attribute(CultureAttributeName) == redirect.From.Culture; } + private static bool IsSameRedirect(AssemblyRedirect redirect, XElement bindingRedirectElement) + { + if (bindingRedirectElement == null) + { + return false; + } + return (string)bindingRedirectElement.Attribute(OldVersionAttributeName) == redirect.From.Version.ToString() && + (string)bindingRedirectElement.Attribute(NewVersionAttributeName) == redirect.To.Version.ToString(); + } + private static XElement GetOrAddElement(XContainer parent, XName elementName) { XElement element; diff --git a/src/Microsoft.DotNet.Compiler.Common/Executable.cs b/src/Microsoft.DotNet.Compiler.Common/Executable.cs index 5d6ea890f..8b1cb396b 100644 --- a/src/Microsoft.DotNet.Compiler.Common/Executable.cs +++ b/src/Microsoft.DotNet.Compiler.Common/Executable.cs @@ -254,7 +254,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common public void GenerateBindingRedirects(LibraryExporter exporter) { var outputName = _outputPaths.RuntimeFiles.Assembly; - var configFile = outputName + ".config"; + var configFile = outputName + Constants.ConfigSuffix; var existingConfig = new DirectoryInfo(_context.ProjectDirectory) .EnumerateFiles() @@ -271,12 +271,12 @@ namespace Microsoft.DotNet.Cli.Compiler.Common foreach (var export in exporter.GetDependencies()) { var dependencyExecutables = export.RuntimeAssemblyGroups.GetDefaultAssets() - .Where(asset => asset.FileName.ToLower().EndsWith(".exe")) + .Where(asset => asset.FileName.ToLower().EndsWith(FileNameSuffixes.DotNet.Exe)) .Select(asset => Path.Combine(_runtimeOutputPath, asset.FileName)); foreach (var executable in dependencyExecutables) { - configFile = executable + ".config"; + configFile = executable + Constants.ConfigSuffix; configFiles.Add(configFile); } } diff --git a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestAssetsManager.cs b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestAssetsManager.cs index aa1e6ba56..fcf9b5ad6 100644 --- a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestAssetsManager.cs +++ b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestAssetsManager.cs @@ -47,7 +47,7 @@ namespace Microsoft.DotNet.TestFramework .CaptureStdOut() .CaptureStdErr() .Execute(); - + int exitCode = commandResult.ExitCode; if (exitCode != 0) @@ -93,7 +93,12 @@ namespace Microsoft.DotNet.TestFramework throw new Exception($"Cannot find '{testProjectName}' at '{AssetsRoot}'"); } - string testDestination = Path.Combine(AppContext.BaseDirectory, callingMethod + identifier, testProjectName); +#if NET451 + string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; +#else + string baseDirectory = AppContext.BaseDirectory; +#endif + string testDestination = Path.Combine(baseDirectory, callingMethod + identifier, testProjectName); var testInstance = new TestInstance(testProjectDir, testDestination); return testInstance; } diff --git a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestInstance.cs b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestInstance.cs index 99954d074..4e479d48a 100644 --- a/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestInstance.cs +++ b/src/Microsoft.DotNet.TestFramework/Microsoft.DotNet.TestFramework.TestInstance.cs @@ -63,7 +63,7 @@ namespace Microsoft.DotNet.TestFramework { file = file.ToLower(); return !file.EndsWith("project.lock.json") - && !file.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") + && !file.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") && !file.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"); }); @@ -71,7 +71,6 @@ namespace Microsoft.DotNet.TestFramework { string destFile = srcFile.Replace(_testAssetRoot, _testDestination); File.Copy(srcFile, destFile, true); - FixTimeStamp(srcFile, destFile); } } @@ -81,7 +80,6 @@ namespace Microsoft.DotNet.TestFramework { string destinationLockFile = lockFile.Replace(_testAssetRoot, _testDestination); File.Copy(lockFile, destinationLockFile, true); - FixTimeStamp(lockFile, destinationLockFile); } return this; @@ -93,9 +91,9 @@ namespace Microsoft.DotNet.TestFramework .Where(dir => { dir = dir.ToLower(); - return dir.EndsWith($"{Path.DirectorySeparatorChar}bin") + return dir.EndsWith($"{Path.DirectorySeparatorChar}bin") || dir.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") - || dir.EndsWith($"{Path.DirectorySeparatorChar}obj") + || dir.EndsWith($"{Path.DirectorySeparatorChar}obj") || dir.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"); }); @@ -109,7 +107,7 @@ namespace Microsoft.DotNet.TestFramework { file = file.ToLower(); - var isArtifact = file.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") + var isArtifact = file.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}") || file.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"); var isBlackListed = BuildArtifactBlackList.Any(b => file.Contains(b)); @@ -121,7 +119,6 @@ namespace Microsoft.DotNet.TestFramework { string destFile = binFile.Replace(_testAssetRoot, _testDestination); File.Copy(binFile, destFile, true); - FixTimeStamp(binFile, destFile); } return this; @@ -131,15 +128,5 @@ namespace Microsoft.DotNet.TestFramework { get { return _testDestination; } } - - private static void FixTimeStamp(string originalFile, string newFile) - { - // workaround for https://github.com/dotnet/corefx/issues/6083 - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var originalTime = File.GetLastWriteTime(originalFile); - File.SetLastWriteTime(newFile, originalTime); - } - } } } diff --git a/src/Microsoft.DotNet.TestFramework/project.json b/src/Microsoft.DotNet.TestFramework/project.json index d6cb24b7f..2451e2a4f 100644 --- a/src/Microsoft.DotNet.TestFramework/project.json +++ b/src/Microsoft.DotNet.TestFramework/project.json @@ -4,6 +4,9 @@ "tags": [ "" ], + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, "projectUrl": "", "licenseUrl": "", "dependencies": { @@ -14,6 +17,7 @@ "imports": [ "portable-net45+wp80+win8+wpa81+dnxcore50" ] - } + }, + "net451": { } } } diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs index 17da3df6a..b427bd0de 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/BuildCommand.cs @@ -266,7 +266,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities public string GetExecutableExtension() { +#if NET451 + return ".exe"; +#else return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""; +#endif } private string BuildArgs() diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs index 54d7d8050..a16de4d69 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/PublishCommand.cs @@ -104,7 +104,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities public string GetExecutableExtension() { +#if NET451 + return ".exe"; +#else return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""; +#endif } private string BuildArgs() diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs index 094c36a85..deb82052e 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs @@ -14,6 +14,8 @@ namespace Microsoft.DotNet.Tools.Test.Utilities public class TestCommand { protected string _command; + + private string _baseDirectory; public string WorkingDirectory { get; set; } @@ -24,6 +26,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities public TestCommand(string command) { _command = command; +#if NET451 + _baseDirectory = AppDomain.CurrentDomain.BaseDirectory; +#else + _baseDirectory = AppContext.BaseDirectory; +#endif } public virtual CommandResult Execute(string args = "") @@ -63,7 +70,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities var command = _command; ResolveCommand(ref command, ref args); var commandPath = Env.GetCommandPath(command, ".exe", ".cmd", "") ?? - Env.GetCommandPathFromRootPath(AppContext.BaseDirectory, command, ".exe", ".cmd", ""); + Env.GetCommandPathFromRootPath(_baseDirectory, command, ".exe", ".cmd", ""); Console.WriteLine($"Executing (Captured Output) - {commandPath} {args}"); @@ -102,7 +109,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities if (!Path.IsPathRooted(executable)) { executable = Env.GetCommandPath(executable) ?? - Env.GetCommandPathFromRootPath(AppContext.BaseDirectory, executable); + Env.GetCommandPathFromRootPath(_baseDirectory, executable); } } @@ -153,12 +160,17 @@ namespace Microsoft.DotNet.Tools.Test.Utilities Arguments = args, RedirectStandardError = true, RedirectStandardOutput = true, - RedirectStandardInput = true + RedirectStandardInput = true, + UseShellExecute = false }; foreach (var item in Environment) { +#if NET451 + psi.EnvironmentVariables[item.Key] = item.Value; +#else psi.Environment[item.Key] = item.Value; +#endif } if (!string.IsNullOrWhiteSpace(WorkingDirectory)) diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/ProcessExtensions.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/ProcessExtensions.cs index 1f0dec647..c82fd7a06 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/ProcessExtensions.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/ProcessExtensions.cs @@ -8,7 +8,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities { internal static class ProcessExtensions { +#if NET451 + private static readonly bool _isWindows = true; +#else private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(30); public static void KillTree(this Process process) diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs index 38c75693d..da89f3f96 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs @@ -33,7 +33,11 @@ namespace Microsoft.DotNet.Tools.Test.Utilities return s_repoRoot; } - string directory = AppContext.BaseDirectory; +#if NET451 + string directory = AppDomain.CurrentDomain.BaseDirectory; +#else + string directory = AppContext.BaseDirectory; +#endif while (!Directory.Exists(Path.Combine(directory, ".git")) && directory != null) { diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json b/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json index c36f2e0b9..c4054ee83 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/project.json @@ -5,13 +5,6 @@ "keyFile": "../../tools/Key.snk" }, "dependencies": { - "Microsoft.NETCore.App": { - "type": "platform", - "version": "1.0.0-rc2-*" - }, - "System.Runtime.Serialization.Primitives": "4.1.1-rc2-24022", - "System.Collections.Immutable": "1.2.0-rc2-24022", - "System.Net.NetworkInformation": "4.1.0-rc2-24022", "FluentAssertions": "4.0.0", "xunit": "2.1.0", "dotnet-test-xunit": "1.0.0-rc2-162081-13", @@ -27,8 +20,22 @@ "netcoreapp1.0": { "imports": [ "dotnet5.4", - "portable-net451+win8" - ] + "portable-net451+win8" + ], + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0-rc2-*" + }, + "System.Runtime.Serialization.Primitives": "4.1.1-rc2-24022", + "System.Collections.Immutable": "1.2.0-rc2-24022", + "System.Net.NetworkInformation": "4.1.0-rc2-24022" + }, + "net451": { + "frameworkAssemblies": { + "System.Runtime": { + "type": "build" + } + } } } } diff --git a/test/binding-redirects.Tests/BindingRedirectTests.cs b/test/binding-redirects.Tests/BindingRedirectTests.cs new file mode 100644 index 000000000..7e5175e99 --- /dev/null +++ b/test/binding-redirects.Tests/BindingRedirectTests.cs @@ -0,0 +1,287 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Xml.Linq; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Test.Utilities; +using Microsoft.DotNet.TestFramework; +using Microsoft.Extensions.PlatformAbstractions; +using Xunit; +using FluentAssertions; + +namespace Microsoft.DotNet.Tests +{ + public class TestSetupFixture : TestBase + { + private const string Framework = "net451"; + private const string Config = "Debug"; + private const string AppWithConfig = "AppWithRedirectsAndConfig"; + private const string AppWithoutConfig = "AppWithRedirectsNoConfig"; + + private string _Runtime = PlatformServices.Default.Runtime.GetLegacyRestoreRuntimeIdentifier(); + private string _desktopProjectsRoot = Path.Combine(RepoRoot, "TestAssets", "DesktopTestProjects"); + private string _buildRelativePath; + private string _appWithConfigBuildDir; + private string _appWithConfigPublishDir; + private string _appWithoutConfigBuildDir; + private string _appWithoutConfigPublishDir; + private TestInstance _testInstance; + + + public string AppWithConfigBuildOutput { get; } + public string AppWithConfigPublishOutput { get; } + public string AppWithoutConfigBuildOutput { get; } + public string AppWithoutConfigPublishOutput { get; } + + public TestSetupFixture() + { + _buildRelativePath = Path.Combine("bin", Config, Framework, _Runtime); + var testAssetsMgr = new TestAssetsManager(_desktopProjectsRoot); + _testInstance = testAssetsMgr.CreateTestInstance("BindingRedirectSample") + .WithLockFiles(); + + Setup(AppWithConfig, ref _appWithConfigBuildDir, ref _appWithConfigPublishDir); + Setup(AppWithoutConfig, ref _appWithoutConfigBuildDir, ref _appWithoutConfigPublishDir); + + AppWithConfigBuildOutput = Path.Combine(_appWithConfigBuildDir, AppWithConfig + ".exe"); + AppWithConfigPublishOutput = Path.Combine(_appWithConfigPublishDir, AppWithConfig + ".exe"); + AppWithoutConfigBuildOutput = Path.Combine(_appWithoutConfigBuildDir, AppWithoutConfig + ".exe"); + AppWithoutConfigPublishOutput = Path.Combine(_appWithoutConfigPublishDir, AppWithoutConfig + ".exe"); + } + + private void Setup(string project, ref string buildDir, ref string publishDir) + { + string projectRoot = Path.Combine(_testInstance.TestRoot, project); + buildDir = Path.Combine(projectRoot, _buildRelativePath); + publishDir = Path.Combine(projectRoot, "publish"); + + var buildCommand = new BuildCommand(projectRoot, framework: Framework, runtime: _Runtime); + buildCommand.Execute().Should().Pass(); + + var publishCommand = new PublishCommand(projectRoot, output: publishDir, framework: Framework, runtime: _Runtime); + publishCommand.Execute().Should().Pass(); + } + } + + public class GivenAnAppWithRedirectsAndExecutableDependency : TestBase, IClassFixture + { + private const string ExecutableDependency = "dotnet-desktop-binding-redirects.exe"; + private TestSetupFixture _testSetup; + private string _appWithConfigBuildOutput; + private string _appWithoutConfigBuildOutput; + private string _appWithConfigPublishOutput; + private string _appWithoutConfigPublishOutput; + private string _executableDependencyBuildOutput; + private string _executableDependencyPublishOutput; + + public GivenAnAppWithRedirectsAndExecutableDependency(TestSetupFixture testSetup) + { + _testSetup = testSetup; + _appWithConfigBuildOutput = _testSetup.AppWithConfigBuildOutput; + _appWithConfigPublishOutput = _testSetup.AppWithConfigPublishOutput; + _appWithoutConfigBuildOutput = _testSetup.AppWithoutConfigBuildOutput; + _appWithoutConfigPublishOutput = _testSetup.AppWithoutConfigPublishOutput; + _executableDependencyBuildOutput = Path.Combine(Path.GetDirectoryName(_appWithConfigBuildOutput), ExecutableDependency); + _executableDependencyPublishOutput = Path.Combine(Path.GetDirectoryName(_appWithConfigPublishOutput), ExecutableDependency); + } + + private static List BindingsAppNoConfig + { + get + { + List bindings = new List() + { + @" + + + + ", + @" + + + " + }; + + return bindings; + } + } + + private static List BindingsAppWithConfig + { + get + { + List bindings = new List() + { + @" + + + + + ", + @" + + + ", + @" + + + " + }; + + return bindings; + } + } + + private static List ExpectedBindingsAppNoConfig + { + get + { + List bindingElements = new List(); + + foreach (var binding in BindingsAppNoConfig) + { + bindingElements.Add(XElement.Parse(binding)); + } + + return bindingElements; + } + } + + private static List ExpectedBindingsAppWithConfig + { + get + { + List bindingElements = new List(); + + foreach (var binding in BindingsAppWithConfig) + { + bindingElements.Add(XElement.Parse(binding)); + } + + return bindingElements; + } + } + + private static Dictionary ExpectedAppSettings + { + get + { + Dictionary appSettings = new Dictionary() + { + {"Setting1", "Hello"}, + {"Setting2", "World"} + }; + + return appSettings; + } + } + + private IEnumerable GetRedirects(string exePath) + { + var configFile = exePath + ".config"; + File.Exists(configFile).Should().BeTrue($"Config file not found - {configFile}"); + var config = ConfigurationManager.OpenExeConfiguration(exePath); + var runtimeSectionXml = config.Sections["runtime"].SectionInformation.GetRawXml(); + var runtimeSectionElement = XElement.Parse(runtimeSectionXml); + var redirects = runtimeSectionElement.Elements() + .Where(e => e.Name.LocalName == "assemblyBinding").Elements() + .Where(e => e.Name.LocalName == "dependentAssembly"); + return redirects; + } + + private void VerifyRedirects(IEnumerable redirects, IEnumerable generatedBindings) + { + foreach (var binding in generatedBindings) + { + var redirect = redirects.SingleOrDefault(r => /*XNode.DeepEquals(r, binding)*/ r.ToString() == binding.ToString()); + + redirect.Should().NotBeNull($"Binding not found in runtime section : {Environment.NewLine}{binding}"); + } + } + + private void VerifyAppSettings(string exePath) + { + var configFile = ConfigurationManager.OpenExeConfiguration(exePath); + foreach (var appSetting in ExpectedAppSettings) + { + var value = configFile.AppSettings.Settings[appSetting.Key]; + value.Should().NotBeNull($"AppSetting with key '{appSetting.Key}' not found in config file."); + value.Value.Should().Be(appSetting.Value, $"For AppSetting '{appSetting.Key}' - Expected Value '{appSetting.Value}', Actual '{ value.Value}'"); + } + } + + [WindowsOnlyFact] + public void Build_Generates_Redirects_For_App_Without_Config() + { + var redirects = GetRedirects(_appWithoutConfigBuildOutput); + VerifyRedirects(redirects, ExpectedBindingsAppNoConfig); + + var commandResult = new TestCommand(_appWithoutConfigBuildOutput) + .Execute(); + commandResult.Should().Pass(); + } + + [WindowsOnlyFact] + public void Publish_Generates_Redirects_For_App_Without_Config() + { + var redirects = GetRedirects(_appWithoutConfigPublishOutput); + VerifyRedirects(redirects, ExpectedBindingsAppNoConfig); + + var commandResult = new TestCommand(_appWithoutConfigPublishOutput) + .Execute(); + commandResult.Should().Pass(); + } + + [WindowsOnlyFact] + public void Build_Generates_Redirects_For_Executable_Dependency() + { + var redirects = GetRedirects(_executableDependencyBuildOutput); + VerifyRedirects(redirects, ExpectedBindingsAppNoConfig); + + var commandResult = new TestCommand(_executableDependencyBuildOutput) + .Execute(); + commandResult.Should().Pass(); + } + + //[WindowsOnlyFact] + public void Publish_Generates_Redirects_For_Executable_Dependency() + { + var redirects = GetRedirects(_executableDependencyPublishOutput); + VerifyRedirects(redirects, ExpectedBindingsAppNoConfig); + + var commandResult = new TestCommand(_executableDependencyPublishOutput) + .Execute(); + commandResult.Should().Pass(); + } + + [WindowsOnlyFact] + public void Build_Generates_Redirects_For_App_With_Config() + { + var redirects = GetRedirects(_appWithConfigBuildOutput); + VerifyRedirects(redirects, ExpectedBindingsAppWithConfig); + VerifyAppSettings(_appWithConfigBuildOutput); + + var commandResult = new TestCommand(_appWithConfigBuildOutput) + .Execute(); + commandResult.Should().Pass(); + } + + [WindowsOnlyFact] + public void Publish_Generates_Redirects_For_App_With_Config() + { + var redirects = GetRedirects(_appWithConfigPublishOutput); + VerifyRedirects(redirects, ExpectedBindingsAppWithConfig); + VerifyAppSettings(_appWithConfigPublishOutput); + + var commandResult = new TestCommand(_appWithConfigPublishOutput) + .Execute(); + commandResult.Should().Pass(); + } + } +} diff --git a/test/binding-redirects.Tests/binding-redirects.Tests.xproj b/test/binding-redirects.Tests/binding-redirects.Tests.xproj new file mode 100644 index 000000000..f789a017e --- /dev/null +++ b/test/binding-redirects.Tests/binding-redirects.Tests.xproj @@ -0,0 +1,21 @@ + + + + 14.0.23107 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 27dbf851-f2e3-4fd5-bf4d-a73c81933283 + binding-redirects.Tests + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/binding-redirects.Tests/project.json b/test/binding-redirects.Tests/project.json new file mode 100644 index 000000000..205b297b5 --- /dev/null +++ b/test/binding-redirects.Tests/project.json @@ -0,0 +1,23 @@ +{ + "version": "1.0.0-*", + "dependencies": { + "xunit": "2.1.0", + "dotnet-test-xunit": "1.0.0-rc2-157751-46", + "Microsoft.NETCore.Platforms": "1.0.1-*", + "Microsoft.DotNet.Tools.Tests.Utilities": { + "target": "project" + } + }, + "frameworks": { + "net451": { + "frameworkAssemblies": { + "System.Configuration": "" + } + } + }, + "runtimes": { + "win7-x64": {}, + "win7-x86": {} + }, + "testRunner": "xunit" +}