Skip to content
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
13 changes: 3 additions & 10 deletions SysManager/SysManager.Tests/AppPackageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,9 @@ public void IsSelected_RaisesPropertyChanged()
Assert.Contains(nameof(AppPackage.IsSelected), raised);
}

[Fact]
public void Status_Transitions_ArePossible()
{
var p = new AppPackage();
foreach (var s in new[] { "Pending", "Upgrading...", "Done", "Failed (exit 1)" })
{
p.Status = s;
Assert.Equal(s, p.Status);
}
}
// Status_Transitions_ArePossible was removed: assigning four strings to a string property and
// reading each back cannot fail. "Transitions are possible" was never in question — what would
// be worth pinning is who WRITES those values, which belongs to the winget service's tests.

[Fact]
public void ScenarioLikeRealData_IsStored()
Expand Down
32 changes: 6 additions & 26 deletions SysManager/SysManager.Tests/CleanupViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,16 +330,9 @@ public void StatusMessage_Setter_RaisesPropertyChanged()
Assert.Equal("hello", vm.StatusMessage);
}

[Fact]
public void Progress_AcceptsFullRange()
{
var vm = NewVm();
foreach (var p in new[] { 0, 1, 50, 99, 100 })
{
vm.Progress = p;
Assert.Equal(p, vm.Progress);
}
}
// Progress_AcceptsFullRange was removed: it looped five values through a bare
// [ObservableProperty] and read each back. The name implied a 0-100 contract that nothing
// enforces (ViewModelBase._progress is unclamped; the ProgressBar clamps visually).

[Fact]
public void IsProgressIndeterminate_TogglesCleanly()
Expand Down Expand Up @@ -386,22 +379,9 @@ public async Task PreScan_EventuallyPopulatesLabels()
Assert.NotEqual("Scanning…", vm.RecycleBinLabel);
}

[Fact]
public void TempSizeLabel_CanBeSetDirectly()
{
var vm = NewVm();
vm.TempSizeLabel = "42.0 MB can be freed";
Assert.Equal("42.0 MB can be freed", vm.TempSizeLabel);
}

[Fact]
public void RecycleBinLabel_CanBeSetDirectly()
{
var vm = NewVm();
vm.RecycleBinLabel = "Empty";
Assert.Equal("Empty", vm.RecycleBinLabel);
}

// A "…_CanBeSetDirectly" round-trip was removed here: it set a bare [ObservableProperty]
// and read it straight back, so only the source generator could fail it. The labels' real
// behaviour is covered by …_DefaultIsScanning and PreScan_EventuallyPopulatesLabels above.
// ---------- progress feedback (regression) ----------
// CleanupView.xaml binds a progress bar to IsBusy and the sidebar spinner reads the same flag,
// but this VM never assigned it — so nothing appeared while SFC or DISM ran, which is minutes of
Expand Down
46 changes: 13 additions & 33 deletions SysManager/SysManager.Tests/DashboardViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,47 +89,27 @@ public void Command_IsExposedAndNotNull(string name)
}

// ---------- property setters ----------

[Fact]
public void OsLine_Setter_Works()
{
var vm = NewVm();
vm.OsLine = "Windows 11 Pro";
Assert.Equal("Windows 11 Pro", vm.OsLine);
}

[Fact]
public void UptimeLine_Setter_Works()
{
var vm = NewVm();
vm.UptimeLine = "Uptime 3d 5h";
Assert.Equal("Uptime 3d 5h", vm.UptimeLine);
}

[Fact]
public void CpuPercent_Setter_Works()
{
var vm = NewVm();
vm.CpuPercent = 42.5;
Assert.Equal(42.5, vm.CpuPercent);
}

[Fact]
public void RamPercent_Setter_Works()
{
var vm = NewVm();
vm.RamPercent = 67.3;
Assert.Equal(67.3, vm.RamPercent);
}
// The four "…_Setter_Works" round-trips that lived here were removed: each set a bare
// [ObservableProperty] and read it straight back, which can only fail if the CommunityToolkit
// source generator breaks. What a binding depends on — the change notification — is covered by
// Setter_FiresPropertyChanged below, which the two percentages were added to.

// ---------- PropertyChanged ----------

/// <summary>
/// Every bound property must raise <c>PropertyChanged</c>: the dashboard is written by a background
/// poll loop, so the UI only updates on the notification. The parameter is <c>object</c> so the two
/// percentages can join the string rows — they arrived here when their standalone round-trip tests
/// were removed, because notification is the half that a binding actually depends on.
/// </summary>
[Theory]
[InlineData(nameof(DashboardViewModel.OsLine), "test")]
[InlineData(nameof(DashboardViewModel.UptimeLine), "test")]
[InlineData(nameof(DashboardViewModel.CpuName), "test")]
[InlineData(nameof(DashboardViewModel.GpuName), "test")]
public void Setter_FiresPropertyChanged(string propName, string value)
[InlineData(nameof(DashboardViewModel.CpuPercent), 42.5)]
[InlineData(nameof(DashboardViewModel.RamPercent), 67.3)]
public void Setter_FiresPropertyChanged(string propName, object value)
{
var vm = NewVm();
var fired = false;
Expand Down
11 changes: 4 additions & 7 deletions SysManager/SysManager.Tests/DuplicateFileViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,10 @@ public void BrowseFolderCommand_Exists()
Assert.NotNull(vm.BrowseFolderCommand);
}

[Fact]
public void MinSizeKb_CanBeChanged()
{
var vm = NewVm();
vm.MinSizeKb = 500;
Assert.Equal(500, vm.MinSizeKb);
}
// MinSizeKb_CanBeChanged was removed as a setter round-trip. The property's real consequence is
// `var minBytes = MinSizeKb * 1024` in ScanAsync, and asserting THAT needs a guard the code does
// not have yet (a large typed value overflows long and inverts the filter) — tracked separately
// so this test-only change stays behaviour-neutral.

[Fact]
public void SelectedFolder_CanBeChanged()
Expand Down
29 changes: 24 additions & 5 deletions SysManager/SysManager.Tests/FriendlyEventEntryExtendedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,32 @@ public void SeverityColor_UnknownSeverity_GivesFallback()
Assert.Equal(StatusColors.Neutral, e.SeverityColor);
}

/// <summary>
/// The extreme timestamps a real event log can hand us must not produce nonsense in the two strings
/// the user actually sees.
/// <para>Replaces an assertion that stored <c>DateTime.MinValue</c>/<c>MaxValue</c> and read them back
/// — a round-trip through a generated setter, proving only that a <c>DateTime</c> field holds a
/// <c>DateTime</c>. The reachable question is what <c>RelativeTime</c> and <c>FullTimestamp</c> DO with
/// those values, since both are computed from <c>Timestamp</c> and <c>RelativeTime</c> subtracts it
/// from <c>DateTime.Now</c>.</para>
/// <para>This pins the behaviour rather than changing it: MinValue is special-cased to an em dash, and
/// a FUTURE stamp (a clock correction, or a log copied from a machine running ahead) yields "just now",
/// because the negative span falls into the first branch. Imprecise but harmless — and far better than
/// printing "in -3d". Asserted so a future reordering of those branches cannot silently start showing
/// the user a negative duration.</para>
/// </summary>
[Fact]
public void TimestampsVastlyDifferent_StillStorable()
public void ExtremeTimestamps_ProduceSaneDisplayStrings()
{
var e = new FriendlyEventEntry { Timestamp = DateTime.MinValue };
Assert.Equal(DateTime.MinValue, e.Timestamp);
e.Timestamp = DateTime.MaxValue;
Assert.Equal(DateTime.MaxValue, e.Timestamp);
var min = new FriendlyEventEntry { Timestamp = DateTime.MinValue };
Assert.Equal("—", min.RelativeTime);
Assert.Equal("0001-01-01 00:00:00", min.FullTimestamp);

var future = new FriendlyEventEntry { Timestamp = DateTime.MaxValue };
Assert.Equal("just now", future.RelativeTime);
Assert.Equal("9999-12-31 23:59:59", future.FullTimestamp);
// The point of the assertion: no negative duration ever reaches the user.
Assert.DoesNotContain("-", future.RelativeTime);
}

[Fact]
Expand Down
9 changes: 2 additions & 7 deletions SysManager/SysManager.Tests/PerformanceViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,8 @@ public void Constructor_WantToggles_DefaultFalse()

// ── Property changes ──

[Fact]
public void SelectedPlan_CanBeChanged()
{
var vm = NewVm();
vm.SelectedPlan = "ultimate";
Assert.Equal("ultimate", vm.SelectedPlan);
}
// SelectedPlan_CanBeChanged was removed as a pure setter round-trip: the default is pinned by
// Constructor_SelectedPlan_DefaultBalanced and the notification by SelectedPlan_NotifiesPropertyChanged.

[Fact]
public void WantVisualEffectsReduced_CanBeToggled()
Expand Down
14 changes: 3 additions & 11 deletions SysManager/SysManager.Tests/PingTargetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,7 @@ public void Role_CanBeSet(TargetRole role)
Assert.Equal(role, t.Role);
}

[Fact]
public void Stats_CanBeUpdated()
{
var t = new PingTarget();
t.AverageMs = 25.3;
t.JitterMs = 2.1;
t.LossPercent = 5.0;
Assert.Equal(25.3, t.AverageMs);
Assert.Equal(2.1, t.JitterMs);
Assert.Equal(5.0, t.LossPercent);
}
// Stats_CanBeUpdated was removed: three doubles assigned and read back on a model with no
// computed members over them. PingTarget's meaningful behaviour is in the ping service, which
// has its own tests.
}
42 changes: 40 additions & 2 deletions SysManager/SysManager.Tests/ProcessManagerViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,50 @@ public void FilterText_DefaultEmpty()
Assert.Equal("", vm.FilterText);
}

/// <summary>
/// Typing in the search box must actually narrow the bound list.
/// <para>Replaces an assertion that set <c>FilterText</c> and then read <c>FilterText</c> back — a
/// round-trip through a generated <c>[ObservableProperty]</c> setter, which can only fail if the
/// source generator itself breaks. What the user depends on is the CONSEQUENCE:
/// <c>OnFilterTextChanged</c> calls <c>ApplyFilter</c>, which rebuilds <c>FilteredProcesses</c> —
/// the collection the DataGrid binds to. A filter wired to nothing is a defect this project has
/// shipped before (batch 30, five unreachable Services filters), and the old assertion could not
/// have caught it.</para>
/// </summary>
[Fact]
public void FilterText_CanBeChanged()
public void FilterText_NarrowsTheBoundList()
{
var vm = new ProcessManagerViewModel(new Services.ProcessManagerService());
vm.Processes.Clear();
vm.Processes.Add(new ProcessEntry { Pid = 1, Name = "chrome", MemoryBytes = 300 });
vm.Processes.Add(new ProcessEntry { Pid = 2, Name = "notepad", MemoryBytes = 200 });

vm.FilterText = "chrome";
Assert.Equal("chrome", vm.FilterText);

Assert.Equal(["chrome"], vm.FilteredProcesses.Select(p => p.Name).ToArray());

// …and clearing it restores the full list, so the filter is not one-way.
vm.FilterText = "";
Assert.Equal(2, vm.FilteredProcesses.Count);
}

/// <summary>
/// The filter matches a PID and the description fields too, not only the name: three separate
/// <c>Matches…</c> helpers feed it and only the name path was ever exercised.
/// </summary>
[Fact]
public void FilterText_AlsoMatchesPidAndDescription()
{
var vm = new ProcessManagerViewModel(new Services.ProcessManagerService());
vm.Processes.Clear();
vm.Processes.Add(new ProcessEntry { Pid = 4321, Name = "svchost", PlainDescription = "Windows service host" });
vm.Processes.Add(new ProcessEntry { Pid = 9, Name = "notepad" });

vm.FilterText = "4321";
Assert.Equal(["svchost"], vm.FilteredProcesses.Select(p => p.Name).ToArray());

vm.FilterText = "service host";
Assert.Equal(["svchost"], vm.FilteredProcesses.Select(p => p.Name).ToArray());
}

[Fact]
Expand Down
38 changes: 36 additions & 2 deletions SysManager/SysManager.Tests/UninstallerViewModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,46 @@ public void Summary_HasDefaultValue()
Assert.False(string.IsNullOrEmpty(vm.Summary));
}

/// <summary>
/// Typing in the search box must narrow the bound list AND correct the count and summary the user
/// reads above it. Replaces a <c>FilterText</c> round-trip that only exercised the generated setter;
/// <c>ApplyFilter</c> rebuilds <c>FilteredApps</c>, recomputes <c>AppCount</c> and rewrites
/// <c>Summary</c>, and none of those three was asserted anywhere.
/// </summary>
[Fact]
public void FilterText_CanBeChanged()
public void FilterText_NarrowsTheListAndCorrectsTheCountAndSummary()
{
var vm = NewVm();
vm.AllApps.Clear();
vm.AllApps.Add(new InstalledApp { Name = "Google Chrome", Id = "Google.Chrome" });
vm.AllApps.Add(new InstalledApp { Name = "Notepad++", Id = "Notepad.Plus" });

vm.FilterText = "chrome";
Assert.Equal("chrome", vm.FilterText);

Assert.Equal(["Google Chrome"], vm.FilteredApps.Select(a => a.Name).ToArray());
Assert.Equal(1, vm.AppCount);
Assert.Contains("of 2 total", vm.Summary);

// Clearing it restores everything, and the summary drops the "(of N total)" qualifier because
// nothing is filtered out any more.
vm.FilterText = "";
Assert.Equal(2, vm.FilteredApps.Count);
Assert.Equal(2, vm.AppCount);
Assert.DoesNotContain("of 2 total", vm.Summary);
}

/// <summary>The filter matches the package Id too, not just the display name.</summary>
[Fact]
public void FilterText_AlsoMatchesThePackageId()
{
var vm = NewVm();
vm.AllApps.Clear();
vm.AllApps.Add(new InstalledApp { Name = "Google Chrome", Id = "Google.Chrome" });
vm.AllApps.Add(new InstalledApp { Name = "Notepad++", Id = "Notepad.Plus" });

vm.FilterText = "Notepad.Plus";

Assert.Equal(["Notepad++"], vm.FilteredApps.Select(a => a.Name).ToArray());
}

[Fact]
Expand Down