public void DependencyOrderWhenParallelAndSkipping( TargetCollection targets, TestConsole console, int clock, int buildStartTime, int test1StartTime, int test2StartTime) { "Given a target that takes a long time to start up" .x(() => Ensure(ref targets).Add(CreateTarget( "build", () => { Thread.Sleep(TimeSpan.FromSeconds(1)); // a weak way to encourage the tests to run first buildStartTime = Interlocked.Increment(ref clock); }))); "And a second target which depends on the first target" .x(() => targets.Add(CreateTarget("test1", new[] { "build" }, () => test1StartTime = Interlocked.Increment(ref clock)))); "And a third target which depends on the first target" .x(() => targets.Add(CreateTarget("test2", new[] { "build" }, () => test2StartTime = Interlocked.Increment(ref clock)))); "When I run all the targets with parallelism, skipping dependencies" .x(() => targets.RunAsync(new List <string> { "--parallel", "--skip-dependencies", "test1", "test2", "build" }, console = new TestConsole())); "Then the first target is run first" .x(() => Assert.Equal(1, buildStartTime)); "And the other targets are run later" .x(() => Assert.Equal(5, test1StartTime + test2StartTime)); }
public void WithInputs(TargetCollection targets, List <int> inputsReceived) { "Given a target with inputs 1 and 2" .x(() => Ensure(ref targets).Add(CreateTarget("default", new[] { 1, 2 }, input => Ensure(ref inputsReceived).Add(input)))); "When I run the target" .x(() => targets.RunAsync(new List <string>(), default, default, default));
public void DoubleTransitiveDependency(TargetCollection targets, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target which depends on the first target" .x(() => targets.Add(CreateTarget("second", new[] { "first" }, () => Ensure(ref ran).Add("second")))); "And a third target which depends on the first target and the second target" .x(() => targets.Add(CreateTarget("third", new[] { "first", "second" }, () => Ensure(ref ran).Add("third")))); "When I run the third target" .x(() => targets.RunAsync(new List <string> { "third" }, default)); "Then all targets are run" .x(() => Assert.Equal(3, ran.Count)); "And the first target is run first" .x(() => Assert.Equal("first", ran[0])); "And the second target is run second" .x(() => Assert.Equal("second", ran[1])); "And the third target is run third" .x(() => Assert.Equal("third", ran[2])); }
public void NotExistentDependencies(TargetCollection targets, TestConsole console, bool anyRan, Exception exception) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => anyRan = true))); "And a second target which depends on the first target and a non-existent target" .x(() => targets.Add(CreateTarget("second", new[] { "first", "non-existing" }, () => anyRan = true))); "And a third target which depends on the second target and another non-existent target" .x(() => targets.Add(CreateTarget("third", new[] { "second", "also-non-existing" }, () => anyRan = true))); "When I run the third target" .x(async() => exception = await Record.ExceptionAsync(() => targets.RunAsync(new List <string> { "third" }, console = new TestConsole()))); "Then the operation fails" .x(() => Assert.NotNull(exception)); "And I am told that the first non-existent target could not be found" .x(() => Assert.Contains("non-existing, required by second", exception.Message)); "And I am told that the second non-existent target could not be found" .x(() => Assert.Contains("also-non-existing, required by third", exception.Message)); "And the other targets are not run" .x(() => Assert.False(anyRan)); }
public void MultipleNonExistent( TargetCollection targets, TestConsole console, bool existing, Exception exception) { "Given an existing target" .x(() => Ensure(ref targets).Add(CreateTarget(nameof(existing), () => existing = true))); "When I run that target and two non-existent targets" .x(async() => exception = await Record.ExceptionAsync(() => targets.RunAsync( new List <string> { nameof(existing), "non-existing", "also-non-existing" }, console = new TestConsole()))); "Then the operation fails" .x(() => Assert.NotNull(exception)); "Then I am told that the first non-existent target could not be found" .x(() => Assert.Contains("non-existing", exception.Message)); "Then I am told that the second non-existent target could not be found" .x(() => Assert.Contains("also-non-existing", exception.Message)); "And the existing target is not run" .x(() => Assert.False(existing)); }
public void NestedDependencies(TargetCollection targets, TestConsole console, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target which depends on the first target" .x(() => targets.Add(CreateTarget("second", new[] { "first" }, () => Ensure(ref ran).Add("second")))); "And a third target which depends on the second target" .x(() => targets.Add(CreateTarget("third", new[] { "second" }, () => Ensure(ref ran).Add("third")))); "When I run the third target" .x(() => targets.RunAsync(new List <string> { "third" }, console = new TestConsole())); "Then all targets are run" .x(() => Assert.Equal(3, ran.Count)); "And the first target is run first" .x(() => Assert.Equal("first", ran[0])); "And the second target is run second" .x(() => Assert.Equal("second", ran[1])); "And the third target is run third" .x(() => Assert.Equal("third", ran[2])); }
public void Default(TargetCollection targets, bool @default, bool other) { "Given a default target" .x(() => Ensure(ref targets).Add(CreateTarget("default", () => @default = true))); "And another target" .x(() => targets.Add(CreateTarget(nameof(other), () => other = true))); "When I run without specifying any target names" .x(() => targets.RunAsync(new List <string>(), default, default));
public void MixingCase(TargetCollection targets, bool first, bool second) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => first = true))); "And another target which depends on the first target with a different case" .x(() => targets.Add(CreateTarget("second", new[] { "FIRST" }, () => second = true))); "When I run the second target with a different case" .x(() => targets.RunAsync(new[] { "SECOND" }, default, default, default));
public void WithoutInputs(TargetCollection targets, bool ran) { "Given a target with missing inputs" .x(() => Ensure(ref targets).Add(CreateTarget("default", Enumerable.Empty <object>(), input => ran = true))); "When I run the target" .x(() => targets.RunAsync(new List <string>(), new TestConsole())); "Then the target is not run" .x(() => Assert.False(ran)); }
public void DryRun(TargetCollection targets, bool ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("target", () => ran = true))); "When I run the target specifying a dry run" .x(() => targets.RunAsync(new List <string> { "target", "-n" }, default)); "Then the target is not run" .x(() => Assert.False(ran)); }
public void FlatDependencies(TargetCollection targets, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target" .x(() => targets.Add(CreateTarget("second", () => Ensure(ref ran).Add("second")))); "And a third target which depends on the first and second target" .x(() => targets.Add(CreateTarget("third", new[] { "first", "second" }, () => Ensure(ref ran).Add("third")))); "When I run the third target" .x(() => targets.RunAsync(new List <string> { "third" }, default, default, default));
public void SelfDependency(TargetCollection targets, Exception exception) { "Given a target which depends on itself" .x(() => Ensure(ref targets).Add(CreateTarget("first", new[] { "first" }))); "When I run the target" .x(async() => exception = await Record.ExceptionAsync(() => targets.RunAsync(new List <string> { "first" }, null))); "Then the operation fails" .x(() => Assert.NotNull(exception)); "And I am told that the circular dependency was detected" .x(() => Assert.Contains("first -> first", exception.Message)); }
public void WithInputs(TargetCollection targets, List <int> inputsReceived) { "Given a target with inputs 1 and 2" .x(() => Ensure(ref targets).Add(CreateTarget("default", new[] { 1, 2 }, input => Ensure(ref inputsReceived).Add(input)))); "When I run the target" .x(() => targets.RunAsync(new List <string>(), new TestConsole())); "Then the target is run twice" .x(() => Assert.Equal(2, inputsReceived.Count)); "And target was run with 1 first" .x(() => Assert.Equal(1, inputsReceived[0])); "And target was run with 2 second" .x(() => Assert.Equal(2, inputsReceived[1])); }
public void Default(TargetCollection targets, TestConsole console, bool @default, bool other) { "Given a default target" .x(() => Ensure(ref targets).Add(CreateTarget("default", () => @default = true))); "And another target" .x(() => targets.Add(CreateTarget(nameof(other), () => other = true))); "When I run without specifying any target names" .x(() => targets.RunAsync(new List <string>(), console = new TestConsole())); "Then the default target is run" .x(() => Assert.True(@default)); "But the other target is not run" .x(() => Assert.False(other)); }
public void SkippingDependencies(TargetCollection targets, TestConsole console, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target which depends on the first target and a non-existent target" .x(() => targets.Add(CreateTarget("second", new[] { "first", "non-existent" }, () => Ensure(ref ran).Add("second")))); "When I run the second target, skipping dependencies" .x(() => targets.RunAsync(new List <string> { "second", "-s" }, console = new TestConsole())); "Then the second target is run" .x(() => Assert.Contains("second", ran)); "But the first target is not run" .x(() => Assert.DoesNotContain("first", ran)); }
public void UnknownOptions(TargetCollection targets, bool ran, Exception exception) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("target", () => ran = true))); "When I run the target specifying unknown options" .x(async() => exception = await Record.ExceptionAsync(() => targets.RunAsync(new List <string> { "target", "-b", "-z" }, default))); "Then the operation fails" .x(() => Assert.NotNull(exception)); "Then I am told that the option is unknown" .x(() => Assert.Contains("Unknown options -b -z", exception.Message)); "Then I am told how to get help" .x(() => Assert.Contains(". \"--help\" for usage", exception.Message)); "And the target is not run" .x(() => Assert.False(ran)); }
public void DoubleDependency(TargetCollection targets, TestConsole console, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target which depends on the first target twice" .x(() => targets.Add(CreateTarget("second", new[] { "first", "first" }, () => Ensure(ref ran).Add("second")))); "When I run the second target" .x(() => targets.RunAsync(new List <string> { "second" }, console = new TestConsole())); "Then both targets are run once" .x(() => Assert.Equal(2, ran.Count)); "And the first target is run first" .x(() => Assert.Equal("first", ran[0])); "And the second target is run second" .x(() => Assert.Equal("second", ran[1])); }
public void DependencyOrderWhenSkipping(TargetCollection targets, TestConsole console, List <string> ran) { "Given a target" .x(() => Ensure(ref targets).Add(CreateTarget("first", () => Ensure(ref ran).Add("first")))); "And a second target which depends on the first target" .x(() => targets.Add(CreateTarget("second", new[] { "first" }, () => Ensure(ref ran).Add("second")))); "When I run the second and first targets, skipping dependencies" .x(() => targets.RunAsync(new List <string> { "--skip-dependencies", "second", "first" }, console = new TestConsole())); "Then all targets are run" .x(() => Assert.Equal(2, ran.Count)); "And the first target is run first" .x(() => Assert.Equal("first", ran[0])); "And the second target is run second" .x(() => Assert.Equal("second", ran[1])); }
public void CircularDependency(TargetCollection targets, Exception exception) { "Given a target which depends on a third target" .x(() => Ensure(ref targets).Add(CreateTarget("first", new[] { "third" }))); "And a second target which depends on the first target" .x(() => targets.Add(CreateTarget("second", new[] { "first" }))); "And a third target which depends on the second target" .x(() => targets.Add(CreateTarget("third", new[] { "second" }))); "When I run the third target" .x(async() => exception = await Record.ExceptionAsync(() => targets.RunAsync(new List <string> { "third" }, default))); "Then the operation fails" .x(() => Assert.NotNull(exception)); "And I am told that the circular dependency was detected" .x(() => Assert.Contains("first -> third -> second -> first", exception.Message)); }
public static Task RunTargetsAsync(IEnumerable <string> args) => targets.RunAsync(args, new SystemConsole());
public async Task <int> OnExecuteAsync() { Exception error = default; try { NeedMono = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); TestFlagsNonParallel = "-parallel none -maxthreads 1 "; // TestFlagsNonParallel = "-parallel none -maxthreads 1 -preenumeratetheories "; TestFlagsParallel = ""; // Find the folder with the solution file BaseFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); while (true) { if (Directory.Exists(Path.Combine(BaseFolder, ".git"))) { break; } BaseFolder = Path.GetDirectoryName(BaseFolder); if (BaseFolder == null) { throw new InvalidOperationException("Could not locate a solution file in the directory hierarchy"); } } ConsoleRunnerExe = Path.Combine(BaseFolder, "src", "xunit.v3.runner.console", "bin", ConfigurationText, "net472", "merged", "xunit.v3.runner.console.exe"); ConsoleRunner32Exe = Path.Combine(BaseFolder, "src", "xunit.v3.runner.console", "bin", ConfigurationText + "_x86", "net472", "merged", "xunit.v3.runner.console.x86.exe"); // Dependent folders PackageOutputFolder = Path.Combine(BaseFolder, "artifacts", "packages"); Directory.CreateDirectory(PackageOutputFolder); TestOutputFolder = Path.Combine(BaseFolder, "artifacts", "test"); Directory.CreateDirectory(TestOutputFolder); // Parse the targets var targetNames = Targets.Select(x => x.ToString()).ToList(); // Turn off test parallelization in CI, for more repeatable test timing if (Targets.Contains(BuildTarget.CI)) { TestFlagsParallel = TestFlagsNonParallel; } // Find target classes var targetCollection = new TargetCollection(); var targets = Assembly.GetExecutingAssembly() .ExportedTypes .Select(x => new { type = x, attr = x.GetCustomAttribute <TargetAttribute>() }) .Where(x => x.attr != null); foreach (var target in targets) { var method = target.type.GetRuntimeMethod("OnExecute", new[] { typeof(BuildContext) }); if (method == null) { targetCollection.Add(new Target(target.attr.TargetName, target.attr.DependentTargets)); } else { targetCollection.Add(new ActionTarget(target.attr.TargetName, target.attr.DependentTargets, async() => { var sw = Stopwatch.StartNew(); try { await(Task) method.Invoke(null, new[] { this }); } finally { if (Timing) { WriteLineColor(ConsoleColor.Cyan, $"TIMING: Target '{target.attr.TargetName}' took {sw.Elapsed}{Environment.NewLine}"); } } })); } } var swTotal = Stopwatch.StartNew(); // Let Bullseye run the target(s) await targetCollection.RunAsync(targetNames, SkipDependencies, dryRun : false, parallel : false, new NullLogger(), _ => false); WriteLineColor(ConsoleColor.Green, $"==> Build success! <=={Environment.NewLine}"); if (Timing) { WriteLineColor(ConsoleColor.Cyan, $"TIMING: Build took {swTotal.Elapsed}{Environment.NewLine}"); } return(0); } catch (Exception ex) { error = ex; while (error is TargetInvocationException || error is TargetFailedException) { error = error.InnerException; } } Console.WriteLine(); if (error is NonZeroExitCodeException nonZeroExit) { WriteLineColor(ConsoleColor.Red, "==> Build failed! <=="); return(nonZeroExit.ExitCode); } WriteLineColor(ConsoleColor.Red, $"==> Build failed! An unhandled exception was thrown <=="); Console.WriteLine(error.ToString()); return(-1); }
public static Task RunTargetsAsync(IEnumerable <string> args) => targets.RunAsync(args);
async Task <int> OnExecuteAsync() { Exception error = default; try { NeedMono = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // Find the folder with the solution file BaseFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); while (true) { if (Directory.GetFiles(BaseFolder, "*.sln").Count() != 0) { break; } BaseFolder = Path.GetDirectoryName(BaseFolder); if (BaseFolder == null) { throw new InvalidOperationException("Could not locate a solution file in the directory hierarchy"); } } // Dependent folders PackageOutputFolder = Path.Combine(BaseFolder, "artifacts", "packages"); Directory.CreateDirectory(PackageOutputFolder); TestOutputFolder = Path.Combine(BaseFolder, "artifacts", "test"); Directory.CreateDirectory(TestOutputFolder); var homeFolder = NeedMono ? Environment.GetEnvironmentVariable("HOME") : Environment.GetEnvironmentVariable("USERPROFILE"); var nuGetCliFolder = Path.Combine(homeFolder, ".nuget", "cli", NuGetVersion); Directory.CreateDirectory(nuGetCliFolder); NuGetExe = Path.Combine(nuGetCliFolder, "nuget.exe"); NuGetUrl = $"https://dist.nuget.org/win-x86-commandline/v{NuGetVersion}/nuget.exe"; // Parse the targets and Bullseye-specific arguments var bullseyeArguments = Targets.Select(x => x.ToString()); if (SkipDependencies) { bullseyeArguments = bullseyeArguments.Append("--skip-dependencies"); } // Find target classes var targetCollection = new TargetCollection(); var targets = Assembly.GetExecutingAssembly() .ExportedTypes .Select(x => new { type = x, attr = x.GetCustomAttribute <TargetAttribute>() }) .Where(x => x.attr != null); foreach (var target in targets) { var method = target.type.GetRuntimeMethod("OnExecute", new[] { typeof(BuildContext) }); if (method == null) { targetCollection.Add(new Target(target.attr.TargetName, target.attr.DependentTargets)); } else { targetCollection.Add(new ActionTarget(target.attr.TargetName, target.attr.DependentTargets, () => (Task)method.Invoke(null, new[] { this }))); } } // Let Bullseye run the target(s) await targetCollection.RunAsync(bullseyeArguments.ToList(), SkipDependencies, false, false, new NullLogger(), null); return(0); } catch (Exception ex) { error = ex; while (error is TargetInvocationException || error is TargetFailedException) { error = error.InnerException; } } Console.WriteLine(); if (error is NonZeroExitCodeException nonZeroExit) { WriteLineColor(ConsoleColor.Red, "==> Build failed! <=="); return(nonZeroExit.ExitCode); } WriteLineColor(ConsoleColor.Red, $"==> Build failed! An unhandled exception was thrown <=="); Console.WriteLine(error.ToString()); return(-1); }
/// <summary> /// Runs the previously specified targets. /// In most cases, <see cref="RunTargetsAndExitAsync(IEnumerable{string}, Func{Exception, bool})"/> should be used instead of this method. /// This method should only be used if continued code execution after running targets is specifically required. /// </summary> /// <param name="args">The command line arguments.</param> /// <param name="messageOnly"> /// A predicate that is called when an exception is thrown. /// Return <c>true</c> to display only the exception message instead instead of the full exception details. /// </param> /// <returns>A <see cref="Task"/> that represents the asynchronous running of the targets.</returns> public static Task RunTargetsWithoutExitingAsync(IEnumerable <string> args, Func <Exception, bool> messageOnly) => targets.RunAsync(args, messageOnly);
/// <summary> /// Runs the previously specified targets. /// In most cases, <see cref="RunTargetsAndExitAsync(IEnumerable{string}, Func{Exception, bool}, string)"/> should be used instead of this method. /// This method should only be used if continued code execution after running targets is specifically required. /// </summary> /// <param name="args">The command line arguments.</param> /// <param name="messageOnly"> /// A predicate that is called when an exception is thrown. /// Return <c>true</c> to display only the exception message instead instead of the full exception details. /// </param> /// <param name="logPrefix"> /// The prefix to use for log messages. /// If not specified or <c>null</c>, the name of the entry assembly will be used, as returned by <see cref="System.Reflection.Assembly.GetEntryAssembly"/>. /// If the entry assembly is <c>null</c>, the default prefix of "Bullseye" is used. /// </param> /// <returns>A <see cref="Task"/> that represents the asynchronous running of the targets.</returns> public static Task RunTargetsWithoutExitingAsync(IEnumerable <string> args, Func <Exception, bool> messageOnly = null, string logPrefix = null) => targets.RunAsync(args, messageOnly, logPrefix);
public async Task <int> OnExecuteAsync() { Exception error = default; try { NeedMono = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); TestFlagsNonParallel = "-parallel none -maxthreads 1"; // TestFlagsParallel = "-parallel all -maxthreads 16"; TestFlagsParallel = "-parallel collections -maxthreads 16"; // Find the folder with the solution file BaseFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); while (true) { if (Directory.GetFiles(BaseFolder, "*.sln").Count() != 0) { break; } BaseFolder = Path.GetDirectoryName(BaseFolder); if (BaseFolder == null) { throw new InvalidOperationException("Could not locate a solution file in the directory hierarchy"); } } // Dependent folders PackageOutputFolder = Path.Combine(BaseFolder, "artifacts", "packages"); Directory.CreateDirectory(PackageOutputFolder); TestOutputFolder = Path.Combine(BaseFolder, "artifacts", "test"); Directory.CreateDirectory(TestOutputFolder); // Parse the targets var targetNames = Targets.Select(x => x.ToString()).ToList(); // Turn off test parallelization in CI, for more repeatable test timing if (Targets.Contains(BuildTarget.CI)) { TestFlagsParallel = TestFlagsNonParallel; } // Find target classes var targetCollection = new TargetCollection(); foreach (var target in Assembly.GetExecutingAssembly() .ExportedTypes .Select(x => new { type = x, attr = x.GetCustomAttribute <TargetAttribute>() }) .Where(x => x.attr != null)) { var method = target.type.GetRuntimeMethod("OnExecute", new[] { typeof(BuildContext) }); if (method == null) { targetCollection.Add(new Target(target.attr.TargetName, target.attr.DependentTargets)); } else { targetCollection.Add(new ActionTarget(target.attr.TargetName, target.attr.DependentTargets, () => (Task)method.Invoke(null, new[] { this }))); } } // Let Bullseye run the target(s) await targetCollection.RunAsync(targetNames, SkipDependencies, dryRun : false, parallel : false, new NullLogger(), _ => false); return(0); } catch (Exception ex) { error = ex; while (error is TargetInvocationException || error is TargetFailedException) { error = error.InnerException; } } Console.WriteLine(); if (error is NonZeroExitCodeException nonZeroExit) { WriteLineColor(ConsoleColor.Red, "==> Build failed! <=="); return(nonZeroExit.ExitCode); } WriteLineColor(ConsoleColor.Red, $"==> Build failed! An unhandled exception was thrown <=="); Console.WriteLine(error.ToString()); return(-1); }