diff --git a/BuildMonitor.Test/HistoryRunsTests.cs b/BuildMonitor.Test/HistoryRunsTests.cs new file mode 100644 index 0000000..db030db --- /dev/null +++ b/BuildMonitor.Test/HistoryRunsTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2023-2026 ktsu-dev contributors + +namespace ktsu.BuildMonitor.Test; + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Tests for . +/// +/// +/// The History column draws its indicators left to right, and a column narrower than all of them +/// clips on the right. The newest runs therefore have to come first, so clipping drops the oldest. +/// +[TestClass] +public sealed class HistoryRunsTests +{ + private static readonly DateTimeOffset Start = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero); + + /// + /// Builds runs with the given statuses, most recent first, an hour apart. + /// + private static List RunsNewestFirst(params RunStatus[] statuses) => + [.. statuses.Select((status, i) => new Run { Status = status, Started = Start.AddHours(-i) })]; + + [TestMethod] + public void TheNewestRunIsDrawnFirst() + { + // Arrange + List branchRuns = RunsNewestFirst(RunStatus.Running, RunStatus.Success, RunStatus.Success, RunStatus.Success, RunStatus.Success); + + // Act + List history = BuildMonitor.SelectHistoryRuns(branchRuns); + + // Assert + CollectionAssert.AreEqual(branchRuns, history); + } + + [TestMethod] + public void OnlyTheFiveNewestRunsAreShown() + { + // Arrange + List branchRuns = RunsNewestFirst(RunStatus.Running, RunStatus.Failure, RunStatus.Success, RunStatus.Success, RunStatus.Success, RunStatus.Failure); + + // Act + List history = BuildMonitor.SelectHistoryRuns(branchRuns); + + // Assert + CollectionAssert.AreEqual(branchRuns.Take(5).ToList(), history); + } + + [TestMethod] + public void CanceledRunsAreSkipped() + { + // Arrange + List branchRuns = RunsNewestFirst(RunStatus.Canceled, RunStatus.Success, RunStatus.Failure); + + // Act + List history = BuildMonitor.SelectHistoryRuns(branchRuns); + + // Assert + CollectionAssert.AreEqual(branchRuns.Skip(1).ToList(), history); + } +} diff --git a/BuildMonitor/BuildMonitor.cs b/BuildMonitor/BuildMonitor.cs index 41606a9..adc05de 100644 --- a/BuildMonitor/BuildMonitor.cs +++ b/BuildMonitor/BuildMonitor.cs @@ -749,17 +749,27 @@ internal static bool ShouldShowBuildBranch(Build build, BranchName branch) return shouldShow; } - private static void ShowBranchHistory(List branchRuns) + /// + /// Picks the runs the History column shows from a branch's runs, most recent first. + /// + /// + /// The indicators are drawn left to right, and a column too narrow for all of them clips on the + /// right, so the newest come first: clipping then drops the oldest runs rather than the newest. + /// + /// The branch's runs, most recent first. + /// Up to five runs that were not canceled, in the order they are drawn. + internal static List SelectHistoryRuns(IEnumerable branchRuns) { const int recentRuns = 5; - List runs = [.. branchRuns + return [.. branchRuns .Where(r => r.Status != RunStatus.Canceled) .Take(recentRuns)]; + } - runs.Reverse(); - - foreach (Run? run in runs) + private static void ShowBranchHistory(List branchRuns) + { + foreach (Run? run in SelectHistoryRuns(branchRuns)) { ImGui.SameLine(); ImGuiWidgets.ColorIndicator(GetStatusColor(run.Status), true); diff --git a/README.md b/README.md index 89ef5c6..37cfc46 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ The main interface displays a table of all your builds grouped by workflow and b - **Status**: Current status (Pending, Running, Success, Failure, Canceled) - **Last Run**: Timestamp of when the build started - **Duration**: How long the build has been running or took to complete -- **History**: Visual history of the last 5 builds on this branch +- **History**: Visual history of the last 5 builds on this branch, newest first - **Progress**: Progress bar for ongoing builds - **ETA**: Estimated time to completion for ongoing builds - **Errors**: Error messages from failed builds (click to view full details)