public void ContextPinsGlobExpansionCache(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var projectDirectory = _env.DefaultTestDirectory.FolderPath; _env.SetCurrentDirectory(projectDirectory); var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.cs"), ""); EvaluateProjects( _globProjects, context, project => { var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]; evaluationCount++; File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.cs"), ""); ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i")); } ); }
public void ReevaluationShouldNotReuseInitialContext(EvaluationContext.SharingPolicy policy) { try { EvaluationContext.TestOnlyHookOnCreate = c => SetResolverForContext(c, _resolver); var collection = _env.CreateProjectCollection().Collection; var context = EvaluationContext.Create(policy); var project = Project.FromXmlReader( XmlReader.Create(new StringReader("<Project Sdk=\"foo\"></Project>")), new ProjectOptions { ProjectCollection = collection, EvaluationContext = context, LoadSettings = ProjectLoadSettings.IgnoreMissingImports }); _resolver.ResolvedCalls["foo"].ShouldBe(1); project.AddItem("a", "b"); project.ReevaluateIfNecessary(); _resolver.ResolvedCalls["foo"].ShouldBe(2); } finally { EvaluationContext.TestOnlyHookOnCreate = null; } }
public void SharedContextShouldGetReusedWhereasIsolatedContextShouldNot(EvaluationContext.SharingPolicy policy) { var previousContext = EvaluationContext.Create(policy); for (var i = 0; i < 10; i++) { var currentContext = previousContext.ContextForNewProject(); if (i == 0) { currentContext.ShouldBeSameAs(previousContext, "first usage context was not the same as the initial context"); } else { switch (policy) { case EvaluationContext.SharingPolicy.Shared: currentContext.ShouldBeSameAs(previousContext, $"Shared policy: usage {i} was not the same as usage {i - 1}"); break; case EvaluationContext.SharingPolicy.Isolated: currentContext.ShouldNotBeSameAs(previousContext, $"Isolated policy: usage {i} was the same as usage {i - 1}"); break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } } previousContext = currentContext; } }
public void ReevaluationShouldRespectContextLifetime(EvaluationContext.SharingPolicy policy) { var collection = _env.CreateProjectCollection().Collection; var context1 = EvaluationContext.Create(policy); var project = Project.FromXmlReader( XmlReader.Create(new StringReader("<Project></Project>")), new ProjectOptions { ProjectCollection = collection, EvaluationContext = context1, LoadSettings = ProjectLoadSettings.IgnoreMissingImports }); project.AddItem("a", "b"); project.ReevaluateIfNecessary(); var context2 = GetEvaluationContext(project); switch (policy) { case EvaluationContext.SharingPolicy.Shared: context1.ShouldBeSameAs(context2); break; case EvaluationContext.SharingPolicy.Isolated: context1.ShouldNotBeSameAs(context2); break; } }
public void ProjectInstanceShouldRespectSharingPolicy(EvaluationContext.SharingPolicy policy) { try { var seenContexts = new HashSet <EvaluationContext>(); EvaluationContext.TestOnlyHookOnCreate = c => seenContexts.Add(c); var collection = _env.CreateProjectCollection().Collection; var context = EvaluationContext.Create(policy); const int numIterations = 10; for (int i = 0; i < numIterations; i++) { ProjectInstance.FromProjectRootElement( ProjectRootElement.Create(), new ProjectOptions { ProjectCollection = collection, EvaluationContext = context, LoadSettings = ProjectLoadSettings.IgnoreMissingImports }); } int expectedNumContexts = policy == EvaluationContext.SharingPolicy.Shared ? 1 : numIterations; seenContexts.Count.ShouldBe(expectedNumContexts); seenContexts.ShouldAllBe(c => c.Policy == policy); } finally { EvaluationContext.TestOnlyHookOnCreate = null; } }
public void ContextDisambiguatesRelativeGlobsOutsideOfProjectCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var project1Root = _env.DefaultTestDirectory.CreateDirectory("Project1"); var project1Directory = project1Root.CreateDirectory("1").Path; var project1GlobDirectory = project1Root.CreateDirectory("Glob").CreateDirectory("1").Path; var project2Root = _env.DefaultTestDirectory.CreateDirectory("Project2"); var project2Directory = project2Root.CreateDirectory("2").Path; var project2GlobDirectory = project2Root.CreateDirectory("Glob").CreateDirectory("2").Path; var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(project1GlobDirectory, $"1.{evaluationCount}.cs"), ""); File.WriteAllText(Path.Combine(project2GlobDirectory, $"2.{evaluationCount}.cs"), ""); EvaluateProjects( new [] { new ProjectSpecification( Path.Combine(project1Directory, "1"), @"<Project> <ItemGroup> <i Include=`../Glob/**/*.cs` /> </ItemGroup> </Project>"), new ProjectSpecification( Path.Combine(project2Directory, "2"), @"<Project> <ItemGroup> <i Include=`../Glob/**/*.cs` /> </ItemGroup> </Project>") }, context, project => { var projectName = Path.GetFileNameWithoutExtension(project.FullPath); // globs have the fixed directory part prepended, so add it to the expected results var expectedGlobExpansion = expectedGlobExpansions[evaluationCount] .Select(i => Path.Combine("..", "Glob", projectName, $"{projectName}.{i}")) .ToArray(); var actualGlobExpansion = project.GetItems("i"); ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion); evaluationCount++; File.WriteAllText(Path.Combine(project1GlobDirectory, $"1.{evaluationCount}.cs"), ""); File.WriteAllText(Path.Combine(project2GlobDirectory, $"2.{evaluationCount}.cs"), ""); } ); }
public void ContextCachesExistenceChecksInGetDirectoryNameOfFileAbove(EvaluationContext.SharingPolicy policy) { var context = EvaluationContext.Create(policy); var subdirectory = _env.DefaultTestDirectory.CreateDirectory("subDirectory"); var subdirectoryFile = subdirectory.CreateFile("a"); _env.DefaultTestDirectory.CreateFile("a"); int evaluationCount = 0; EvaluateProjects( new [] { $@"<Project> <PropertyGroup> <SearchedPath>$([MSBuild]::GetDirectoryNameOfFileAbove('{subdirectory.Path}', 'a'))</SearchedPath> </PropertyGroup> </Project>" }, context, project => { evaluationCount++; var searchedPath = project.GetProperty("SearchedPath"); switch (policy) { case EvaluationContext.SharingPolicy.Shared: searchedPath.EvaluatedValue.ShouldBe(subdirectory.Path); break; case EvaluationContext.SharingPolicy.Isolated: searchedPath.EvaluatedValue.ShouldBe( evaluationCount == 1 ? subdirectory.Path : _env.DefaultTestDirectory.Path); break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } if (evaluationCount == 1) { // this will cause the upper file to get picked up in the Isolated policy subdirectoryFile.Delete(); } }); evaluationCount.ShouldBe(2); }
public void ContextCachesExistenceChecksInConditions(EvaluationContext.SharingPolicy policy) { var projectDirectory = _env.DefaultTestDirectory.FolderPath; _env.SetCurrentDirectory(projectDirectory); var context = EvaluationContext.Create(policy); var theFile = Path.Combine(projectDirectory, "0.cs"); File.WriteAllText(theFile, ""); var evaluationCount = 0; EvaluateProjects( _projectsWithConditions, context, project => { evaluationCount++; if (File.Exists(theFile)) { File.Delete(theFile); } if (evaluationCount == 1) { project.GetPropertyValue("p").ShouldBe("val"); } else { switch (policy) { case EvaluationContext.SharingPolicy.Shared: project.GetPropertyValue("p").ShouldBe("val"); break; case EvaluationContext.SharingPolicy.Isolated: project.GetPropertyValue("p").ShouldBeEmpty(); break; default: throw new ArgumentOutOfRangeException(nameof(policy), policy, null); } } } ); }
public void ContextDisambiguatesRelativeGlobsUnderProjectCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var projectDirectory1 = _env.DefaultTestDirectory.CreateDirectory("1").Path; var projectDirectory2 = _env.DefaultTestDirectory.CreateDirectory("2").Path; var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(projectDirectory1, $"1.{evaluationCount}.cs"), ""); File.WriteAllText(Path.Combine(projectDirectory2, $"2.{evaluationCount}.cs"), ""); EvaluateProjects( new [] { new ProjectSpecification( Path.Combine(projectDirectory1, "1"), @"<Project> <ItemGroup> <i Include=`**/*.cs` /> </ItemGroup> </Project>"), new ProjectSpecification( Path.Combine(projectDirectory2, "2"), @"<Project> <ItemGroup> <i Include=`**/*.cs` /> </ItemGroup> </Project>"), }, context, project => { var projectName = Path.GetFileNameWithoutExtension(project.FullPath); var expectedGlobExpansion = expectedGlobExpansions[evaluationCount] .Select(i => $"{projectName}.{i}") .ToArray(); ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i")); evaluationCount++; File.WriteAllText(Path.Combine(projectDirectory1, $"1.{evaluationCount}.cs"), ""); File.WriteAllText(Path.Combine(projectDirectory2, $"2.{evaluationCount}.cs"), ""); } ); }
public void ContextSdkResolverIsUsed(EvaluationContext.SharingPolicy policy, int sdkLookupsForFoo, int sdkLookupsForBar) { try { EvaluationContext.TestOnlyAlterStateOnCreate = c => SetResolverForContext(c, _resolver); var context = EvaluationContext.Create(policy); EvaluateProjects(context); _resolver.ResolvedCalls["foo"].ShouldBe(sdkLookupsForFoo); _resolver.ResolvedCalls["bar"].ShouldBe(sdkLookupsForBar); _resolver.ResolvedCalls.Count.ShouldBe(2); } finally { EvaluationContext.TestOnlyAlterStateOnCreate = null; } }
public void ContextPinsSdkResolverCache(EvaluationContext.SharingPolicy policy, int sdkLookupsForFoo, int sdkLookupsForBar) { try { EvaluationContext.TestOnlyHookOnCreate = c => SetResolverForContext(c, _resolver); var context = EvaluationContext.Create(policy); EvaluateProjects(_sdkResolutionProjects, context, null); _resolver.ResolvedCalls.Count.ShouldBe(2); _resolver.ResolvedCalls["foo"].ShouldBe(sdkLookupsForFoo); _resolver.ResolvedCalls["bar"].ShouldBe(sdkLookupsForBar); } finally { EvaluationContext.TestOnlyHookOnCreate = null; } }
public void ContextCachesImportGlobExpansions(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var projectDirectory = _env.DefaultTestDirectory.Path; var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.props"), $"<Project><ItemGroup><i Include=`{evaluationCount}.cs`/></ItemGroup></Project>".Cleanup()); EvaluateProjects( _projectsWithGlobImports, context, project => { var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]; evaluationCount++; File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.props"), $"<Project><ItemGroup><i Include=`{evaluationCount}.cs`/></ItemGroup></Project>".Cleanup()); ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i")); } ); }
private void ContextCachesCommonOutOfProjectCone(bool itemSpecPathIsRelative, EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var testDirectory = _env.DefaultTestDirectory; var globDirectory = testDirectory.CreateDirectory("GlobDirectory"); var itemSpecDirectoryPart = itemSpecPathIsRelative ? Path.Combine("..", "GlobDirectory") : globDirectory.Path; Directory.CreateDirectory(globDirectory.Path); // Globs with a directory part will produce items prepended with that directory part foreach (var globExpansion in expectedGlobExpansions) { for (var i = 0; i < globExpansion.Length; i++) { globExpansion[i] = Path.Combine(itemSpecDirectoryPart, globExpansion[i]); } } var projectSpecs = new[] { $@"<Project> <ItemGroup> <i Include=`{Path.Combine("{0}", "**", "*.cs")}`/> </ItemGroup> </Project>", $@"<Project> <ItemGroup> <i Include=`{Path.Combine("{0}", "**", "*.cs")}`/> </ItemGroup> </Project>" } .Select(p => string.Format(p, itemSpecDirectoryPart)) .Select((p, i) => new ProjectSpecification(Path.Combine(testDirectory.Path, $"ProjectDirectory{i}", $"Project{i}.proj"), p)); var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), ""); EvaluateProjects( projectSpecs, context, project => { var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]; evaluationCount++; File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), ""); ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i")); } ); }
// projects should cache glob expansions when the __relative__ glob is shared between projects and points outside of project cone public void ContextCachesCommonOutOfProjectConeRelativeGlob(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { ContextCachesCommonOutOfProjectCone(itemSpecPathIsRelative: true, policy: policy, expectedGlobExpansions: expectedGlobExpansions); }
public void ContextDisambiguatesDistinctRelativeGlobsPointingOutsideOfSameProjectCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var globDirectory = _env.DefaultTestDirectory.CreateDirectory("glob"); var projectRoot = _env.DefaultTestDirectory.CreateDirectory("proj"); var project1Directory = projectRoot.CreateDirectory("Project1"); var project2SubDir = projectRoot.CreateDirectory("subdirectory"); var project2Directory = project2SubDir.CreateDirectory("Project2"); var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), ""); EvaluateProjects( new [] { new ProjectSpecification( Path.Combine(project1Directory.Path, "1"), @"<Project> <ItemGroup> <i Include=`../../glob/*.cs` /> </ItemGroup> </Project>"), new ProjectSpecification( Path.Combine(project2Directory.Path, "2"), @"<Project> <ItemGroup> <i Include=`../../../glob/*.cs` /> </ItemGroup> </Project>") }, context, project => { var projectName = Path.GetFileNameWithoutExtension(project.FullPath); var globFixedDirectoryPart = projectName.EndsWith("1") ? Path.Combine("..", "..", "glob") : Path.Combine("..", "..", "..", "glob"); // globs have the fixed directory part prepended, so add it to the expected results var expectedGlobExpansion = expectedGlobExpansions[evaluationCount] .Select(i => Path.Combine(globFixedDirectoryPart, i)) .ToArray(); var actualGlobExpansion = project.GetItems("i"); ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion); evaluationCount++; File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), ""); } ); }
public void ContextDisambiguatesAFullyQualifiedGlobPointingInAnotherRelativeGlobsCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { if (policy == EvaluationContext.SharingPolicy.Shared) { // This test case has a dependency on our glob expansion caching policy. If the evaluation context is reused // between evaluations and files are added to the filesystem between evaluations, the cache may be returning // stale results. Run only the Isolated variant. return; } var project1Directory = _env.DefaultTestDirectory.CreateDirectory("Project1"); var project1GlobDirectory = project1Directory.CreateDirectory("Glob").CreateDirectory("1").Path; var project2Directory = _env.DefaultTestDirectory.CreateDirectory("Project2"); var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(project1GlobDirectory, $"{evaluationCount}.cs"), ""); EvaluateProjects( new [] { // first project uses a relative path new ProjectSpecification( Path.Combine(project1Directory.Path, "1"), $@"<Project> <ItemGroup> <i Include=`{Path.Combine("Glob", "**", "*.cs")}` /> </ItemGroup> </Project>"), // second project reaches out into first project's cone via a fully qualified path new ProjectSpecification( Path.Combine(project2Directory.Path, "2"), $@"<Project> <ItemGroup> <i Include=`{Path.Combine(project1Directory.Path, "Glob", "**", "*.cs")}` /> </ItemGroup> </Project>") }, context, project => { var projectName = Path.GetFileNameWithoutExtension(project.FullPath); // globs have the fixed directory part prepended, so add it to the expected results var expectedGlobExpansion = expectedGlobExpansions[evaluationCount] .Select(i => Path.Combine("Glob", "1", i)) .ToArray(); // project 2 has fully qualified directory parts, so make the results for 2 fully qualified if (projectName.Equals("2")) { expectedGlobExpansion = expectedGlobExpansion .Select(i => Path.Combine(project1Directory.Path, i)) .ToArray(); } var actualGlobExpansion = project.GetItems("i"); ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion); evaluationCount++; File.WriteAllText(Path.Combine(project1GlobDirectory, $"{evaluationCount}.cs"), ""); } ); }
// projects should cache glob expansions when the glob is shared between projects and points outside of project cone public void ContextCachesCommonOutOfProjectConeGlob(bool itemSpecPathIsRelative, EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions) { var testDirectory = _env.DefaultTestDirectory.FolderPath; var globDirectory = Path.Combine(testDirectory, "GlobDirectory"); var itemSpecDirectoryPart = itemSpecPathIsRelative ? Path.Combine("..", "GlobDirectory") : globDirectory; itemSpecDirectoryPart = itemSpecDirectoryPart.WithTrailingSlash(); Directory.CreateDirectory(globDirectory); // Globs with a directory part will produce items prepended with that directory part foreach (var globExpansion in expectedGlobExpansions) { for (var i = 0; i < globExpansion.Length; i++) { globExpansion[i] = Path.Combine(itemSpecDirectoryPart, globExpansion[i]); } } var projectSpecs = _projectsWithOutOfConeGlobs .Select(p => string.Format(p, itemSpecDirectoryPart)) .Select((p, i) => new ProjectSpecification(Path.Combine(testDirectory, $"ProjectDirectory{i}", $"Project{i}.proj"), p)); var context = EvaluationContext.Create(policy); var evaluationCount = 0; File.WriteAllText(Path.Combine(globDirectory, $"{evaluationCount}.cs"), ""); EvaluateProjects( projectSpecs, context, project => { var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]; evaluationCount++; File.WriteAllText(Path.Combine(globDirectory, $"{evaluationCount}.cs"), ""); ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i")); } ); }