mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-19 06:58:57 +08:00
533 lines
24 KiB
C#
533 lines
24 KiB
C#
using System.Diagnostics;
|
|
using System.Xml.Linq;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Generator.Tests.BuildInfrastructure;
|
|
|
|
public sealed class BuildLayoutTests
|
|
{
|
|
private static readonly string[] s_expectedBuildFiles =
|
|
[
|
|
"AtomUI.Generator.props",
|
|
"AtomUI.Generator.targets",
|
|
"AtomUI.GeneratorConsumer.targets",
|
|
"AtomUI.LinkedRegistration.props",
|
|
"AtomUI.LinkedRegistration.targets",
|
|
"AtomUI.LinkedRegistration.SidecarConsumer.targets",
|
|
"AtomUI.Localization.props",
|
|
"AtomUI.Localization.targets",
|
|
"AtomUI.Repository.props",
|
|
"AtomUI.Repository.targets",
|
|
"AtomUI.ThemeAssets.targets",
|
|
"MacOSHomebrewNativeAot.targets",
|
|
"OutputPaths.props",
|
|
"PackageMetadata.props",
|
|
"ProjectDefaults.props",
|
|
"Versions.props"
|
|
];
|
|
|
|
private static readonly string[] s_expectedNuGetBuildAssets =
|
|
[
|
|
"AtomUI.Generator.props",
|
|
"AtomUI.Generator.targets",
|
|
"AtomUI.LinkedRegistration.props",
|
|
"AtomUI.LinkedRegistration.targets",
|
|
"AtomUI.LinkedRegistration.SidecarConsumer.targets",
|
|
"AtomUI.Localization.props",
|
|
"AtomUI.Localization.targets",
|
|
"AtomUI.ThemeAssets.targets"
|
|
];
|
|
|
|
[Fact]
|
|
public void Directory_Build_Files_Import_Only_Repository_Entry_Points()
|
|
{
|
|
GetImports("Directory.Build.props")
|
|
.ShouldBe(["$(MSBuildThisFileDirectory)build/AtomUI.Repository.props"]);
|
|
GetImports("Directory.Build.targets")
|
|
.ShouldBe(["$(MSBuildThisFileDirectory)build/AtomUI.Repository.targets"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Props_Import_Configuration_And_Generator_Entry_Point()
|
|
{
|
|
const string repositoryPropsPath = "build/AtomUI.Repository.props";
|
|
GetImports(repositoryPropsPath).ShouldBe([
|
|
"$(MSBuildThisFileDirectory)Versions.props",
|
|
"$(MSBuildThisFileDirectory)ProjectDefaults.props",
|
|
"$(MSBuildThisFileDirectory)PackageMetadata.props",
|
|
"$(MSBuildThisFileDirectory)OutputPaths.props",
|
|
"$(OutputPathWithoutFramework)/netstandard2.0/AtomUI.BuildTasks.ShadowKey.props",
|
|
"$(MSBuildThisFileDirectory)AtomUI.Generator.props"
|
|
]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Targets_Reuse_The_Generator_Entry_Point()
|
|
{
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
|
|
GetImports("build/AtomUI.Repository.targets")
|
|
.ShouldBe(["$(MSBuildThisFileDirectory)AtomUI.Generator.targets"]);
|
|
GetTargetNames(repositoryTargets)
|
|
.ShouldContain("AtomUIPrepareGeneratorConsumerPackageAssets");
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_Task_Shadow_Copies_Are_Not_Removed_During_Consumer_Builds()
|
|
{
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
var stagingTarget = repositoryTargets.Descendants("Target")
|
|
.Single(element =>
|
|
(string?)element.Attribute("Name") ==
|
|
"_AtomUIStageBuildTasksToolset");
|
|
|
|
stagingTarget.Descendants()
|
|
.Where(element => element.Name.LocalName == "RemoveDir")
|
|
.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_Task_Shadow_Copies_Are_Staged_For_Cross_Targeting_Pack_Targets()
|
|
{
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
var stagingTarget = repositoryTargets.Descendants("Target")
|
|
.Single(element =>
|
|
(string?)element.Attribute("Name") ==
|
|
"_AtomUIStageBuildTasksToolset");
|
|
|
|
((string?)stagingTarget.Attribute("BeforeTargets")).ShouldNotBeNull()
|
|
.ShouldContain("AtomUIPrepareLanguageModuleAssets");
|
|
((string?)stagingTarget.Attribute("Condition")).ShouldNotBeNull()
|
|
.ShouldNotContain("IsCrossTargetingBuild");
|
|
}
|
|
|
|
[Fact]
|
|
public void Generator_Build_Assets_Have_One_Explicit_Manifest()
|
|
{
|
|
var repositoryProps = XDocument.Load(GetRepoFile("build/AtomUI.Repository.props"));
|
|
var buildAssets = repositoryProps.Descendants("AtomUINuGetBuildAsset").ShouldHaveSingleItem();
|
|
var includes = ((string?)buildAssets.Attribute("Include"))
|
|
.ShouldNotBeNull()
|
|
.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Select(include => include.Replace("$(MSBuildThisFileDirectory)", string.Empty,
|
|
StringComparison.Ordinal))
|
|
.ToArray();
|
|
includes.ShouldBe(s_expectedNuGetBuildAssets);
|
|
buildAssets.Attribute("Exclude").ShouldBeNull();
|
|
buildAssets.Elements("PackagePath")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("buildTransitive/%(Filename)%(Extension)");
|
|
|
|
var toolAssets = repositoryProps.Descendants("AtomUIGeneratorToolAsset").ShouldHaveSingleItem();
|
|
var toolIncludes = ((string?)toolAssets.Attribute("Include")).ShouldNotBeNull();
|
|
toolIncludes.ShouldContain("AtomUI.Generator.dll");
|
|
toolIncludes.ShouldContain("AtomUI.Build.Tasks.dll");
|
|
toolAssets.Elements("PackagePath")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("tools/netstandard2.0/%(Filename)%(Extension)");
|
|
|
|
var generatorProject = XDocument.Load(GetRepoFile("src/AtomUI.Generator/AtomUI.Generator.csproj"));
|
|
generatorProject.Descendants("ProjectReference")
|
|
.ShouldNotContain(element =>
|
|
((string?)element.Attribute("Include") ?? string.Empty)
|
|
.Contains("AtomUI.Generator.LinkedPublish", StringComparison.Ordinal));
|
|
generatorProject.Descendants("None")
|
|
.ShouldContain(element =>
|
|
(string?)element.Attribute("Include") == "@(AtomUINuGetBuildAsset)");
|
|
generatorProject.Descendants("None")
|
|
.ShouldContain(element =>
|
|
(string?)element.Attribute("Include") == "@(AtomUIGeneratorToolAsset)");
|
|
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
repositoryTargets.Descendants("None")
|
|
.ShouldContain(element =>
|
|
(string?)element.Attribute("Include") == "@(AtomUINuGetBuildAsset)");
|
|
repositoryTargets.Descendants("None")
|
|
.ShouldContain(element =>
|
|
(string?)element.Attribute("Include") == "@(AtomUIGeneratorToolAsset)");
|
|
|
|
var linkedPackTarget = generatorProject.Descendants("Target")
|
|
.Single(element =>
|
|
(string?)element.Attribute("Name") ==
|
|
"AtomUIPrepareLinkedPublishGeneratorForPack");
|
|
((string?)linkedPackTarget.Attribute("BeforeTargets")).ShouldBe("_GetPackageFiles");
|
|
linkedPackTarget.Descendants("MSBuild")
|
|
.Single()
|
|
.Attribute("Projects")!
|
|
.Value.ShouldContain("AtomUI.Generator.LinkedPublish.csproj");
|
|
((string?)linkedPackTarget.Descendants("MSBuild").Single().Attribute("Condition"))
|
|
.ShouldBe("'$(NoBuild)' != 'true'");
|
|
linkedPackTarget.Descendants("Error")
|
|
.Single()
|
|
.Attribute("Condition")!
|
|
.Value.ShouldContain("AtomUILinkedPublishGeneratorAssembly");
|
|
}
|
|
|
|
[Fact]
|
|
public void NuGet_Features_Use_The_Shared_Build_Tasks_Assembly()
|
|
{
|
|
var repositoryProps = XDocument.Load(GetRepoFile("build/AtomUI.Repository.props"));
|
|
repositoryProps.Descendants("AtomUIBuildTasksAssembly")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldContain("AtomUI.Build.Tasks.dll");
|
|
|
|
var generatorProps = XDocument.Load(GetRepoFile("build/AtomUI.Generator.props"));
|
|
var fallback = generatorProps.Descendants("AtomUIBuildTasksAssembly").ShouldHaveSingleItem();
|
|
((string?)fallback.Attribute("Condition"))
|
|
.ShouldBe("'$(AtomUIBuildTasksAssembly)' == ''");
|
|
fallback.Value.Trim().ShouldBe(
|
|
"$(MSBuildThisFileDirectory)../tools/netstandard2.0/AtomUI.Build.Tasks.dll");
|
|
|
|
var buildRoot = Path.Combine(GetRepositoryRoot(), "build");
|
|
var featureFiles = s_expectedNuGetBuildAssets
|
|
.Where(file => Path.GetExtension(file) == ".targets")
|
|
.Select(file => Path.Combine(buildRoot, file));
|
|
var usingTasks = featureFiles.SelectMany(file => XDocument.Load(file).Descendants("UsingTask"))
|
|
.ToArray();
|
|
usingTasks.ShouldNotBeEmpty();
|
|
usingTasks.ShouldAllBe(element =>
|
|
(string?)element.Attribute("AssemblyFile") == "$(AtomUIBuildTasksAssembly)");
|
|
|
|
File.Exists(Path.Combine(buildRoot, "BuildTasks.props")).ShouldBeFalse();
|
|
var buildText = string.Join('\n', Directory.EnumerateFiles(buildRoot).Select(File.ReadAllText));
|
|
buildText.ShouldNotContain("AtomUILocalizationBuildTasksAssembly");
|
|
buildText.ShouldNotContain("AtomUILinkedRegistrationBuildTasksAssembly");
|
|
}
|
|
|
|
[Fact]
|
|
public void NuGet_Build_Tasks_Load_From_Shadow_Copy_In_Process()
|
|
{
|
|
var buildRoot = Path.Combine(GetRepositoryRoot(), "build");
|
|
var usingTasks = s_expectedNuGetBuildAssets
|
|
.Where(file => Path.GetExtension(file) == ".targets")
|
|
.Select(file => Path.Combine(buildRoot, file))
|
|
.SelectMany(file => XDocument.Load(file)
|
|
.Descendants()
|
|
.Where(element => element.Name.LocalName == "UsingTask"))
|
|
.Where(element =>
|
|
(string?)element.Attribute("AssemblyFile") == "$(AtomUIBuildTasksAssembly)")
|
|
.ToArray();
|
|
|
|
usingTasks.ShouldNotBeEmpty();
|
|
usingTasks.ShouldAllBe(element =>
|
|
element.Attribute("Runtime") == null);
|
|
usingTasks.ShouldAllBe(element =>
|
|
element.Attribute("TaskFactory") == null);
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Test_Project_Flag_Has_A_Canonical_Boolean_Value()
|
|
{
|
|
var defaults = XDocument.Load(GetRepoFile("build/ProjectDefaults.props"));
|
|
var isTestProject = defaults.Descendants("IsTestProject").ShouldHaveSingleItem();
|
|
|
|
isTestProject.Value.ShouldBe("true");
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Defaults_Exclude_Stale_Project_Local_Obj_Files()
|
|
{
|
|
var defaults = XDocument.Load(GetRepoFile("build/ProjectDefaults.props"));
|
|
var excludes = defaults.Descendants("DefaultItemExcludes").ShouldHaveSingleItem();
|
|
|
|
excludes.Value.ShouldContain("$(MSBuildProjectDirectory)/obj/**");
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Output_Paths_Centralize_Binaries_And_Intermediate_Files()
|
|
{
|
|
var outputPaths = XDocument.Load(GetRepoFile("build/OutputPaths.props"));
|
|
|
|
outputPaths.Descendants("PackageOutputPath")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(MSBuildThisFileDirectory)../.artifacts/Nuget/$(Configuration)");
|
|
outputPaths.Descendants("OutputPathWithoutFramework")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(MSBuildThisFileDirectory)../.artifacts/bin/$(Configuration)");
|
|
outputPaths.Descendants("OutputPath")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(OutputPathWithoutFramework)");
|
|
outputPaths.Descendants("BaseIntermediateOutputPath")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe(
|
|
"$(MSBuildThisFileDirectory)../.artifacts/$(MSBuildProjectName)/obj");
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Test_Runs_Clean_VSTest_Temporary_Results()
|
|
{
|
|
var outputPaths = XDocument.Load(GetRepoFile("build/OutputPaths.props"));
|
|
outputPaths.Descendants("AtomUITestResultsRoot")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(MSBuildThisFileDirectory)../.artifacts/TestResults");
|
|
outputPaths.Descendants("AtomUITestResultsDirectory")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(AtomUITestResultsRoot)/$(MSBuildProjectName)");
|
|
outputPaths.Descendants("VSTestResultsDirectory")
|
|
.ShouldHaveSingleItem()
|
|
.Value.ShouldBe("$(AtomUITestResultsDirectory)");
|
|
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
AssertTestResultsCleanupTarget(
|
|
repositoryTargets,
|
|
"AtomUICleanTestResultsBeforeRun",
|
|
"BeforeTargets",
|
|
"_AtomUITestResultsDirectoryToCleanBeforeRun");
|
|
AssertTestResultsCleanupTarget(
|
|
repositoryTargets,
|
|
"AtomUICleanTestResultsAfterRun",
|
|
"AfterTargets",
|
|
"_AtomUITestResultsDirectoryToCleanAfterRun");
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Test_Runs_Forbid_Persistent_VSTest_Dumps()
|
|
{
|
|
var outputPaths = XDocument.Load(GetRepoFile("build/OutputPaths.props"));
|
|
var hangDumpType = outputPaths.Descendants("VSTestBlameHangDumpType")
|
|
.ShouldHaveSingleItem();
|
|
hangDumpType.Value.ShouldBe("none");
|
|
((string?)hangDumpType.Attribute("Condition")).ShouldBe(
|
|
"'$(IsTestProject)' == 'true' and '$(VSTestBlameHang)' == 'true' and '$(VSTestBlameHangDumpType)' == ''");
|
|
|
|
var crashDumpType = outputPaths.Descendants("VSTestBlameCrashDumpType")
|
|
.ShouldHaveSingleItem();
|
|
crashDumpType.Value.ShouldBe("none");
|
|
((string?)crashDumpType.Attribute("Condition")).ShouldBe(
|
|
"'$(IsTestProject)' == 'true' and '$(VSTestBlameCrash)' == 'true' and '$(VSTestBlameCrashDumpType)' == ''");
|
|
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
var guardTarget = repositoryTargets.Descendants("Target")
|
|
.Single(element =>
|
|
(string?)element.Attribute("Name") ==
|
|
"AtomUIRejectPersistentVSTestDumps");
|
|
|
|
((string?)guardTarget.Attribute("BeforeTargets")).ShouldBe("VSTest");
|
|
((string?)guardTarget.Attribute("Condition")).ShouldBe("'$(IsTestProject)' == 'true'");
|
|
|
|
var errors = guardTarget.Descendants("Error").ToArray();
|
|
errors.Length.ShouldBe(2);
|
|
errors.Select(element => (string?)element.Attribute("Condition"))
|
|
.ShouldBe([
|
|
"'$(VSTestBlameHangDumpType)' == 'full' or '$(VSTestBlameHangDumpType)' == 'mini'",
|
|
"'$(VSTestBlameCrashDumpType)' == 'full' or '$(VSTestBlameCrashDumpType)' == 'mini'"
|
|
]);
|
|
errors.Select(element => (string?)element.Attribute("Text"))
|
|
.ShouldAllBe(text => text != null &&
|
|
text.Contains("must not collect persistent", StringComparison.Ordinal));
|
|
}
|
|
|
|
private static void AssertTestResultsCleanupTarget(
|
|
XDocument repositoryTargets,
|
|
string targetName,
|
|
string timingAttribute,
|
|
string itemName)
|
|
{
|
|
var cleanupTarget = repositoryTargets.Descendants("Target")
|
|
.Single(element =>
|
|
(string?)element.Attribute("Name") == targetName);
|
|
|
|
((string?)cleanupTarget.Attribute(timingAttribute)).ShouldBe("VSTest");
|
|
((string?)cleanupTarget.Attribute("Condition")).ShouldBe("'$(IsTestProject)' == 'true'");
|
|
|
|
var directoriesToClean = cleanupTarget.Descendants(itemName)
|
|
.Select(element => (string?)element.Attribute("Include"))
|
|
.ToArray();
|
|
directoriesToClean.ShouldBe([
|
|
"$(MSBuildProjectDirectory)/TestResults",
|
|
"$(AtomUITestResultsDirectory)"
|
|
]);
|
|
|
|
var removeDir = cleanupTarget.Descendants("RemoveDir").ShouldHaveSingleItem();
|
|
((string?)removeDir.Attribute("Directories")).ShouldBe($"@({itemName})");
|
|
removeDir.Attribute("ContinueOnError").ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Tool_Source_Files_Stay_Trackable_While_Tool_Build_Outputs_Are_Ignored()
|
|
{
|
|
IsIgnoredByGit("tools/AtomUI.Docs.LLMsGenerator/Program.cs").ShouldBeFalse();
|
|
IsIgnoredByGit("tools/performances/AtomUI.Performance/Program.cs").ShouldBeFalse();
|
|
IsIgnoredByGit("tools/AtomUI.Docs.LLMsGenerator/bin/Debug/tool.dll").ShouldBeTrue();
|
|
IsIgnoredByGit("tools/performances/AtomUI.Performance/obj/project.assets.json").ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Targets_Exclude_Compiler_Generated_Files_Once()
|
|
{
|
|
const string generatedFilesPattern = "$(CompilerGeneratedFilesOutputPath)/**/*.cs";
|
|
var repositoryTargets = XDocument.Load(GetRepoFile("build/AtomUI.Repository.targets"));
|
|
var removal = repositoryTargets.Descendants("Compile")
|
|
.Where(element =>
|
|
(string?)element.Attribute("Remove") == generatedFilesPattern)
|
|
.ShouldHaveSingleItem();
|
|
|
|
((string?)removal.Parent?.Attribute("Condition"))
|
|
.ShouldBe("'$(CompilerGeneratedFilesOutputPath)' != ''");
|
|
|
|
var repositoryRoot = GetRepositoryRoot();
|
|
var projectFiles = new[] { "src", "controlgallery", "tests", "tools" }
|
|
.Select(directory => Path.Combine(repositoryRoot, directory))
|
|
.Where(Directory.Exists)
|
|
.SelectMany(directory => Directory.EnumerateFiles(
|
|
directory,
|
|
"*.csproj",
|
|
SearchOption.AllDirectories));
|
|
foreach (var projectFile in projectFiles)
|
|
{
|
|
var project = XDocument.Load(projectFile);
|
|
if (!project.Descendants("CompilerGeneratedFilesOutputPath").Any())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
project.Descendants("Compile")
|
|
.ShouldNotContain(element =>
|
|
(string?)element.Attribute("Remove") == generatedFilesPattern,
|
|
projectFile);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Repository_Default_Items_Always_Exclude_Stale_Generated_Files()
|
|
{
|
|
var projectDefaults = XDocument.Load(GetRepoFile("build/ProjectDefaults.props"));
|
|
var excludes = projectDefaults.Descendants("DefaultItemExcludes")
|
|
.ShouldHaveSingleItem()
|
|
.Value;
|
|
|
|
excludes.ShouldContain("$(MSBuildProjectDirectory)/obj/**");
|
|
excludes.ShouldContain("$(MSBuildProjectDirectory)/GeneratedFiles/**");
|
|
}
|
|
|
|
[Fact]
|
|
public void Desktop_Project_Does_Not_Keep_Stale_Window_Directory_Exclusions()
|
|
{
|
|
var project = File.ReadAllText(GetRepoFile(
|
|
"src/AtomUI.Desktop.Controls/AtomUI.Desktop.Controls.csproj"));
|
|
|
|
project.ShouldNotContain("Window\\Reflection");
|
|
project.ShouldNotContain("Window\\Visuals");
|
|
}
|
|
|
|
[Fact]
|
|
public void NuGet_Generator_Entry_Points_Import_Flat_Feature_Files()
|
|
{
|
|
GetImports("build/AtomUI.Generator.props").ShouldBe([
|
|
"$(MSBuildThisFileDirectory)AtomUI.LinkedRegistration.props",
|
|
"$(MSBuildThisFileDirectory)AtomUI.Localization.props"
|
|
]);
|
|
GetImports("build/AtomUI.Generator.targets").ShouldBe([
|
|
"$(MSBuildThisFileDirectory)AtomUI.LinkedRegistration.targets",
|
|
"$(MSBuildThisFileDirectory)AtomUI.Localization.targets",
|
|
"$(MSBuildThisFileDirectory)AtomUI.ThemeAssets.targets"
|
|
]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Localization_Targets_Own_The_Complete_Build_Integration()
|
|
{
|
|
var targets = XDocument.Load(GetRepoFile("build/AtomUI.Localization.targets"));
|
|
|
|
GetImports("build/AtomUI.Localization.targets").ShouldBeEmpty();
|
|
targets.Descendants("AdditionalFiles").ShouldNotBeEmpty();
|
|
targets.Descendants("CompilerVisibleItemMetadata").ShouldNotBeEmpty();
|
|
GetTargetNames(targets).ShouldBe([
|
|
"AtomUIGetLanguageModuleSourceAssets",
|
|
"AtomUIResolveLanguageContractProjectReferences",
|
|
"AtomUIInferLanguagePackageContractPolicy",
|
|
"AtomUIGetLanguagePackProjectAssets",
|
|
"AtomUIResolveLanguagePackProjectReferences",
|
|
"AtomUIExportLanguageTemplates",
|
|
"AtomUIPrepareLanguagePackage",
|
|
"AtomUIPrepareLanguageModuleAssets"
|
|
]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_Root_Is_A_Flat_Explicit_MSBuild_Surface()
|
|
{
|
|
var buildRoot = Path.Combine(GetRepositoryRoot(), "build");
|
|
Directory.EnumerateDirectories(buildRoot).ShouldBeEmpty();
|
|
Directory.EnumerateFiles(buildRoot)
|
|
.Select(Path.GetFileName)
|
|
.ShouldBe(s_expectedBuildFiles, ignoreOrder: true);
|
|
Directory.EnumerateFiles(buildRoot)
|
|
.All(file => Path.GetExtension(file) is ".props" or ".targets")
|
|
.ShouldBeTrue();
|
|
}
|
|
|
|
private static string[] GetImports(string relativePath)
|
|
{
|
|
return XDocument.Load(GetRepoFile(relativePath))
|
|
.Descendants()
|
|
.Where(element => element.Name.LocalName == "Import")
|
|
.Select(element => (string?)element.Attribute("Project"))
|
|
.Where(static path => path is not null)
|
|
.Cast<string>()
|
|
.ToArray();
|
|
}
|
|
|
|
private static string[] GetTargetNames(XDocument document)
|
|
{
|
|
return document.Descendants()
|
|
.Where(element => element.Name.LocalName == "Target")
|
|
.Select(element => (string?)element.Attribute("Name"))
|
|
.Where(static name => name is not null)
|
|
.Cast<string>()
|
|
.ToArray();
|
|
}
|
|
|
|
private static string GetRepoFile(string relativePath)
|
|
{
|
|
var repositoryRoot = GetRepositoryRoot();
|
|
var candidate = Path.Combine(repositoryRoot, relativePath);
|
|
if (File.Exists(candidate))
|
|
{
|
|
return candidate;
|
|
}
|
|
|
|
throw new FileNotFoundException(relativePath);
|
|
}
|
|
|
|
private static string GetRepositoryRoot()
|
|
{
|
|
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (directory is not null)
|
|
{
|
|
if (File.Exists(Path.Combine(directory.FullName, "AtomUI.slnx")))
|
|
{
|
|
return directory.FullName;
|
|
}
|
|
|
|
directory = directory.Parent;
|
|
}
|
|
|
|
throw new DirectoryNotFoundException("Could not locate the AtomUI repository root.");
|
|
}
|
|
|
|
private static bool IsIgnoredByGit(string relativePath)
|
|
{
|
|
using var process = Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = "git",
|
|
WorkingDirectory = GetRepositoryRoot(),
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
ArgumentList =
|
|
{
|
|
"check-ignore",
|
|
"--quiet",
|
|
"--no-index",
|
|
"--",
|
|
relativePath
|
|
}
|
|
});
|
|
process.ShouldNotBeNull();
|
|
process.WaitForExit();
|
|
process.ExitCode.ShouldBeOneOf(0, 1);
|
|
return process.ExitCode == 0;
|
|
}
|
|
}
|