diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..73a669f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + "command": "dotnet", + "isShellCommand": true, + "args": [], + "tasks": [ + { + "taskName": "build", + "args": [ "test/dotnet-new.Tests/dotnet-new.Tests.csproj" ], + "isBuildCommand": true, + "showOutput": "silent", + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/FSharp.NET.Sdk/FSharp.NET.Sdk.proj b/src/FSharp.NET.Sdk/FSharp.NET.Sdk.proj index 5a2f40a..8a5fe6c 100644 --- a/src/FSharp.NET.Sdk/FSharp.NET.Sdk.proj +++ b/src/FSharp.NET.Sdk/FSharp.NET.Sdk.proj @@ -38,7 +38,7 @@ - + diff --git a/test/dotnet-new.Tests/MSBuildHostTypeAttribute.cs b/test/dotnet-new.Tests/MSBuildHostTypeAttribute.cs new file mode 100644 index 0000000..2cac9be --- /dev/null +++ b/test/dotnet-new.Tests/MSBuildHostTypeAttribute.cs @@ -0,0 +1,44 @@ +using System; +using Xunit; +using static System.Environment; + +namespace NetcoreCliFsc.Tests +{ + [Flags] + public enum MSBuildHostTypesOnly + { + Core = 1, + MSBuild = 2, + Mono = 4, + } + + public class MSBuildHostTypeOnlyFactAttribute : FactAttribute + { + public string Reason { get; set; } + + public MSBuildHostTypeOnlyFactAttribute(MSBuildHostTypesOnly msbuildHostType) + { + if (!TestSuite.MSBuildHostTypesOnly.HasFlag(msbuildHostType)) + { + var hostName = Enum.GetName(typeof(MSBuildHostTypesOnly), msbuildHostType); + this.Skip = $"This test requires msbuild host type {hostName} to run" + + (string.IsNullOrEmpty(Reason)? "" : $". Why? {Reason}"); + } + } + } + + public class MSBuildHostTypeOnlyTheoryAttribute : TheoryAttribute + { + public string Reason { get; set; } + + public MSBuildHostTypeOnlyTheoryAttribute(MSBuildHostTypesOnly msbuildHostType) + { + if (!TestSuite.MSBuildHostTypesOnly.HasFlag(msbuildHostType)) + { + var hostName = Enum.GetName(typeof(MSBuildHostTypesOnly), msbuildHostType); + this.Skip = $"This test requires msbuild host type {hostName} to run" + + (string.IsNullOrEmpty(Reason)? "" : $". Why? {Reason}"); + } + } + } +} diff --git a/test/dotnet-new.Tests/MsbuildCommonScenarioTests.cs b/test/dotnet-new.Tests/MsbuildCommonScenarioTests.cs new file mode 100644 index 0000000..944b223 --- /dev/null +++ b/test/dotnet-new.Tests/MsbuildCommonScenarioTests.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.DotNet.Tools.Test.Utilities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; +using FluentAssertions; +using static System.Environment; +using static NetcoreCliFsc.Tests.MsbuildTestSuite; + +namespace NetcoreCliFsc.Tests +{ + public class MsbuildCommonScenario : TestBase + { + private static string RestoreProps() + { + var props = new Dictionary() + { + { "FSharpNETSdkVersion", GetEnvironmentVariable("TEST_SUITE_FSHARP_NET_SDK_PKG_VERSION")}, + { "FSharpCorePkgVersion", GetEnvironmentVariable("TEST_SUITE_FSHARP_CORE_PKG_VERSION")}, + }; + + return string.Join(" ", props.Where(kv => kv.Value != null).Select(kv => $"/p:{kv.Key}={kv.Value}") ); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestAppWithArgs() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("netcoreapp1.0/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppWithArgs", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + + test("dotnet") + .Execute($"run {LogArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestAppWithArgs451() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("net451/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppWithArgs", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + string rid = GetCurrentRID(); + + test("msbuild") + .Execute($"/t:Restore /p:RuntimeIdentifier={rid} {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build /p:RuntimeIdentifier={rid} {LogArgs}") + .Should().Pass(); + + test(Path.Combine(rootPath, "bin", "Debug", "net451", rid, "ConsoleApp.exe")) + .Execute($"arg1 arg2") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.Mono)] + public void MonoCheck() + { + var rootPath = Temp.CreateDirectory().Path; + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("mono") + .Execute($"--version") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestLibrary() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("TestLibrary", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestXmlDoc() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("TestLibrary", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + Assert.Equal(false, File.Exists(Path.Combine(rootPath, "doc.xml"))); + + test("msbuild") + .Execute($"/t:Build {LogArgs} /p:DocumentationFile=doc.xml") + .Should().Pass(); + + Assert.Equal(true, File.Exists(Path.Combine(rootPath, "doc.xml"))); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestImplicitFrameworkDefines() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("netcoreapp1.0/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppDefines", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + + var result = test("dotnet").ExecuteWithCapturedOutput($"run {LogArgs}"); + + result.Should().Pass(); + + Assert.NotNull(result.StdOut); + Assert.Contains($"TFM: 'NETCOREAPP1_0'", result.StdOut.Trim()); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestImplicitConfigurationDefines() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("netcoreapp1.0/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppDefines", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + var configurations = new Dictionary { + { "release", "RELEASE" }, + { "debug", "DEBUG" }, + }; + + foreach (var kv in configurations) + { + test("msbuild") + .Execute($"/t:Build {LogArgs} /p:Configuration={kv.Key}") + .Should().Pass(); + + var result = test("dotnet").ExecuteWithCapturedOutput($"run -c {kv.Key} {LogArgs}"); + + result.Should().Pass(); + + Assert.NotNull(result.StdOut); + Assert.Contains($"CONF: '{kv.Value}'", result.StdOut.Trim()); + } + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestMultipleLibraryInSameDir() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("TestLibrary", rootPath); + TestAssets.CopyDirTo("TestMultipleLibraryInSameDir", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore TestLibrary.fsproj {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Restore TestLibrary2.fsproj {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build TestLibrary.fsproj {LogArgs}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build TestLibrary2.fsproj {LogArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestLibraryCross() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("TestLibraryCross", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Pack {LogArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestApp() + { + var rootPath = Temp.CreateDirectory().Path; + + foreach (var a in new[] { "TestLibrary", "TestApp" }) + { + var projDir = Path.Combine(rootPath, a); + TestAssets.CopyDirTo(a, projDir); + TestAssets.CopyDirTo("TestSuiteProps", projDir); + } + + var appDir = Path.Combine(rootPath, "TestApp"); + + Func test = name => new TestCommand(name) { WorkingDirectory = appDir }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + + test("dotnet") + .Execute($"run {LogArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestPathWithBlank() + { + var rootPath = Path.Combine(Temp.CreateDirectory().Path, "path with blank"); + + TestAssets.CopyDirTo("TestLibrary", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + } + + private string GetCurrentRID() + { + var rootPath = Temp.CreateDirectory().Path; + + Func test = n => new TestCommand(n) { WorkingDirectory = rootPath }; + + var result = test("dotnet").ExecuteWithCapturedOutput($"--info"); + + result.Should().Pass(); + + var dotnetInfo = result.StdOut; + + string rid = + dotnetInfo + .Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => s.StartsWith("RID:")) + .Select(s => s.Replace("RID:", "").Trim()) + .FirstOrDefault(); + + return rid; + } + + private void CreateNoopExe(string intoDir, string name, bool fail = false) + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("Noop", rootPath); + + Func test = n => new TestCommand(n) { WorkingDirectory = rootPath }; + + string rid = GetCurrentRID(); + string msbuildArgs = $"/p:AssemblyName={name} " + (fail? "/p:Fail=true" : ""); + + test("dotnet") + .Execute($"restore -r {rid} {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()} {msbuildArgs}") + .Should().Pass(); + + test("dotnet") + .Execute($"publish -r {rid} -o \"{intoDir}\" {msbuildArgs}") + .Should().Pass(); + } + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestAppWithRes() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("netcoreapp1.0/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppWithRes", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + test("msbuild") + .Execute($"/t:Restore {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build {LogArgs}") + .Should().Pass(); + + var result = test("dotnet").ExecuteWithCapturedOutput($"run {LogArgs}"); + + result.Should().Pass(); + + Assert.NotNull(result.StdOut); + Assert.Equal("Hi!", result.StdOut.Trim()); + } + + + [MSBuildHostTypeOnlyFact(MSBuildHostTypesOnly.MSBuild)] + public void TestAppWithResNet451() + { + var rootPath = Temp.CreateDirectory().Path; + + TestAssets.CopyDirTo("net451/TestConsoleAppTemplate", rootPath); + TestAssets.CopyDirTo("TestAppWithRes", rootPath); + TestAssets.CopyDirTo("TestSuiteProps", rootPath); + + Func test = name => new TestCommand(name) { WorkingDirectory = rootPath }; + + string rid = GetCurrentRID(); + + test("msbuild") + .Execute($"/t:Restore /p:RuntimeIdentifier={rid} {RestoreDefaultArgs} {RestoreSourcesArgs(NugetConfigSources)} {RestoreProps()}") + .Should().Pass(); + + test("msbuild") + .Execute($"/t:Build /p:RuntimeIdentifier={rid} {LogArgs}") + .Should().Pass(); + + + var result = + test(Path.Combine(rootPath, "bin", "Debug", "net451", rid, "ConsoleApp.exe")) + .ExecuteWithCapturedOutput(""); + + result.Should().Pass(); + + Assert.NotNull(result.StdOut); + Assert.Equal("Hi!", result.StdOut.Trim()); + } + + } +} diff --git a/test/dotnet-new.Tests/MsbuildTestSuite.cs b/test/dotnet-new.Tests/MsbuildTestSuite.cs new file mode 100644 index 0000000..e210142 --- /dev/null +++ b/test/dotnet-new.Tests/MsbuildTestSuite.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.DotNet.Tools.Test.Utilities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; +using FluentAssertions; +using static System.Environment; + +namespace NetcoreCliFsc.Tests +{ + public static class MsbuildTestSuite + { + public static string RestoreSourcesArgs(IEnumerable sources) + { + return "\"/p:RestoreSources=" + string.Join(MsbuildPropertySeparator, sources) + "\""; + } + + private static string MsbuildPropertySeparator = "%3B"; + + public static IEnumerable NugetConfigSources => TestSuite.NugetConfigSources; + + public static string RestoreDefaultArgs => $"/p:RestoreNoCache=true {LogArgs} \"/p:RestorePackagesPath={TestSuite.NugetPackagesDir}\""; + + public static string LogArgs => "/v:n"; + } +} diff --git a/test/dotnet-new.Tests/TestSuite.cs b/test/dotnet-new.Tests/TestSuite.cs index f8f1d0b..728ab9b 100644 --- a/test/dotnet-new.Tests/TestSuite.cs +++ b/test/dotnet-new.Tests/TestSuite.cs @@ -43,7 +43,7 @@ namespace NetcoreCliFsc.Tests } } - private static string NugetPackagesDir => Path.Combine(TestBase.RepoRoot, "test", "packages"); + public static string NugetPackagesDir => Path.Combine(TestBase.RepoRoot, "test", "packages"); public static string RestoreSourcesArgs(IEnumerable sources) { @@ -53,5 +53,23 @@ namespace NetcoreCliFsc.Tests public static string RestoreDefaultArgs => $"--no-cache {LogArgs} --packages \"{NugetPackagesDir}\""; public static string LogArgs => "-v n"; + + public static MSBuildHostTypesOnly MSBuildHostTypesOnly + { + get + { + var msbuildHost = + (GetEnvironmentVariable("TEST_SUITE_MSBUILD_HOST_ONLY") ?? "") + .ToUpper() + .Split(';'); + + var result = MSBuildHostTypesOnly.Core; + if (msbuildHost.Contains("MSBUILD")) + result = result | MSBuildHostTypesOnly.MSBuild; + if (msbuildHost.Contains("MONO")) + result = result | MSBuildHostTypesOnly.Mono; + return result; + } + } } }