зеркало из https://github.com/microsoft/BuildXL.git
Merged PR 562541: Add Yarn integration tests
Add Yarn integration tests and factor out some common logic between Rush and Yarn tests Related work items: #1742851
This commit is contained in:
Родитель
3e41d11e59
Коммит
8b76936efe
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import {Transformer} from "Sdk.Transformers";
|
||||
|
||||
// This is an empty facade for a Microsoft internal package.
|
||||
|
||||
namespace Contents {
|
||||
export declare const qualifier: {
|
||||
};
|
||||
|
||||
@@public
|
||||
export const all: StaticDirectory = Transformer.sealPartialDirectory(d`.`, []);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
module({
|
||||
name: "Npm.OnCloudBuild"
|
||||
});
|
|
@ -12,6 +12,14 @@ namespace Node {
|
|||
@@public
|
||||
export const npmCli = getNpmCli();
|
||||
|
||||
const nodeExecutablesDir : Directory = d`${getNodeTool().exe.parent}`;
|
||||
|
||||
/**
|
||||
* Self-contained node executables. Platform dependent.
|
||||
*/
|
||||
@@public
|
||||
export const nodeExecutables : StaticDirectory = Transformer.sealDirectory(nodeExecutablesDir, globR(nodeExecutablesDir));
|
||||
|
||||
@@public
|
||||
export function run(args: Transformer.ExecuteArguments) : Transformer.ExecuteResult {
|
||||
// Node code can access any of the following user specific environment variables.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
module({
|
||||
name: "Yarn"
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import {Transformer} from "Sdk.Transformers";
|
||||
|
||||
/**
|
||||
* Returns a static directory containing a valid Yarn installation.
|
||||
*/
|
||||
@@public
|
||||
export function getYarn() : StaticDirectory {
|
||||
if (Environment.getFlag("[Sdk.BuildXL]microsoftInternal")) {
|
||||
// Internally in Microsoft we use a nuget package that contains Yarn.
|
||||
return Transformer.reSealPartialDirectory(importFrom("Npm.OnCloudBuild").Contents.all, r`tools/Yarn`);
|
||||
}
|
||||
|
||||
// For the public build, we require Yarn to be installed
|
||||
const installedYarnLocation = d`${Context.getMount("ProgramFilesX86").path}/Yarn`;
|
||||
const packageJson = f`${installedYarnLocation}/package.json`;
|
||||
if (!File.exists(packageJson))
|
||||
{
|
||||
Contract.fail(`Could not find Yarn installed. File '${packageJson.toDiagnosticString()}' does not exist.`);
|
||||
}
|
||||
|
||||
return Transformer.sealDirectory(installedYarnLocation, globR(installedYarnLocation));
|
||||
}
|
|
@ -35,6 +35,8 @@ namespace JavaScript {
|
|||
],
|
||||
internalsVisibleTo: [
|
||||
"Test.BuildXL.FrontEnd.Rush",
|
||||
"Test.BuildXL.FrontEnd.Yarn",
|
||||
"Test.BuildXL.FrontEnd.Core"
|
||||
],
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.FrontEnd.JavaScript;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Pips = global::BuildXL.Pips.Operations;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Some utilities for JavaScript-based tests
|
||||
/// </summary>
|
||||
public static class JavaScriptTestHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="AddJavaScriptProject(SpecEvaluationBuilder, string, string, string, string[], (string, string)[])"/>
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddJavaScriptProject(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string packageName,
|
||||
string packageFolder,
|
||||
string content = null,
|
||||
string[] dependencies = null,
|
||||
(string, string)[] scriptCommands = null)
|
||||
{
|
||||
var dependenciesWithVersions = dependencies?.Select(dep => (dep, "0.0.1"))?.ToArray();
|
||||
return AddJavaScriptProjectWithExplicitVersions(builder, packageName, packageFolder, content, dependenciesWithVersions, scriptCommands);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility for adding a node spec together with a corresponding package.json
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddJavaScriptProjectWithExplicitVersions(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string packageName,
|
||||
string packageFolder,
|
||||
string content = null,
|
||||
(string, string)[] dependenciesWithVersions = null,
|
||||
(string, string)[] scriptCommands = null)
|
||||
{
|
||||
return builder
|
||||
.AddSpec(Path.Combine(packageFolder, "main.js"), content ?? "function A(){}")
|
||||
.AddSpec(Path.Combine(packageFolder, "package.json"),
|
||||
CreatePackageJson(packageName, scriptCommands, dependenciesWithVersions ?? new (string, string)[] { }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persists a Bxl configuration file at the given path
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddBxlConfigurationFile(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string path,
|
||||
string content)
|
||||
{
|
||||
builder.AddFile(Path.Combine(path, JavaScriptWorkspaceResolver<DsTest, IJavaScriptResolverSettings>.BxlConfigurationFilename), content);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provenance set by the JavaScript scheduler to retrieve a process pip that corresponds to a given package name and script command
|
||||
/// </summary>
|
||||
/// <returns>Null if the process is not found</returns>
|
||||
public static Pips.Process RetrieveProcess(this EngineState engineState, string packageName, string scriptCommand = null)
|
||||
{
|
||||
scriptCommand ??= "build";
|
||||
|
||||
var projectSymbol = JavaScriptPipConstructor.GetFullSymbolFromProject(packageName, scriptCommand, engineState.SymbolTable);
|
||||
var processes = engineState.PipGraph.RetrievePipsOfType(Pips.PipType.Process);
|
||||
|
||||
return (Pips.Process)processes.FirstOrDefault(process => process.Provenance.OutputValueSymbol == projectSymbol);
|
||||
}
|
||||
|
||||
public static string CreatePackageJson(
|
||||
string projectName,
|
||||
(string, string)[] scriptCommands = null,
|
||||
(string dependency, string version)[] dependenciesWithVersions = null)
|
||||
{
|
||||
scriptCommands ??= new[] { ("build", "node ./main.js") };
|
||||
dependenciesWithVersions ??= new (string, string)[] { };
|
||||
|
||||
return $@"
|
||||
{{
|
||||
""name"": ""{projectName}"",
|
||||
""version"": ""0.0.1"",
|
||||
""description"": ""Test project {projectName}"",
|
||||
""scripts"": {{
|
||||
{string.Join(",", scriptCommands.Select(kvp => $"\"{kvp.Item1}\": \"{kvp.Item2}\""))}
|
||||
}},
|
||||
""main"": ""main.js"",
|
||||
""dependencies"": {{
|
||||
{string.Join(",", dependenciesWithVersions.Select(depAndVer => $"\"{depAndVer.dependency}\":\"{depAndVer.version}\""))}
|
||||
}}
|
||||
}}
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ namespace Core {
|
|||
importFrom("BuildXL.FrontEnd").Core.dll,
|
||||
importFrom("BuildXL.FrontEnd").Script.dll,
|
||||
importFrom("BuildXL.FrontEnd").Sdk.dll,
|
||||
importFrom("BuildXL.FrontEnd").JavaScript.dll,
|
||||
importFrom("BuildXL.FrontEnd").TypeScript.Net.dll,
|
||||
importFrom("BuildXL.Pips").dll,
|
||||
importFrom("BuildXL.Utilities").dll,
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.JavaScript.Tracing.LogEventId;
|
||||
|
@ -27,8 +26,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void SimpleRushConfigurationFileIsHonored()
|
||||
{
|
||||
var config = Build()
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddRushConfigurationFile("src/A", @"
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddBxlConfigurationFile("src/A", @"
|
||||
{
|
||||
""outputDirectories"": [""../output/dir""],
|
||||
""sourceFiles"": [""input/file""]
|
||||
|
@ -53,8 +52,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void PathPatternsAreHonored(string outputDirectoriesJSON, string[] expectedOutputDirectories)
|
||||
{
|
||||
var config = Build()
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddRushConfigurationFile("src/A", @$"
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddBxlConfigurationFile("src/A", @$"
|
||||
{{
|
||||
""outputDirectories"": {outputDirectoriesJSON}
|
||||
}}")
|
||||
|
@ -79,8 +78,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
{
|
||||
// Create a project and schedule its 'build' and 'test' script. Only define output directories for 'build'
|
||||
var config = Build(executeCommands: "['build', 'test']")
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "build A"), ("test", "test A")})
|
||||
.AddRushConfigurationFile("src/A", @"
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "build A"), ("test", "test A")})
|
||||
.AddBxlConfigurationFile("src/A", @"
|
||||
{
|
||||
""outputDirectories"": [{""path"": ""../output/dir"", ""targetScripts"": [""build""]}]
|
||||
}")
|
||||
|
@ -107,8 +106,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void MalformedConfigurationFileIsHandled(string outputDirectories)
|
||||
{
|
||||
var config = Build()
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddRushConfigurationFile("src/A", @$"
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddBxlConfigurationFile("src/A", @$"
|
||||
{{
|
||||
""outputDirectories"": [{outputDirectories}]
|
||||
}}")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.JavaScript.Tracing.LogEventId;
|
||||
|
@ -30,7 +30,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void InvalidCustomRushCommands(string customCommands)
|
||||
{
|
||||
var config = Build(customRushCommands: customCommands)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -48,7 +48,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
// Schedule a rush project with a build command 'execute', and extend it to be
|
||||
// 'execute --test' via a custom command
|
||||
var config = Build(customRushCommands: "[{command: 'build', extraArguments: '--test'}]")
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -67,8 +67,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
// Two projects, one with 'build', the other one with 'test'.
|
||||
// Extend 'build'to be 'execute --test' via a custom command
|
||||
var config = Build(customRushCommands: "[{command: 'build', extraArguments: '--test'}]", executeCommands: "['build', 'test']")
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.AddRushProject("@ms/project-B", "src/B", scriptCommands: new[] { ("test", "execute") })
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.AddJavaScriptProject("@ms/project-B", "src/B", scriptCommands: new[] { ("test", "execute") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -90,7 +90,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
{
|
||||
// Exercise custom commands with other types
|
||||
var config = Build(customRushCommands: "[{command: 'build', extraArguments: ['--test', a`atom`, r`relative/path`, p`C:/absolute/path`]}]")
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "execute") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.JavaScript.Tracing.LogEventId;
|
||||
|
@ -51,7 +51,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
// The script commands themselves are not important since nothing gets executed, just scheduled
|
||||
|
||||
var config = Build(executeCommands: "['build', 'sign', 'test']")
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "build A"), ("sign", "sign A"), ("test", "test A") })
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "build A"), ("sign", "sign A"), ("test", "test A") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -77,9 +77,9 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
// The script commands themselves are not important since nothing gets executed, just scheduled
|
||||
// We should find B build depending on A build, and B test depending on B build
|
||||
var config = Build(executeCommands: "['build', 'test']")
|
||||
.AddRushProject("@ms/project-A", "src/A",
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A",
|
||||
scriptCommands: new[] { ("build", "build A"), ("test", "test A") })
|
||||
.AddRushProject("@ms/project-B", "src/B", dependencies: new[] { "@ms/project-A" },
|
||||
.AddJavaScriptProject("@ms/project-B", "src/B", dependencies: new[] { "@ms/project-A" },
|
||||
scriptCommands: new[] { ("build", "build B"), ("test", "test B") })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
|
@ -120,10 +120,10 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
]";
|
||||
|
||||
var config = Build(executeCommands: commands)
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "b A"), ("pre-build", "pr A") })
|
||||
.AddRushProject("@ms/project-B", "src/B", scriptCommands: new[] { ("build", "b B"), ("pre-build", "pr B"), ("post-build", "ps B") },
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "b A"), ("pre-build", "pr A") })
|
||||
.AddJavaScriptProject("@ms/project-B", "src/B", scriptCommands: new[] { ("build", "b B"), ("pre-build", "pr B"), ("post-build", "ps B") },
|
||||
dependencies: new[] { "@ms/project-A" })
|
||||
.AddRushProject("@ms/project-C", "src/C", scriptCommands: new[] { ("build", "b C"), ("pre-build", "pr C"), ("post-build", "ps C") },
|
||||
.AddJavaScriptProject("@ms/project-C", "src/C", scriptCommands: new[] { ("build", "b C"), ("pre-build", "pr C"), ("post-build", "ps C") },
|
||||
dependencies: new[] { "@ms/project-A" })
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
|
@ -182,7 +182,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
private BuildXLEngineResult BuildDummyWithCommands(string commands)
|
||||
{
|
||||
var config = Build(executeCommands: commands)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
return RunRushProjects(config, new[] {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.JavaScript.Tracing.LogEventId;
|
||||
|
@ -27,7 +26,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void InvalidRushExportsSettings(string rushExports)
|
||||
{
|
||||
var config = Build(rushExports: rushExports)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -44,7 +43,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void MissingPackageInExportsIsFlagged(string exports, LogEventId expectedError)
|
||||
{
|
||||
var config = Build(rushExports: exports)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -60,7 +59,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
public void InvalidExportSymbolIsFlagged()
|
||||
{
|
||||
var config = Build(rushExports: "[{symbolName: 'invalid-symbol', content: []}]")
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
@ -82,7 +81,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
rushExports: "[{symbolName: 'exportSymbol', content: ['@ms/project-A']}]",
|
||||
moduleName: "rushTest",
|
||||
addDScriptResolver: true)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddSpec("module.config.dsc", "module({name: 'dscriptTest', nameResolutionSemantics: NameResolutionSemantics.implicitProjectReferences});")
|
||||
// Consume 'exportSymbol' from DScript and make sure it is an array of shared opaques.
|
||||
// Check as well the share opaque output is related to project A
|
||||
|
@ -116,8 +115,8 @@ const assertion2 = Contract.assert(firstOutput.root.isWithin(d`src/A`));")
|
|||
rushExports: $"[{{symbolName: 'exportSymbol', content: [{exportContent}]}}]",
|
||||
moduleName: "rushTest",
|
||||
addDScriptResolver: true)
|
||||
.AddRushProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "call build"), ("test", "call test") })
|
||||
.AddRushConfigurationFile("src/A", @"
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", scriptCommands: new[] { ("build", "call build"), ("test", "call test") })
|
||||
.AddBxlConfigurationFile("src/A", @"
|
||||
{
|
||||
""outputDirectories"": [
|
||||
{""path"": ""output/dir/for/build"", ""targetScripts"": [""build""]},
|
||||
|
@ -148,7 +147,7 @@ import {{exportSymbol}} from 'rushTest';
|
|||
Build(
|
||||
moduleName: "rushTest",
|
||||
addDScriptResolver: true)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddSpec("module.config.dsc", "module({name: 'dscriptTest', nameResolutionSemantics: NameResolutionSemantics.implicitProjectReferences});")
|
||||
// Consume 'all' from DScript
|
||||
.AddSpec("import {all} from 'rushTest';")
|
||||
|
@ -166,7 +165,7 @@ import {{exportSymbol}} from 'rushTest';
|
|||
{
|
||||
var config =
|
||||
Build(rushExports: $"[{{symbolName: 'all', content: []}}]")
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
|
|
@ -223,13 +223,6 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
";
|
||||
}
|
||||
|
||||
protected static bool IsDependencyAndDependent(global::BuildXL.Pips.Operations.Process dependency, global::BuildXL.Pips.Operations.Process dependent)
|
||||
{
|
||||
// Unfortunately the test pip graph we are using doesn't keep track of dependencies/dependents. So we check there is a directory output of the dependency
|
||||
// that is a directory input for a dependent
|
||||
return dependency.DirectoryOutputs.Any(directoryOutput => dependent.DirectoryDependencies.Any(directoryDependency => directoryDependency == directoryOutput));
|
||||
}
|
||||
|
||||
private string DefaultRushPrelude(
|
||||
Dictionary<string, DiscriminatingUnion<string, UnitValue>> environment,
|
||||
string executeCommands,
|
||||
|
|
|
@ -13,7 +13,7 @@ using BuildXL.Utilities;
|
|||
using BuildXL.Utilities.Configuration;
|
||||
using BuildXL.Utilities.Configuration.Mutable;
|
||||
using Test.BuildXL.EngineTestUtilities;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
@ -32,8 +32,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
{
|
||||
// Create two projects A and B such that A -> B.
|
||||
var config = Build()
|
||||
.AddRushProject("@ms/project-A", "src/A", "module.exports = function A(){}")
|
||||
.AddRushProject("@ms/project-B", "src/B", "const A = require('@ms/project-A'); return A();", new string[] { "@ms/project-A"})
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", "module.exports = function A(){}")
|
||||
.AddJavaScriptProject("@ms/project-B", "src/B", "const A = require('@ms/project-A'); return A();", new string[] { "@ms/project-A"})
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunRushProjects(config, new[] {
|
||||
|
@ -60,7 +60,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
var testCache = new TestCache();
|
||||
|
||||
var config = (CommandLineConfiguration)Build()
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
config.Cache.CacheGraph = true;
|
||||
|
@ -107,7 +107,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
};
|
||||
|
||||
var config = (CommandLineConfiguration)Build(environment)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
config.Cache.CacheGraph = true;
|
||||
|
@ -150,7 +150,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
["Test"] = new DiscriminatingUnion<string, UnitValue>(UnitValue.Unit) };
|
||||
|
||||
var config = (CommandLineConfiguration)Build(environment)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
config.Cache.CacheGraph = true;
|
||||
|
@ -187,8 +187,8 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
{
|
||||
// Create two projects with the same package name
|
||||
var config = Build()
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddRushProject("@ms/project-A", "src/B")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/B")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunRushProjects(config, new[] {
|
||||
|
@ -209,7 +209,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
|
||||
// Run a project that writes a file under a nested directory
|
||||
var config = (CommandLineConfiguration)Build()
|
||||
.AddRushProjectWithExplicitVersions(
|
||||
.AddJavaScriptProjectWithExplicitVersions(
|
||||
"@ms/project-A",
|
||||
"src/A",
|
||||
"var fs = require('fs'); fs.mkdirSync('CamelCasedLib'); fs.writeFileSync('CamelCasedLib/out.txt', 'hello');",
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.FrontEnd.Rush;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Pips = global::BuildXL.Pips.Operations;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Rush.IntegrationTests
|
||||
{
|
||||
/// <nodoc/>
|
||||
public static class RushIntegrationTestsHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="AddRushProject(SpecEvaluationBuilder, string, string, string, string[], (string, string)[])"/>
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddRushProject(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string packageName,
|
||||
string packageFolder,
|
||||
string content = null,
|
||||
string[] dependencies = null,
|
||||
(string, string)[] scriptCommands = null)
|
||||
{
|
||||
var dependenciesWithVersions = dependencies?.Select(dep => (dep, "0.0.1"))?.ToArray();
|
||||
return AddRushProjectWithExplicitVersions(builder, packageName, packageFolder, content, dependenciesWithVersions, scriptCommands);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility for adding a node spec together with a corresponding package.json
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddRushProjectWithExplicitVersions(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string packageName,
|
||||
string packageFolder,
|
||||
string content = null,
|
||||
(string, string)[] dependenciesWithVersions = null,
|
||||
(string, string)[] scriptCommands = null)
|
||||
{
|
||||
return builder
|
||||
.AddSpec(Path.Combine(packageFolder, "main.js"), content ?? "function A(){}")
|
||||
.AddSpec(Path.Combine(packageFolder, "package.json"),
|
||||
RushIntegrationTestBase.CreatePackageJson(packageName, scriptCommands, dependenciesWithVersions ?? new (string, string)[] { }));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Persists a rush configuration file at the given path
|
||||
/// </summary>
|
||||
public static SpecEvaluationBuilder AddRushConfigurationFile(
|
||||
this SpecEvaluationBuilder builder,
|
||||
string path,
|
||||
string content)
|
||||
{
|
||||
builder.AddFile(Path.Combine(path, RushWorkspaceResolver.BxlConfigurationFilename), content);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provenance set by the rush scheduler to retrieve a process pip that corresponds to a given package name and script command
|
||||
/// </summary>
|
||||
/// <returns>Null if the process is not found</returns>
|
||||
public static Pips.Process RetrieveProcess(this EngineState engineState, string packageName, string scriptCommand = null)
|
||||
{
|
||||
scriptCommand ??= "build";
|
||||
|
||||
var projectSymbol = RushPipConstructor.GetFullSymbolFromProject(packageName, scriptCommand, engineState.SymbolTable);
|
||||
var processes = engineState.PipGraph.RetrievePipsOfType(Pips.PipType.Process);
|
||||
|
||||
return (Pips.Process)processes.FirstOrDefault(process => process.Provenance.OutputValueSymbol == projectSymbol);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using BuildXL.FrontEnd.JavaScript.Tracing;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
|
@ -25,7 +26,7 @@ namespace Test.BuildXL.FrontEnd.Rush.IntegrationTests
|
|||
public void ExplicitInvalidRushLibLocationIsHandled()
|
||||
{
|
||||
var config = Build(rushBaseLibLocation: "/path/to/foo")
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunRushProjects(config, new[] {
|
||||
|
@ -53,7 +54,7 @@ namespace Test.BuildXL.FrontEnd.Rush.IntegrationTests
|
|||
};
|
||||
|
||||
var config = Build(environment: environment, rushBaseLibLocation: null)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunRushProjects(config, new[] {
|
||||
|
@ -69,7 +70,7 @@ namespace Test.BuildXL.FrontEnd.Rush.IntegrationTests
|
|||
// Explicitly undefine the rush base lib location, but do not expose anything in PATH
|
||||
// that points to a valid Rush installation
|
||||
var config = Build(rushBaseLibLocation: null)
|
||||
.AddRushProject("@ms/project-A", "src/A")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunRushProjects(config, new[] {
|
||||
|
|
|
@ -3,14 +3,12 @@
|
|||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Rush.IntegrationTests;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Test.BuildXL.TestUtilities.Xunit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using LogEventId = global::BuildXL.FrontEnd.Rush.Tracing.LogEventId;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Rush
|
||||
{
|
||||
|
@ -31,7 +29,7 @@ namespace Test.BuildXL.FrontEnd.Rush
|
|||
string commonTempFolder = Path.Combine(TestRoot, "CustomTempFolder").Replace("\\", "/"); ;
|
||||
|
||||
var config = Build(commonTempFolder: commonTempFolder)
|
||||
.AddRushProject("@ms/project-A", "src/A").
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A").
|
||||
PersistSpecsAndGetConfiguration();
|
||||
|
||||
var result = RunRushProjects(config, new[] {
|
||||
|
|
|
@ -167,5 +167,12 @@ namespace Test.DScript.Ast
|
|||
|
||||
return (FrontEndHostController)engine.FrontEndController;
|
||||
}
|
||||
|
||||
protected static bool IsDependencyAndDependent(global::BuildXL.Pips.Operations.Process dependency, global::BuildXL.Pips.Operations.Process dependent)
|
||||
{
|
||||
// Unfortunately the test pip graph we are using doesn't keep track of dependencies/dependents. So we check there is a directory output of the dependency
|
||||
// that is a directory input for a dependent
|
||||
return dependency.DirectoryOutputs.Any(directoryOutput => dependent.DirectoryDependencies.Any(directoryDependency => directoryDependency == directoryOutput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BuildXL.Engine;
|
||||
using BuildXL.Processes;
|
||||
using BuildXL.Utilities;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using BuildXL.Utilities.Configuration.Mutable;
|
||||
using Test.BuildXL.EngineTestUtilities;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Test.BuildXL.TestUtilities;
|
||||
using Test.BuildXL.TestUtilities.Xunit;
|
||||
using Test.DScript.Ast;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Yarn
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides facilities to run the engine adding Yarn specific artifacts.
|
||||
/// </summary>
|
||||
[TestClassIfSupported(requiresWindowsBasedOperatingSystem: true)]
|
||||
public abstract class YarnIntegrationTestBase : DsTestWithCacheBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToYarn => Path.Combine(TestDeploymentDir, "yarn", "bin", "yarn").Replace("\\", "/");
|
||||
|
||||
/// <summary>
|
||||
/// Keep in sync with deployment.
|
||||
/// </summary>
|
||||
protected string PathToNode => Path.Combine(TestDeploymentDir, "Node", OperatingSystemHelper.IsLinuxOS? "bin/node" : "node.exe").Replace("\\", "/");
|
||||
|
||||
/// <nodoc/>
|
||||
protected string PathToNodeFolder => Path.GetDirectoryName(PathToNode).Replace("\\", "/");
|
||||
|
||||
/// <summary>
|
||||
/// Default out dir to use in projects
|
||||
/// </summary>
|
||||
protected string OutDir { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Root to the source enlistment root
|
||||
/// </summary>
|
||||
protected string SourceRoot { get; }
|
||||
|
||||
// By default the engine runs e2e
|
||||
protected virtual EnginePhases Phase => EnginePhases.Execute;
|
||||
|
||||
protected override bool DisableDefaultSourceResolver => true;
|
||||
|
||||
protected YarnIntegrationTestBase(ITestOutputHelper output) : base(output, true)
|
||||
{
|
||||
RegisterEventSource(global::BuildXL.Engine.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.Processes.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.Scheduler.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.Pips.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.FrontEnd.Core.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.FrontEnd.Script.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.FrontEnd.Yarn.ETWLogger.Log);
|
||||
RegisterEventSource(global::BuildXL.FrontEnd.JavaScript.ETWLogger.Log);
|
||||
|
||||
SourceRoot = Path.Combine(TestRoot, RelativeSourceRoot);
|
||||
OutDir = "target";
|
||||
}
|
||||
|
||||
protected SpecEvaluationBuilder Build(
|
||||
Dictionary<string, string> environment = null,
|
||||
string yarnLocation = "",
|
||||
string moduleName = "Test")
|
||||
{
|
||||
environment ??= new Dictionary<string, string> {
|
||||
["PATH"] = PathToNodeFolder,
|
||||
};
|
||||
|
||||
return Build(
|
||||
environment.ToDictionary(kvp => kvp.Key, kvp => new DiscriminatingUnion<string, UnitValue>(kvp.Value)),
|
||||
yarnLocation,
|
||||
moduleName);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected SpecEvaluationBuilder Build(
|
||||
Dictionary<string, DiscriminatingUnion<string, UnitValue>> environment,
|
||||
string yarnLocation = "",
|
||||
string moduleName = "Test")
|
||||
{
|
||||
environment ??= new Dictionary<string, DiscriminatingUnion<string, UnitValue>> {
|
||||
["PATH"] = new DiscriminatingUnion<string, UnitValue>(PathToNodeFolder),
|
||||
};
|
||||
|
||||
// We reserve the null string for a true undefined.
|
||||
if (yarnLocation == string.Empty)
|
||||
{
|
||||
yarnLocation = PathToYarn;
|
||||
}
|
||||
|
||||
// Let's explicitly pass an environment, so the process environment won't affect tests by default
|
||||
return base.Build().Configuration(
|
||||
DefaultYarnPrelude(
|
||||
environment: environment,
|
||||
yarnLocation: yarnLocation,
|
||||
moduleName: moduleName));
|
||||
}
|
||||
|
||||
protected BuildXLEngineResult RunYarnProjects(
|
||||
ICommandLineConfiguration config,
|
||||
TestCache testCache = null,
|
||||
IDetoursEventListener detoursListener = null)
|
||||
{
|
||||
// This bootstraps the 'repo'
|
||||
if (!YarnInit(config))
|
||||
{
|
||||
throw new InvalidOperationException("Yarn init failed.");
|
||||
}
|
||||
|
||||
using (var tempFiles = new TempFileStorage(canGetFileNames: true, rootPath: TestOutputDirectory))
|
||||
{
|
||||
var appDeployment = CreateAppDeployment(tempFiles);
|
||||
|
||||
((CommandLineConfiguration)config).Engine.Phase = Phase;
|
||||
((CommandLineConfiguration)config).Sandbox.FileSystemMode = FileSystemMode.RealAndMinimalPipGraph;
|
||||
|
||||
var engineResult = CreateAndRunEngine(
|
||||
config,
|
||||
appDeployment,
|
||||
testRootDirectory: null,
|
||||
rememberAllChangedTrackedInputs: true,
|
||||
engine: out var engine,
|
||||
testCache: testCache,
|
||||
detoursListener: detoursListener);
|
||||
|
||||
return engineResult;
|
||||
}
|
||||
}
|
||||
|
||||
private string DefaultYarnPrelude(
|
||||
Dictionary<string, DiscriminatingUnion<string, UnitValue>> environment,
|
||||
string yarnLocation,
|
||||
string moduleName) => $@"
|
||||
config({{
|
||||
resolvers: [
|
||||
{{
|
||||
kind: 'Yarn',
|
||||
moduleName: '{moduleName}',
|
||||
root: d`.`,
|
||||
nodeExeLocation: f`{PathToNode}`,
|
||||
{DictionaryToExpression("environment", environment)}
|
||||
{(yarnLocation != null ? $"yarnLocation: f`{yarnLocation}`," : string.Empty)}
|
||||
}}
|
||||
]
|
||||
}});";
|
||||
|
||||
private static string DictionaryToExpression(string memberName, Dictionary<string, DiscriminatingUnion<string, UnitValue>> dictionary)
|
||||
{
|
||||
return (dictionary == null ?
|
||||
string.Empty :
|
||||
$"{memberName}: Map.empty<string, (PassthroughEnvironmentVariable | string)>(){ string.Join(string.Empty, dictionary.Select(property => $".add('{property.Key}', {(property.Value?.GetValue() is UnitValue ? "Unit.unit()" : $"'{property.Value?.GetValue()}'")})")) },");
|
||||
}
|
||||
|
||||
private bool YarnInit(ICommandLineConfiguration config)
|
||||
{
|
||||
// Create a package.json, root of all the workspaces. This package needs to be private
|
||||
// since workspaces need to be declared in a private one
|
||||
var result = YarnRun(config, "init --private --yes");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the root package.json to enable workspaces
|
||||
var pathToPackageJson = config.Layout.SourceDirectory.Combine(PathTable, "package.json").ToString(PathTable);
|
||||
string mainJson = File.ReadAllText(pathToPackageJson);
|
||||
int closingBracket = mainJson.LastIndexOf('}');
|
||||
mainJson = mainJson.Insert(closingBracket, @",
|
||||
""workspaces"": {
|
||||
""packages"": [
|
||||
""src/*""
|
||||
]}");
|
||||
File.WriteAllText(pathToPackageJson, mainJson);
|
||||
|
||||
return YarnRun(config, "install");
|
||||
}
|
||||
|
||||
private bool YarnRun(ICommandLineConfiguration config, string yarnArgs)
|
||||
{
|
||||
string arguments = $"{PathToYarn}.js {yarnArgs}";
|
||||
string filename = PathToNode;
|
||||
|
||||
// Unfortunately, capturing standard out/err non-deterministically hangs node.exe on exit when
|
||||
// concurrent npm install operations happen. Found reported bugs about this that look similar enough
|
||||
// to the problem that manifested here.
|
||||
// So we just report exit codes.
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = filename,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = config.Layout.SourceDirectory.ToString(PathTable),
|
||||
RedirectStandardError = false,
|
||||
RedirectStandardOutput = false,
|
||||
UseShellExecute = false,
|
||||
};
|
||||
|
||||
startInfo.Environment["PATH"] += $";{PathToNodeFolder}";
|
||||
startInfo.Environment["APPDATA"] = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
||||
var runYarn = Process.Start(startInfo);
|
||||
runYarn.WaitForExit();
|
||||
|
||||
return runYarn.ExitCode == 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Linq;
|
||||
using BuildXL.Pips.Operations;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Test.BuildXL.FrontEnd.Yarn;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Yarn
|
||||
{
|
||||
/// <summary>
|
||||
/// End to end execution tests for Yarn, including pip execution
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The common JavaScript functionality is already tested in the Rush related tests, so we don't duplicate it here.
|
||||
/// </remarks>
|
||||
public class YarnIntegrationTests : YarnIntegrationTestBase
|
||||
{
|
||||
public YarnIntegrationTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EndToEndPipExecutionWithDependencies()
|
||||
{
|
||||
// Create two projects A and B such that A -> B.
|
||||
var config = Build()
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A", "module.exports = function A(){}")
|
||||
.AddJavaScriptProject("@ms/project-B", "src/B", "const A = require('@ms/project-A'); return A();", new string[] { "@ms/project-A"})
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunYarnProjects(config);
|
||||
|
||||
Assert.True(engineResult.IsSuccess);
|
||||
|
||||
// Let's do some basic graph validations
|
||||
var processes = engineResult.EngineState.PipGraph.RetrievePipsOfType(PipType.Process).ToList();
|
||||
// There should be two process pips
|
||||
Assert.Equal(2, processes.Count);
|
||||
|
||||
// Project A depends on project B
|
||||
var projectAPip = engineResult.EngineState.RetrieveProcess("_ms_project_A");
|
||||
var projectBPip = engineResult.EngineState.RetrieveProcess("_ms_project_B");
|
||||
Assert.True(IsDependencyAndDependent(projectAPip, projectBPip));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BuildXL.FrontEnd.JavaScript.Tracing;
|
||||
using BuildXL.Utilities.Configuration;
|
||||
using Test.BuildXL.FrontEnd.Core;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Test.BuildXL.FrontEnd.Yarn.IntegrationTests
|
||||
{
|
||||
public class YarnLocationTests : YarnIntegrationTestBase
|
||||
{
|
||||
public YarnLocationTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
// We don't actually need to execute anything, scheduling is enough
|
||||
protected override EnginePhases Phase => EnginePhases.Schedule;
|
||||
|
||||
[Fact]
|
||||
public void ExplicitInvalidYarnLocationIsHandled()
|
||||
{
|
||||
var config = Build(yarnLocation: "/path/to/foo")
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunYarnProjects(config);
|
||||
|
||||
Assert.False(engineResult.IsSuccess);
|
||||
AssertErrorEventLogged(LogEventId.ProjectGraphConstructionError);
|
||||
AssertErrorEventLogged(global::BuildXL.FrontEnd.Core.Tracing.LogEventId.CannotBuildWorkspace);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PathIsUsedWhenYarnLocationIsUndefined()
|
||||
{
|
||||
// Explicitly undefine the yarn location, but add the path to yarn to PATH
|
||||
var environment = new Dictionary<string, string>
|
||||
{
|
||||
["PATH"] = PathToNodeFolder + Path.PathSeparator + Path.GetDirectoryName(PathToYarn).Replace("\\", "/")
|
||||
};
|
||||
|
||||
var config = Build(environment: environment, yarnLocation: null)
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunYarnProjects(config);
|
||||
|
||||
Assert.True(engineResult.IsSuccess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void YarnInstallationNotFoundIsProperlyHandled()
|
||||
{
|
||||
// Explicitly undefine the yarn location, but do not expose anything in PATH either
|
||||
var config = Build(yarnLocation: null)
|
||||
.AddJavaScriptProject("@ms/project-A", "src/A")
|
||||
.PersistSpecsAndGetConfiguration();
|
||||
|
||||
var engineResult = RunYarnProjects(config);
|
||||
|
||||
Assert.False(engineResult.IsSuccess);
|
||||
AssertErrorEventLogged(LogEventId.CannotFindGraphBuilderTool);
|
||||
AssertErrorEventLogged(global::BuildXL.FrontEnd.Core.Tracing.LogEventId.CannotBuildWorkspace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
import * as Managed from "Sdk.Managed";
|
||||
import * as MSBuild from "Sdk.Selfhost.MSBuild";
|
||||
import * as Frameworks from "Sdk.Managed.Frameworks";
|
||||
import * as Node from "Sdk.NodeJs";
|
||||
import {Transformer} from "Sdk.Transformers";
|
||||
|
||||
namespace Test.Yarn {
|
||||
|
||||
// TODO: to enable this, we should use an older version of NodeJs for Linux
|
||||
// Yarn is not easily available in the hosted machines that run the public build. So excluding these tests for now outside of the internal build
|
||||
const isRunningOnSupportedSystem =
|
||||
Context.getCurrentHost().cpuArchitecture === "x64" &&
|
||||
!BuildXLSdk.isHostOsLinux &&
|
||||
Environment.getFlag("[Sdk.BuildXL]microsoftInternal");
|
||||
|
||||
@@public
|
||||
export const dll = isRunningOnSupportedSystem && BuildXLSdk.test({
|
||||
// QTest is not supporting opaque directories as part of the deployment
|
||||
testFramework: importFrom("Sdk.Managed.Testing.XUnit").framework,
|
||||
runTestArgs: {
|
||||
unsafeTestRunArguments: {
|
||||
// These tests require Detours to run itself, so we won't detour the test runner process itself
|
||||
runWithUntrackedDependencies: true,
|
||||
},
|
||||
},
|
||||
assemblyName: "Test.BuildXL.FrontEnd.Yarn",
|
||||
sources: globR(d`.`, "*.cs"),
|
||||
references: [
|
||||
Script.dll,
|
||||
Core.dll,
|
||||
importFrom("BuildXL.Core.UnitTests").EngineTestUtilities.dll,
|
||||
importFrom("BuildXL.Engine").Engine.dll,
|
||||
importFrom("BuildXL.Engine").Processes.dll,
|
||||
importFrom("BuildXL.Engine").Scheduler.dll,
|
||||
importFrom("BuildXL.FrontEnd").Core.dll,
|
||||
importFrom("BuildXL.FrontEnd").Script.dll,
|
||||
importFrom("BuildXL.FrontEnd").Sdk.dll,
|
||||
importFrom("BuildXL.FrontEnd").JavaScript.dll,
|
||||
importFrom("BuildXL.FrontEnd").Yarn.dll,
|
||||
importFrom("BuildXL.FrontEnd").TypeScript.Net.dll,
|
||||
importFrom("BuildXL.FrontEnd").Utilities.dll,
|
||||
importFrom("BuildXL.FrontEnd").SdkProjectGraph.dll,
|
||||
importFrom("BuildXL.Pips").dll,
|
||||
importFrom("BuildXL.Utilities").dll,
|
||||
importFrom("BuildXL.Utilities").Collections.dll,
|
||||
importFrom("BuildXL.Utilities").Configuration.dll,
|
||||
importFrom("BuildXL.Utilities").Native.dll,
|
||||
importFrom("BuildXL.Utilities").Script.Constants.dll,
|
||||
importFrom("BuildXL.Utilities.Instrumentation").Common.dll,
|
||||
],
|
||||
runtimeContent: [
|
||||
// We need Yarn and Node to run these tests
|
||||
{
|
||||
subfolder: r`yarn`,
|
||||
contents: [importFrom("Yarn").getYarn()],
|
||||
},
|
||||
{
|
||||
subfolder: a`node`,
|
||||
contents: [Node.Node.nodeExecutables]
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<!-- Even though this file is not used by Visual Studio to fetch NuGet
|
||||
packages, it is necessary to tell the VS test discoverer it nees to look for
|
||||
xunit based unit tests. -->
|
||||
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
|
||||
</packages>
|
|
@ -55,6 +55,14 @@ namespace BuildXL.FrontEnd.Yarn
|
|||
foundPath = pathToYarn;
|
||||
break;
|
||||
}
|
||||
|
||||
// In some windows installations, only yarn.cmd exists
|
||||
pathToYarn = absolutePath.Combine(m_context.PathTable, "yarn.cmd");
|
||||
if (m_host.Engine.FileExists(pathToYarn))
|
||||
{
|
||||
foundPath = pathToYarn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ interface PackageJson {
|
|||
}
|
||||
|
||||
if (process.argv.length < 5) {
|
||||
console.log("Expected arguments: <repo-folder> <path-to-output-graph> <path-to-yarn");
|
||||
console.log("Expected arguments: <repo-folder> <path-to-output-graph> <path-to-yarn>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -2881,6 +2881,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Component": {
|
||||
"Type": "NuGet",
|
||||
"NuGet": {
|
||||
"Name": "Npm.OnCloudBuild",
|
||||
"Version": "3.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Component": {
|
||||
"Type": "NuGet",
|
||||
|
|
|
@ -57,6 +57,9 @@ export const pkgs = isMicrosoftInternal ? [
|
|||
// Combined runtimes
|
||||
{ id: "Dotnet-Runtime", version: "5.0.3", osSkip: [ "macOS", "unix" ] },
|
||||
|
||||
// Officially mantained CB package that contains Yarn. Used for Yarn tests.
|
||||
{ id: "Npm.OnCloudBuild", version: "3.1.0" },
|
||||
|
||||
] : [
|
||||
|
||||
// Artifact packages and dependencies in OSS
|
||||
|
|
Загрузка…
Ссылка в новой задаче