Skip to content

GH4055: Add task skip reason to task summary output #4056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions src/Cake.Cli/Infrastructure/CakeSpectreReportPrinter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Cake.Core;
using Cake.Core.Diagnostics;
using Spectre.Console;

namespace Cake.Cli
{
/// <summary>
/// The default report printer.
/// </summary>
public sealed class CakeSpectreReportPrinter : ICakeReportPrinter
{
private readonly IAnsiConsole _console;

/// <summary>
/// Initializes a new instance of the <see cref="CakeSpectreReportPrinter"/> class.
/// </summary>
/// <param name="console">The console.</param>
public CakeSpectreReportPrinter(IAnsiConsole console)
{
_console = console;
}

/// <inheritdoc/>
public void Write(CakeReport report)
{
// Create a table
var table = new Table().Border(TableBorder.SimpleHeavy);
table.Width(100);
table.BorderStyle(new Style().Foreground(ConsoleColor.Green));

var includeSkippedReasonColumn = report.Any(r => !string.IsNullOrEmpty(r.SkippedMessage));
var rowStyle = new Style(ConsoleColor.Green);

// Add some columns
table.AddColumn(new TableColumn(new Text("Task", rowStyle)).Footer(new Text("Total:", rowStyle)).PadRight(10));
table.AddColumn(
new TableColumn(
new Text("Duration", rowStyle)).Footer(
new Text(FormatTime(GetTotalTime(report)), rowStyle)));

if (includeSkippedReasonColumn)
{
table.AddColumn(new TableColumn(new Text("Skip Reason", rowStyle)));
}

foreach (var item in report)
{
var itemStyle = GetItemStyle(item);

if (includeSkippedReasonColumn)
{
table.AddRow(new Markup(item.TaskName, itemStyle),
new Markup(FormatDuration(item), itemStyle),
new Markup(item.SkippedMessage, itemStyle));
}
else
{
table.AddRow(new Markup(item.TaskName, itemStyle),
new Markup(FormatDuration(item), itemStyle));
}
}

// Render the table to the console
_console.Write(table);
}

/// <inheritdoc/>
public void WriteStep(string name, Verbosity verbosity)
{
if (verbosity < Verbosity.Normal)
{
return;
}

var table = new Table().Border(DoubleBorder.Shared);
table.Width(100);
table.AddColumn(name);
_console.Write(new Padder(table).Padding(0, 1, 0, 0));
}

/// <inheritdoc/>
public void WriteLifeCycleStep(string name, Verbosity verbosity)
{
if (verbosity < Verbosity.Normal)
{
return;
}

_console.WriteLine();

var table = new Table().Border(SingleBorder.Shared);
table.Width(100);
table.AddColumn(name);
_console.Write(table);
}

/// <inheritdoc/>
public void WriteSkippedStep(string name, Verbosity verbosity)
{
if (verbosity < Verbosity.Verbose)
{
return;
}

_console.WriteLine();

var table = new Table().Border(DoubleBorder.Shared);
table.Width(100);
table.AddColumn(name);
_console.Write(table);
}

private static string FormatDuration(CakeReportEntry item)
{
if (item.ExecutionStatus == CakeTaskExecutionStatus.Skipped)
{
return "Skipped";
}

return FormatTime(item.Duration);
}

private static Style GetItemStyle(CakeReportEntry item)
{
if (item.Category == CakeReportEntryCategory.Setup || item.Category == CakeReportEntryCategory.Teardown)
{
return new Style(ConsoleColor.Cyan);
}

if (item.ExecutionStatus == CakeTaskExecutionStatus.Failed)
{
return new Style(ConsoleColor.Red);
}

if (item.ExecutionStatus == CakeTaskExecutionStatus.Executed)
{
return new Style(ConsoleColor.Green);
}

return new Style(ConsoleColor.Gray);
}

private static string FormatTime(TimeSpan time)
{
return time.ToString("c", CultureInfo.InvariantCulture);
}

private static TimeSpan GetTotalTime(IEnumerable<CakeReportEntry> entries)
{
return entries.Select(i => i.Duration)
.Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2);
}
}
}
35 changes: 35 additions & 0 deletions src/Cake.Cli/Infrastructure/DoubleBorder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Spectre.Console;
using Spectre.Console.Rendering;

namespace Cake.Cli
{
/// <summary>
/// A custom Spectre.Console border class, used for outputting information about steps.
/// </summary>
public class DoubleBorder : TableBorder
{
/// <summary>
/// Gets a single instance of the DoubleBorder class.
/// </summary>
public static DoubleBorder Shared { get; } = new DoubleBorder();

/// <summary>
/// Get information about the custom border.
/// </summary>
/// <param name="part">The part that needs a border applied to it.</param>
/// <returns>A simple double border character.</returns>
public override string GetPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTop => "=",
TableBorderPart.FooterBottom => "=",
_ => string.Empty,
};
}
}
}
35 changes: 35 additions & 0 deletions src/Cake.Cli/Infrastructure/SingleBorder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Spectre.Console;
using Spectre.Console.Rendering;

namespace Cake.Cli
{
/// <summary>
/// A custom Spectre.Console border class, used for outputting information about steps.
/// </summary>
public class SingleBorder : TableBorder
{
/// <summary>
/// Gets a single instance of the SingleBorder class.
/// </summary>
public static SingleBorder Shared { get; } = new SingleBorder();

/// <summary>
/// Get information about the custom border.
/// </summary>
/// <param name="part">The part that needs a border applied to it.</param>
/// <returns>A simple single border character.</returns>
public override string GetPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTop => "-",
TableBorderPart.FooterBottom => "-",
_ => string.Empty,
};
}
}
}
4 changes: 3 additions & 1 deletion src/Cake.Core.Tests/Fixtures/CakeEngineFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal sealed class CakeEngineFixture
public ICakeArguments Arguments { get; set; }
public IProcessRunner ProcessRunner { get; set; }
public ICakeContext Context { get; set; }
public ICakeReportPrinter ReportPrinter { get; set; }
public IExecutionStrategy ExecutionStrategy { get; set; }
public ICakeDataService DataService { get; set; }

Expand All @@ -28,7 +29,8 @@ public CakeEngineFixture()
Globber = Substitute.For<IGlobber>();
Arguments = Substitute.For<ICakeArguments>();
ProcessRunner = Substitute.For<IProcessRunner>();
ExecutionStrategy = new DefaultExecutionStrategy(Log);
ReportPrinter = Substitute.For<ICakeReportPrinter>();
ExecutionStrategy = new DefaultExecutionStrategy(Log, ReportPrinter);
DataService = Substitute.For<ICakeDataService>();

Context = Substitute.For<ICakeContext>();
Expand Down
10 changes: 5 additions & 5 deletions src/Cake.Core.Tests/Unit/CakeReportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void Should_Add_A_New_Task()
var taskName = "task";

// When
report.AddSkipped(taskName);
report.AddSkipped(taskName, "This task was skipped for a great reason!");

// Then
var firstTask = report.First();
Expand All @@ -75,12 +75,12 @@ public void Should_Add_To_End_Of_Sequence()
{
// Given
var report = new CakeReport();
report.AddSkipped("task 1");
report.AddSkipped("task 1", "This task was skipped for a great reason!");

var taskName = "task 2";

// When
report.AddSkipped(taskName);
report.AddSkipped(taskName, "This task was skipped for a great reason!");

// Then
var lastTask = report.Last();
Expand Down Expand Up @@ -115,7 +115,7 @@ public void Should_Add_To_End_Of_Sequence()
{
// Given
var report = new CakeReport();
report.AddSkipped("task 1");
report.AddSkipped("task 1", "This task was skipped for a great reason!");

var taskName = "task 2";
var duration = TimeSpan.FromMilliseconds(100);
Expand Down Expand Up @@ -156,7 +156,7 @@ public void Should_Add_To_End_Of_Sequence()
{
// Given
var report = new CakeReport();
report.AddSkipped("task 1");
report.AddSkipped("task 1", "This task was skipped for a great reason!");

var taskName = "task 2";
var duration = TimeSpan.FromMilliseconds(100);
Expand Down
13 changes: 12 additions & 1 deletion src/Cake.Core/CakeEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ private async Task RunTask(ICakeContext context, IExecutionStrategy strategy, Ca
{
if (!ShouldTaskExecute(context, task, criteria, isTarget))
{
criteria.CausedSkippingOfTask = true;
SkipTask(context, strategy, task, report, criteria);
skipped = true;
break;
Expand Down Expand Up @@ -437,7 +438,17 @@ private void SkipTask(ICakeContext context, IExecutionStrategy strategy, CakeTas
PerformTaskTeardown(context, strategy, task, TimeSpan.Zero, true, null);

// Add the skipped task to the report.
report.AddSkipped(task.Name);
var skippedTaskCriteria = task.Criterias.FirstOrDefault(c => c.CausedSkippingOfTask == true);
var skippedMessage = string.Empty;
if (skippedTaskCriteria != null)
{
if (!string.IsNullOrEmpty(skippedTaskCriteria.Message))
{
skippedMessage = skippedTaskCriteria.Message;
}
}

report.AddSkipped(task.Name, skippedMessage);
}

private static bool IsDelegatedTask(CakeTask task)
Expand Down
18 changes: 10 additions & 8 deletions src/Cake.Core/CakeReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public CakeReport()
/// <param name="span">The span.</param>
public void Add(string task, TimeSpan span)
{
Add(task, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Executed);
Add(task, string.Empty, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Executed);
}

/// <summary>
Expand All @@ -49,16 +49,17 @@ public void Add(string task, TimeSpan span)
/// <param name="span">The span.</param>
public void Add(string task, CakeReportEntryCategory category, TimeSpan span)
{
Add(task, category, span, CakeTaskExecutionStatus.Executed);
Add(task, string.Empty, category, span, CakeTaskExecutionStatus.Executed);
}

/// <summary>
/// Adds a skipped task result to the report.
/// </summary>
/// <param name="task">The task.</param>
public void AddSkipped(string task)
/// <param name="skippedMessage">The message explaining why the task was skipped.</param>
public void AddSkipped(string task, string skippedMessage)
{
Add(task, CakeReportEntryCategory.Task, TimeSpan.Zero, CakeTaskExecutionStatus.Skipped);
Add(task, skippedMessage, CakeReportEntryCategory.Task, TimeSpan.Zero, CakeTaskExecutionStatus.Skipped);
}

/// <summary>
Expand All @@ -68,7 +69,7 @@ public void AddSkipped(string task)
/// <param name="span">The span.</param>
public void AddFailed(string task, TimeSpan span)
{
Add(task, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Failed);
Add(task, string.Empty, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Failed);
}

/// <summary>
Expand All @@ -78,19 +79,20 @@ public void AddFailed(string task, TimeSpan span)
/// <param name="span">The span.</param>
public void AddDelegated(string task, TimeSpan span)
{
Add(task, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Delegated);
Add(task, string.Empty, CakeReportEntryCategory.Task, span, CakeTaskExecutionStatus.Delegated);
}

/// <summary>
/// Adds a task result to the report.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="skippedMessage">The message explaining why the task was skipped.</param>
/// <param name="category">The category.</param>
/// <param name="span">The span.</param>
/// <param name="executionStatus">The execution status.</param>
public void Add(string task, CakeReportEntryCategory category, TimeSpan span, CakeTaskExecutionStatus executionStatus)
public void Add(string task, string skippedMessage, CakeReportEntryCategory category, TimeSpan span, CakeTaskExecutionStatus executionStatus)
{
_report.Add(new CakeReportEntry(task, category, span, executionStatus));
_report.Add(new CakeReportEntry(task, skippedMessage, category, span, executionStatus));
}

/// <summary>
Expand Down
Loading