public void AppHostShowsErrorWhenGivenSubcommandWasNotFoundInProjectJson(string flavor, string os, string architecture) { var runtimeHomeDir = TestUtils.GetRuntimeHomeDir(flavor, os, architecture); var projectStructure = @"{ 'project.json': '{ }' }"; using (runtimeHomeDir) using (var projectPath = TestUtils.CreateTempDir()) { DirTree.CreateFromJson(projectStructure).WriteTo(projectPath); string stdOut, stdErr; var exitCode = BootstrapperTestUtils.ExecBootstrapper( runtimeHomeDir, arguments: $"{projectPath} invalid", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { { EnvironmentNames.AppBase, projectPath } }); Assert.NotEqual(0, exitCode); Assert.Contains("Unable to load application or execute command 'invalid'.", stdErr); } }
public void ProjectResolverChecksProjectFileForDisambiguation() { const string projectName = "ProjectA"; var solutionStructure = @"{ 'global.json': '', 'src1': { 'ProjectA': { 'project.json': '{}' } }, 'src2': { 'ProjectA': { 'file.txt': 'Not a project.json' } } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src1"", ""src2""] }") .WriteTo(solutionPath); var projectPath = Path.Combine(solutionPath, "src1", projectName); Runtime.Project project; Assert.True(new ProjectResolver(projectPath).TryResolveProject(projectName, out project)); Assert.NotNull(project); } }
public void KCommandShowsErrorWhenGivenSubcommandWasNotFoundInProjectJson(DisposableDir runtimeHomeDir) { var projectStructure = @"{ 'project.json': '{ }' }"; using (runtimeHomeDir) using (var projectPath = TestUtils.CreateTempDir()) { DirTree.CreateFromJson(projectStructure).WriteTo(projectPath); string stdOut, stdErr; var exitCode = KCommandTestUtils.ExecKCommand( runtimeHomeDir, subcommand: "invalid", arguments: string.Empty, stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { { EnvironmentNames.AppBase, projectPath } }); Assert.NotEqual(0, exitCode); Assert.Contains("Unable to load application or execute command 'invalid'.", stdErr); } }
public void ProjectResolverDoesNotThrowWhenThereAreDuplicatedEntriesInGlobalJson() { const string unambiguousName = "ProjectA"; var solutionStructure = @"{ 'global.json': '', 'src': { 'ProjectA': { 'project.json': '{}' } } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src"", ""src/../src"", ""somedir\\somesubdir\\..\\..\\src""] }") .WriteTo(solutionPath); var unambiguousProjectPath = Path.Combine(solutionPath, "src", unambiguousName); Runtime.Project project; Assert.True(new ProjectResolver(unambiguousProjectPath).TryResolveProject(unambiguousName, out project)); Assert.NotNull(project); } }
public void CanSpecifyProjectDirectoryInGlobalJson() { var solutionStructure = @"{ 'global.json': '', 'src': { 'ProjectA': { 'project.json': '{}' } }, 'ProjectB': { 'project.json': '{}' } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src"", ""ProjectB""] }") .WriteTo(solutionPath); var resolutionRoot = Path.Combine(solutionPath, "src", "ProjectA"); Runtime.Project project; Assert.True(new ProjectResolver(resolutionRoot).TryResolveProject("ProjectB", out project)); Assert.NotNull(project); } }
public void ProjectResolverWorksWithMultipleNonProjectFoldersThatHaveSameName() { const string projectName = "ProjectA"; var solutionStructure = @"{ 'global.json': '', 'src1': { 'ProjectA': { 'file.txt': 'Not a project.json' } }, 'src2': { 'ProjectA': { 'file.txt': 'Not a project.json' } } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src1"", ""src2""] }") .WriteTo(solutionPath); var projectPath = Path.Combine(solutionPath, "src1", projectName); Runtime.Project project; Assert.False(new ProjectResolver(projectPath).TryResolveProject(projectName, out project)); Assert.Null(project); } }
public void AppHostShowsErrorWhenGivenSubcommandWasNotFoundInProjectJson(string flavor, string os, string architecture) { var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); var projectStructure = @"{ ""project.json"": ""{ }"" }"; using (var projectPath = new DisposableDir()) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", @" { ""frameworks"": { ""dnx451"": { }, ""dnxcore50"": { } } }").WriteTo(projectPath); string stdOut, stdErr; var exitCode = TestUtils.ExecBootstrapper( runtimeHomeDir, arguments: "invalid", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { }, workingDir: projectPath); Assert.Equal(1, exitCode); Assert.Contains("Unable to load application or execute command 'invalid'.", stdErr); } }
public void BootstrapperLaunchesRequestedFramworkVersionIfOptionProvided(string flavor, string os, string architecture, string requestedFramework, string expectedOutput) { const string projectStructure = @"{ ""project.json"": {}, ""project.lock.json"": {}, ""Program.cs"": {} }"; const string projectJson = @"{ ""dependencies"": { }, ""frameworks"": { ""dnx46"": {}, ""dnx452"": {}, ""dnx451"": {} } }"; const string lockFile = @"{ ""locked"": false, ""version"": 1, ""targets"": { ""DNX,Version=v4.5.1"": {} ""DNX,Version=v4.5.2"": {} ""DNX,Version=v4.6"": {} }, ""libraries"": {}, ""projectFileDependencyGroups"": { """": [], ""DNX,Version=v4.5.1"": [] ""DNX,Version=v4.5.2"": [] ""DNX,Version=v4.6"": [] } }"; var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); using (var tempDir = TestUtils.CreateTempDir()) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", projectJson) .WithFileContents("project.lock.json", lockFile) .WithFileContents("Program.cs", ClrVersionTestProgram) .WriteTo(tempDir); string stdOut; string stdErr; var exitCode = TestUtils.ExecBootstrapper( runtimeHomeDir, arguments: $"--framework {requestedFramework} run", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { { EnvironmentNames.Trace, null } }, workingDir: tempDir); Assert.Equal(0, exitCode); Assert.Equal(expectedOutput, stdOut.Trim()); } }
public void BootstrapperLaunches46ClrIfDnx46IsHighestVersionInProject(string flavor, string os, string architecture) { const string projectStructure = @"{ ""project.json"": {}, ""project.lock.json"": {}, ""Program.cs"": {} }"; const string projectJson = @"{ ""dependencies"": { }, ""frameworks"": { ""dnx46"": { }, ""dnx451"": { } } }"; const string lockFile = @"{ ""locked"": false, ""version"": 2, ""targets"": { ""DNX,Version=v4.6"": {}, ""DNX,Version=v4.5.1"": {} }, ""libraries"": {}, ""projectFileDependencyGroups"": { """": [], ""DNX,Version=v4.6"": [], ""DNX,Version=v4.5.1"": [] } }"; var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); using (var tempDir = new DisposableDir()) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", projectJson) .WithFileContents("project.lock.json", lockFile) .WithFileContents("Program.cs", ClrVersionTestProgram) .WriteTo(tempDir); string stdOut; string stdErr; var exitCode = TestUtils.ExecBootstrapper( runtimeHomeDir, arguments: "-p . run", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { { EnvironmentNames.Trace, null } }, workingDir: tempDir); Assert.Equal(0, exitCode); Assert.Contains("40600", stdOut.Trim()); } }
public void BootstrapperConfiguresAppConfigFile(string flavor, string os, string architecture) { const string projectStructure = @"{ ""project.json"": {}, ""project.lock.json"": {}, ""app.config"": {}, ""Program.cs"": {} }"; const string projectJson = @" { ""frameworks"": { ""dnx451"": { ""frameworkAssemblies"": { ""System.Configuration"": """" } } } }"; const string appConfig = @"<configuration> <appSettings> <add key=""TheSetting"" value=""TheValue"" /> </appSettings> </configuration>"; var runtimeHomeDir = _fixture.GetRuntimeHomeDir(flavor, os, architecture); using (var tempDir = TestUtils.CreateTempDir()) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", projectJson) .WithFileContents("app.config", appConfig) .WithFileContents("Program.cs", AppSettingTestProgram) .WriteTo(tempDir); string stdOut; string stdErr; var exitCode = TestUtils.ExecBootstrapper( runtimeHomeDir, arguments: $"run", stdOut: out stdOut, stdErr: out stdErr, environment: new Dictionary <string, string> { { EnvironmentNames.Trace, null } }, workingDir: tempDir); Assert.Equal(0, exitCode); Assert.Equal("TheSetting=TheValue", stdOut.Trim()); } }
private void RunAdditionalFilesTest(string flavor, string os, string architecture, string dirTree, string projectJson, string[] expectedOutput, bool shouldFail = false) { var runtimeHomeDir = TestUtils.GetRuntimeHomeDir(flavor, os, architecture); using (var testEnv = new DnuTestEnvironment(runtimeHomeDir)) { int exitCode; string stdOut; string stdErr; DirTree.CreateFromJson(dirTree) .WithFileContents("project.json", projectJson) .WriteTo(testEnv.ProjectPath); DnuTestUtils.ExecDnu(runtimeHomeDir, "restore", "", workingDir: testEnv.RootDir); exitCode = DnuTestUtils.ExecDnu(runtimeHomeDir, "pack", $"{testEnv.ProjectPath} --out {testEnv.PublishOutputDirPath}", out stdOut, out stdErr, workingDir: testEnv.RootDir); var packageOutputPath = Path.Combine(testEnv.PublishOutputDirPath, "Debug", $"{testEnv.ProjectName}.1.0.0.nupkg"); // Check it if (shouldFail) { Assert.NotEqual(0, exitCode); foreach (var message in expectedOutput) { Assert.Contains( message.Replace("PROJECTJSONPATH", Path.Combine(testEnv.ProjectPath, "project.json")), stdErr.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)); } Assert.False(File.Exists(packageOutputPath)); } else { Assert.True(File.Exists(packageOutputPath)); string[] entries; using (var archive = ZipFile.OpenRead(packageOutputPath)) { entries = archive.Entries.Select(e => e.FullName).Where(IsNotOpcMetadata).ToArray(); } Assert.Equal(0, exitCode); Assert.Equal(expectedOutput, entries); } } }
public void ProjectResolverThrowsWhenResolvingAmbiguousName() { const string ambiguousName = "ProjectA"; var solutionStructure = @"{ 'global.json': '', 'src1': { 'ProjectA': { 'project.json': '{}' } }, 'src2': { 'ProjectA': { 'project.json': '{}' } } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src1"", ""src2""] }") .WriteTo(solutionPath); var src1ProjectPath = Path.Combine(solutionPath, "src1", ambiguousName); var src2ProjectPath = Path.Combine(solutionPath, "src2", ambiguousName); var expectedMessage = $@"The project name '{ambiguousName}' is ambiguous between the following projects: {src1ProjectPath} {src2ProjectPath}"; Runtime.Project project = null; var resolver1 = new ProjectResolver(src1ProjectPath); var exception = Assert.Throws <InvalidOperationException>(() => resolver1.TryResolveProject(ambiguousName, out project)); Assert.Contains(expectedMessage, exception.Message); Assert.Null(project); var resolver2 = new ProjectResolver(src2ProjectPath); exception = Assert.Throws <InvalidOperationException>(() => resolver2.TryResolveProject(ambiguousName, out project)); Assert.Contains(expectedMessage, exception.Message); Assert.Null(project); } }
public void ProjectResolverDoesNotThrowWhenAmbiguousNameIsNotUsed() { const string ambiguousName = "ProjectA"; const string unambiguousName = "ProjectB"; var solutionStructure = @"{ 'global.json': '', 'src1': { 'ProjectA': { 'project.json': '{}' }, 'ProjectB': { 'project.json': '{}' } }, 'src2': { 'ProjectA': { 'project.json': '{}' } } }"; using (var solutionPath = new DisposableDir()) { DirTree.CreateFromJson(solutionStructure) .WithFileContents("global.json", @"{ ""projects"": [""src1"", ""src2""] }") .WriteTo(solutionPath); var ambiguousProjectPath = Path.Combine(solutionPath, "src1", ambiguousName); var unambiguousProjectPath = Path.Combine(solutionPath, "src1", unambiguousName); Runtime.Project project; Assert.True(new ProjectResolver(ambiguousProjectPath).TryResolveProject(unambiguousName, out project)); Assert.NotNull(project); project = null; Assert.True(new ProjectResolver(unambiguousProjectPath).TryResolveProject(unambiguousName, out project)); Assert.NotNull(project); } }
public void DnuPack_NormalizesVersionNumberWithNoBuildNumber(string flavor, string os, string architecture) { int exitCode; var projectName = "TestProject"; var projectStructure = @"{ '.': ['project.json'] }"; var runtimeHomeDir = TestUtils.GetRuntimeHomeDir(flavor, os, architecture); using (var testEnv = new DnuTestEnvironment(runtimeHomeDir, projectName)) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", @"{ ""version"": ""1.0-beta"", ""frameworks"": { ""dnx451"": {} } }") .WriteTo(testEnv.ProjectPath); exitCode = DnuTestUtils.ExecDnu( runtimeHomeDir, subcommand: "restore", arguments: string.Empty, workingDir: testEnv.ProjectPath); Assert.Equal(0, exitCode); exitCode = DnuTestUtils.ExecDnu( runtimeHomeDir, subcommand: "pack", arguments: string.Empty, workingDir: testEnv.ProjectPath); Assert.Equal(0, exitCode); Assert.True(File.Exists(Path.Combine(testEnv.ProjectPath, "bin", "Debug", $"{projectName}.1.0.0-beta.nupkg"))); Assert.True(File.Exists(Path.Combine(testEnv.ProjectPath, "bin", "Debug", $"{projectName}.1.0.0-beta.symbols.nupkg"))); } }
public void DnuRestore_ExecutesScripts(string flavor, string os, string architecture) { bool isWindows = TestUtils.CurrentRuntimeEnvironment.OperatingSystem == "Windows"; var environment = new Dictionary <string, string> { { "DNX_TRACE", "0" }, }; var expectedPreContent = @"""one"" ""two"" "">three"" ""four"" "; var expectedPostContent = @"""five"" ""six"" ""argument seven"" ""argument eight"" "; string projectJsonContent; string scriptContent; string scriptName; if (isWindows) { projectJsonContent = @"{ ""frameworks"": { ""dnx451"": { } }, ""scripts"": { ""prerestore"": [ ""script.cmd one two > pre"", ""script.cmd ^>three >> pre && script.cmd ^ four >> pre"" ], ""postrestore"": [ ""\""%project:Directory%/script.cmd\"" five six > post"", ""\""%project:Directory%/script.cmd\"" \""argument seven\"" \""argument eight\"" >> post"" ] } }"; scriptContent = @"@echo off :argumentStart if ""%~1""=="""" goto argumentEnd echo ""%~1"" shift goto argumentStart :argumentEnd"; scriptName = "script.cmd"; } else { projectJsonContent = @"{ ""frameworks"": { ""dnx451"": { } }, ""scripts"": { ""prerestore"": [ ""script one two > pre"", ""script.sh \\>three >> pre; ./script.sh four >> pre"" ], ""postrestore"": [ ""\""%project:Directory%/script\"" five six > post"", ""\""%project:Directory%/script.sh\"" \""argument seven\"" \""argument eight\"" >> post"" ] } }"; scriptContent = @"#!/usr/bin/env bash set -o errexit for arg in ""$@""; do printf ""\""%s\""\n"" ""$arg"" done"; scriptName = "script.sh"; } var projectStructure = $@"{{ '.': ['project.json', '{ scriptName }'] }}"; var runtimeHomePath = _fixture.GetRuntimeHomeDir(flavor, os, architecture); using (var testEnv = new DnuTestEnvironment(runtimeHomePath, projectName: "Project Name")) { var projectPath = testEnv.ProjectPath; DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", projectJsonContent) .WithFileContents(scriptName, scriptContent) .WriteTo(projectPath); FileOperationUtils.MarkExecutable(Path.Combine(projectPath, scriptName)); string output; string error; var exitCode = DnuTestUtils.ExecDnu( runtimeHomePath, subcommand: "restore", arguments: null, stdOut: out output, stdErr: out error, environment: environment, workingDir: projectPath); Assert.Equal(0, exitCode); Assert.Empty(error); Assert.Contains("Executing script 'prerestore' in project.json", output); Assert.Contains("Executing script 'postrestore' in project.json", output); var preContent = File.ReadAllText(Path.Combine(projectPath, "pre")); Assert.Equal(expectedPreContent, preContent); var postContent = File.ReadAllText(Path.Combine(projectPath, "post")); Assert.Equal(expectedPostContent, postContent); } }
public void DnuPack_ExecutesScriptsForEachConfigurationAndTargetFramework(string flavor, string os, string architecture) { var projectStructure = @"{ '.': ['project.json'] }"; var runtimeHomeDir = TestUtils.GetRuntimeHomeDir(flavor, os, architecture); using (var testEnv = new DnuTestEnvironment(runtimeHomeDir, "TestProject")) { DirTree.CreateFromJson(projectStructure) .WithFileContents("project.json", @"{ ""version"": ""1.0-beta"", ""frameworks"": { ""dnx451"": {}, ""dnxcore50"": { ""dependencies"": { ""System.Runtime"":""4.0.20-*"" } } }, ""scripts"": { ""prebuild"": ""echo PREBUILD_SCRIPT_OUTPUT %build:Configuration% %build:TargetFramework%"", ""prepack"": ""echo PREPACK_SCRIPT_OUTPUT %build:Configuration% %build:TargetFramework%"", ""postbuild"": ""echo POSTBUILD_SCRIPT_OUTPUT %build:Configuration% %build:TargetFramework%"", ""postpack"": ""echo POSTPACK_SCRIPT_OUTPUT %build:Configuration% %build:TargetFramework%"" } }") .WriteTo(testEnv.ProjectPath); string stdOut, stdErr; var exitCode = DnuTestUtils.ExecDnu( runtimeHomeDir, subcommand: "restore", arguments: string.Empty, workingDir: testEnv.ProjectPath); Assert.Equal(0, exitCode); exitCode = DnuTestUtils.ExecDnu( runtimeHomeDir, "pack", testEnv.ProjectPath + " --configuration Debug --configuration Release", out stdOut, out stdErr); Assert.Equal(0, exitCode); Assert.Empty(stdErr); var idx = 0; foreach (var configuration in new[] { "Debug", "Release" }) { // note that %TargetFramework% is not defined outside build idx = stdOut.IndexOf($"PREPACK_SCRIPT_OUTPUT {configuration} %build:TargetFramework%", 0); Assert.True(idx >= 0); foreach (var framework in new[] { "dnx451", "dnxcore50" }) { idx = stdOut.IndexOf($"PREBUILD_SCRIPT_OUTPUT {configuration} {framework}", idx); Assert.True(idx >= 0); idx = stdOut.IndexOf($"POSTBUILD_SCRIPT_OUTPUT {configuration} {framework}", idx); Assert.True(idx >= 0); } idx = stdOut.IndexOf($"POSTPACK_SCRIPT_OUTPUT {configuration} %build:TargetFramework%", 0); Assert.True(idx >= 0); } Assert.Equal(-1, stdOut.IndexOf("PREPACK_SCRIPT_OUTPUT", idx + 1)); Assert.Equal(-1, stdOut.IndexOf("PREBUILD_SCRIPT_OUTPUT", idx + 1)); Assert.Equal(-1, stdOut.IndexOf("POSTBUILD_SCRIPT_OUTPUT", idx + 1)); Assert.Equal(-1, stdOut.IndexOf("POSTPACK_SCRIPT_OUTPUT", idx + 1)); } }
public void AppHost_ExecutesCommands( string flavor, string os, string architecture, string command, string expectedOutput) { var environment = new Dictionary <string, string> { { "DNX_TRACE", "0" }, }; var projectName = "Project Name"; var projectStructure = $@"{{ '.': ['Program.cs', '{ Project.ProjectFileName }'] }}"; var programContents = @"using System; namespace Project_Name { public class Program { public void Main(string[] arguments) { for (var i = 0; i < arguments.Length; i++) { var argument = arguments[i]; if (!string.IsNullOrWhiteSpace(argument)) { Console.WriteLine($""{ i }: '{ argument }'""); } } } } }"; var projectJsonContents = $@"{{ ""commands"": {{ ""one"": ""\""{ projectName }\"" one two"", ""two"": ""\""{ projectName }\"" ^>three &&>>^\"""", ""three"": ""\""{ projectName }\"" four \""argument five\"""" }}, ""frameworks"" : {{ ""dnx451"": {{ }} }} }}"; using (var applicationRoot = new DisposableDir()) { var projectPath = Path.Combine(applicationRoot, projectName); DirTree.CreateFromJson(projectStructure) .WithFileContents("Program.cs", programContents) .WithFileContents(Project.ProjectFileName, projectJsonContents) .WriteTo(projectPath); var runtimeHomePath = _fixture.GetRuntimeHomeDir(flavor, os, architecture); var exitCode = DnuTestUtils.ExecDnu( runtimeHomePath, subcommand: "restore", arguments: null, environment: environment, workingDir: projectPath); Assert.Equal(0, exitCode); // Guard string output; string error; exitCode = TestUtils.ExecBootstrapper( runtimeHomePath, arguments: $@" { command } extra", stdOut: out output, stdErr: out error, environment: environment, workingDir: projectPath); Assert.Equal(0, exitCode); Assert.Empty(error); Assert.Contains(expectedOutput, output); } }
public void DnuFeeds_ListsAllSources(string flavor, string os, string architecture) { var environment = new Dictionary <string, string> { { "DNX_TRACE", "0" }, }; var rootConfig = @"<?xml version=""1.0"" encoding=""utf-8""?> <configuration> <packageSources> <clear /> <!-- Remove the effects of any machine-level config --> <add key=""Source1"" value=""https://source1"" /> <add key=""Source2"" value=""https://source2"" /> </packageSources> </configuration>"; var subConfig = @"<?xml version=""1.0"" encoding=""utf-8""?> <configuration> <packageSources> <add key=""Source3"" value=""https://source3"" /> </packageSources> <disabledPackageSources> <add key=""Source1"" value=""https://source1"" /> </disabledPackageSources> </configuration>"; var projectStructure = @"{ 'root': { 'NuGet.config': """", 'sub': { 'NuGet.config': """" } } }"; var runtimeHomePath = _fixture.GetRuntimeHomeDir(flavor, os, architecture); using (var testEnv = new DnuTestEnvironment(runtimeHomePath, projectName: "Project Name")) { var projectPath = testEnv.ProjectPath; DirTree.CreateFromJson(projectStructure) .WithFileContents("root/nuget.config", rootConfig) .WithFileContents("root/sub/nuget.config", subConfig) .WriteTo(projectPath); string output; string error; var exitCode = DnuTestUtils.ExecDnu( runtimeHomePath, subcommand: "feeds", arguments: "list root/sub", stdOut: out output, stdErr: out error, environment: environment, workingDir: projectPath); Assert.Equal(0, exitCode); Assert.Empty(error); // CI Machines and such have different sources in the user-global config // So we can't actually assert the exact content of the output. Assert.Contains($"https://source1 [Disabled]{Environment.NewLine} Origin: {Path.Combine(projectPath, "root", "nuget.config")}", output); Assert.Contains($"https://source2{Environment.NewLine} Origin: {Path.Combine(projectPath, "root", "nuget.config")}", output); Assert.Contains($"https://source3{Environment.NewLine} Origin: {Path.Combine(projectPath, "root", "sub", "nuget.config")}", output); } }