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
19 changes: 10 additions & 9 deletions SysManager/SysManager.IntegrationTests/CleanupCategoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Author: laurentiu021 · https://github.com/laurentiu021/SystemManager
// License: MIT

using SysManager.Helpers;
using SysManager.Models;

namespace SysManager.IntegrationTests;
Expand All @@ -12,13 +13,13 @@ public class CleanupCategoryTests
[InlineData(0L, "0 B")]
[InlineData(1L, "1 B")]
[InlineData(1023L, "1023 B")]
[InlineData(1024L, "1 KB")]
[InlineData(1024L * 1024, "1 MB")]
[InlineData(1024L * 1024 * 1024, "1 GB")]
[InlineData(1024L * 1024 * 1024 * 1024, "1 TB")]
[InlineData(1024L, "1.0 KB")]
[InlineData(1024L * 1024, "1.0 MB")]
[InlineData(1024L * 1024 * 1024, "1.0 GB")]
[InlineData(1024L * 1024 * 1024 * 1024, "1.0 TB")]
public void HumanSize_FormatsCorrectly(long bytes, string expected)
{
Assert.Equal(expected, CleanupCategory.HumanSize(bytes));
Assert.Equal(expected, FormatHelper.FormatSize(bytes));
}

[Theory]
Expand All @@ -27,13 +28,13 @@ public void HumanSize_FormatsCorrectly(long bytes, string expected)
[InlineData(long.MinValue)]
public void HumanSize_NegativeIsZero(long bytes)
{
Assert.Equal("0 B", CleanupCategory.HumanSize(bytes));
Assert.Equal("0 B", FormatHelper.FormatSize(bytes));
}

[Fact]
public void HumanSize_VeryLarge_DoesNotCrash()
{
var s = CleanupCategory.HumanSize(long.MaxValue);
var s = FormatHelper.FormatSize(long.MaxValue);
Assert.NotNull(s);
Assert.Contains("TB", s);
}
Expand Down Expand Up @@ -71,7 +72,7 @@ public void Category_SizeDisplay_UsesHumanSize()
Name = "X", Description = "Y", Paths = Array.Empty<string>(),
TotalSizeBytes = 2048
};
Assert.Equal("2 KB", c.SizeDisplay);
Assert.Equal("2.0 KB", c.SizeDisplay);
}

[Fact]
Expand Down Expand Up @@ -148,7 +149,7 @@ public void LargeFileEntry_SizeDisplay_UsesHumanSize()
SizeBytes = 2048,
LastModified = DateTime.Now
};
Assert.Equal("2 KB", e.SizeDisplay);
Assert.Equal("2.0 KB", e.SizeDisplay);
}

[Fact]
Expand Down
21 changes: 11 additions & 10 deletions SysManager/SysManager.Tests/CleanupCategoryHumanSizeBulkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Author: laurentiu021 · https://github.com/laurentiu021/SystemManager
// License: MIT

using SysManager.Helpers;
using SysManager.Models;

namespace SysManager.Tests;
Expand All @@ -24,7 +25,7 @@ public static IEnumerable<object[]> Powers()
[MemberData(nameof(Powers))]
public void HumanSize_PowersOfTwo_DoNotCrash(long n)
{
var s = CleanupCategory.HumanSize(n);
var s = FormatHelper.FormatSize(n);
Assert.False(string.IsNullOrWhiteSpace(s));
Assert.Matches(@"^[\d.,]+ (B|KB|MB|GB|TB)$", s);
}
Expand All @@ -41,7 +42,7 @@ public void HumanSize_PowersOfTwo_DoNotCrash(long n)
[InlineData(1024L * 1024 * 1024 * 1024, "TB")]
public void HumanSize_PicksCorrectUnit(long bytes, string expectedUnit)
{
var s = CleanupCategory.HumanSize(bytes);
var s = FormatHelper.FormatSize(bytes);
Assert.EndsWith(expectedUnit, s);
}

Expand All @@ -58,7 +59,7 @@ public void HumanSize_PicksCorrectUnit(long bytes, string expectedUnit)
[InlineData(100_000_000_000L)]
public void HumanSize_RealisticSizes_HaveNumericPrefix(long bytes)
{
var s = CleanupCategory.HumanSize(bytes);
var s = FormatHelper.FormatSize(bytes);
Assert.Matches(@"^\d", s);
}

Expand All @@ -68,7 +69,7 @@ public void HumanSize_RealisticSizes_HaveNumericPrefix(long bytes)
[InlineData(long.MinValue, "0 B")]
public void HumanSize_ZeroOrNegative_IsZeroBytes(long bytes, string expected)
{
Assert.Equal(expected, CleanupCategory.HumanSize(bytes));
Assert.Equal(expected, FormatHelper.FormatSize(bytes));
}

[Theory]
Expand All @@ -78,7 +79,7 @@ public void HumanSize_ZeroOrNegative_IsZeroBytes(long bytes, string expected)
[InlineData(1024L * 1024 * 1024 * 1023)]
public void HumanSize_NearBoundary_ValidFormat(long n)
{
Assert.Matches(@"^[\d.,]+ (B|KB|MB|GB|TB)$", CleanupCategory.HumanSize(n));
Assert.Matches(@"^[\d.,]+ (B|KB|MB|GB|TB)$", FormatHelper.FormatSize(n));
}

[Theory]
Expand All @@ -92,7 +93,7 @@ public void HumanSize_NearBoundary_ValidFormat(long n)
[InlineData(15_500_000_000L)]
public void HumanSize_FractionalResult_IsFormatted(long bytes)
{
var s = CleanupCategory.HumanSize(bytes);
var s = FormatHelper.FormatSize(bytes);
Assert.False(string.IsNullOrWhiteSpace(s));
}

Expand All @@ -109,16 +110,16 @@ public void HumanSize_FractionalResult_IsFormatted(long bytes)
[InlineData(512L)]
public void HumanSize_SmallByteValues_StayInBytes(long n)
{
var s = CleanupCategory.HumanSize(n);
var s = FormatHelper.FormatSize(n);
Assert.EndsWith(" B", s);
}

[Fact]
public void HumanSize_SameInputSameOutput()
{
// Determinism
var a = CleanupCategory.HumanSize(123456789);
var b = CleanupCategory.HumanSize(123456789);
var a = FormatHelper.FormatSize(123456789);
var b = FormatHelper.FormatSize(123456789);
Assert.Equal(a, b);
}

Expand All @@ -132,6 +133,6 @@ public void HumanSize_SameInputSameOutput()
[InlineData(1024L * 1024 * 1024 * 1024 * 10, "TB")]
public void HumanSize_LargeMultiples(long n, string unit)
{
Assert.EndsWith(unit, CleanupCategory.HumanSize(n));
Assert.EndsWith(unit, FormatHelper.FormatSize(n));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// SysManager · CleanupCategory.HumanSize extended boundary tests
// SysManager · FormatHelper.FormatSize extended boundary tests
using SysManager.Helpers;
using SysManager.Models;

namespace SysManager.Tests;
Expand All @@ -8,69 +9,69 @@ public class CleanupCategoryHumanSizeExtendedTests
[Fact]
public void HumanSize_Zero_ReturnsZeroB()
{
Assert.Equal("0 B", CleanupCategory.HumanSize(0));
Assert.Equal("0 B", FormatHelper.FormatSize(0));
}

[Fact]
public void HumanSize_Negative_ReturnsZeroB()
{
Assert.Equal("0 B", CleanupCategory.HumanSize(-100));
Assert.Equal("0 B", FormatHelper.FormatSize(-100));
}

[Fact]
public void HumanSize_OneByte_Returns1B()
{
Assert.Equal("1 B", CleanupCategory.HumanSize(1));
Assert.Equal("1 B", FormatHelper.FormatSize(1));
}

[Fact]
public void HumanSize_1023Bytes_Returns1023B()
{
Assert.Equal("1023 B", CleanupCategory.HumanSize(1023));
Assert.Equal("1023 B", FormatHelper.FormatSize(1023));
}

[Fact]
public void HumanSize_1024Bytes_Returns1KB()
{
Assert.Equal("1 KB", CleanupCategory.HumanSize(1024));
Assert.Equal("1.0 KB", FormatHelper.FormatSize(1024));
}

[Fact]
public void HumanSize_1MB_Returns1MB()
{
Assert.Equal("1 MB", CleanupCategory.HumanSize(1024 * 1024));
Assert.Equal("1.0 MB", FormatHelper.FormatSize(1024 * 1024));
}

[Fact]
public void HumanSize_1GB_Returns1GB()
{
Assert.Equal("1 GB", CleanupCategory.HumanSize(1024L * 1024 * 1024));
Assert.Equal("1.0 GB", FormatHelper.FormatSize(1024L * 1024 * 1024));
}

[Fact]
public void HumanSize_1TB_Returns1TB()
{
Assert.Equal("1 TB", CleanupCategory.HumanSize(1024L * 1024 * 1024 * 1024));
Assert.Equal("1.0 TB", FormatHelper.FormatSize(1024L * 1024 * 1024 * 1024));
}

[Fact]
public void HumanSize_1point5GB_FormatsCorrectly()
{
long bytes = (long)(1.5 * 1024 * 1024 * 1024);
Assert.Equal("1.5 GB", CleanupCategory.HumanSize(bytes));
Assert.Equal("1.5 GB", FormatHelper.FormatSize(bytes));
}

[Fact]
public void HumanSize_LargeValue_StaysInTB()
{
long bytes = 10L * 1024 * 1024 * 1024 * 1024;
Assert.Equal("10 TB", CleanupCategory.HumanSize(bytes));
Assert.Equal("10.0 TB", FormatHelper.FormatSize(bytes));
}

[Fact]
public void HumanSize_MaxLong_DoesNotThrow()
{
var result = CleanupCategory.HumanSize(long.MaxValue);
var result = FormatHelper.FormatSize(long.MaxValue);
Assert.Contains("TB", result);
}

Expand All @@ -82,7 +83,7 @@ public void CleanupResult_Summary_NoErrors_FormatsCorrectly()
BytesFreed = 1024 * 1024 * 50,
FilesDeleted = 100
};
Assert.Contains("50 MB", result.Summary);
Assert.Contains("50.0 MB", result.Summary);
Assert.Contains("100", result.Summary);
Assert.DoesNotContain("skipped", result.Summary);
}
Expand Down Expand Up @@ -139,7 +140,7 @@ public void CleanupCategory_SizeDisplay_ShowsHumanSize()
TotalSizeBytes = 2048,
FileCount = 5
};
Assert.Equal("2 KB", cat.SizeDisplay);
Assert.Equal("2.0 KB", cat.SizeDisplay);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public void OpenFileLocation_NonExistentPath_DoesNotThrow()
public void ProcessEntry_MemoryDisplay_FormatsCorrectly()
{
var entry = new ProcessEntry { MemoryBytes = 52_428_800 }; // 50 MB
Assert.Equal("50 MB", entry.MemoryDisplay);
Assert.Equal("50.0 MB", entry.MemoryDisplay);
}

[Fact]
Expand Down
12 changes: 7 additions & 5 deletions SysManager/SysManager/Helpers/FormatHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ namespace SysManager.Helpers;
public static class FormatHelper
{
/// <summary>
/// Formats a byte count into a human-readable string (B, KB, MB, GB).
/// Formats a byte count into a human-readable string (B, KB, MB, GB, TB).
/// </summary>
public static string FormatSize(long bytes) => bytes switch
{
>= 1L << 30 => $"{bytes / (double)(1L << 30):F1} GB",
>= 1L << 20 => $"{bytes / (double)(1L << 20):F1} MB",
>= 1L << 10 => $"{bytes / (double)(1L << 10):F1} KB",
_ => $"{bytes} B"
<= 0 => "0 B",
< 1L << 10 => $"{bytes} B",
< 1L << 20 => $"{bytes / (double)(1L << 10):F1} KB",
< 1L << 30 => $"{bytes / (double)(1L << 20):F1} MB",
< 1L << 40 => $"{bytes / (double)(1L << 30):F1} GB",
_ => $"{bytes / (double)(1L << 40):F1} TB"
};
}
16 changes: 4 additions & 12 deletions SysManager/SysManager/Models/CleanupCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// License: MIT

using CommunityToolkit.Mvvm.ComponentModel;
using SysManager.Helpers;

namespace SysManager.Models;

Expand All @@ -22,17 +23,8 @@ public sealed partial class CleanupCategory : ObservableObject
public TimeSpan? OlderThan { get; init; }
public bool IsDestructiveHint { get; init; }

public string SizeDisplay => HumanSize(TotalSizeBytes);
public string SizeDisplay => FormatHelper.FormatSize(TotalSizeBytes);
public string CountDisplay => $"{FileCount:N0} files";

public static string HumanSize(long bytes)
{
if (bytes <= 0) return "0 B";
string[] u = { "B", "KB", "MB", "GB", "TB" };
double v = bytes; var i = 0;
while (v >= 1024 && i < u.Length - 1) { v /= 1024; i++; }
return $"{v:0.#} {u[i]}";
}
}

public sealed class CleanupResult
Expand All @@ -41,7 +33,7 @@ public sealed class CleanupResult
public int FilesDeleted { get; init; }
public IReadOnlyList<string> Errors { get; init; } = [];
public string Summary =>
$"Freed {CleanupCategory.HumanSize(BytesFreed)} across {FilesDeleted:N0} files" +
$"Freed {FormatHelper.FormatSize(BytesFreed)} across {FilesDeleted:N0} files" +
(Errors.Count > 0 ? $" · {Errors.Count} skipped" : string.Empty);
}

Expand All @@ -52,6 +44,6 @@ public sealed class LargeFileEntry
public required string Name { get; init; }
public required long SizeBytes { get; init; }
public required DateTime LastModified { get; init; }
public string SizeDisplay => CleanupCategory.HumanSize(SizeBytes);
public string SizeDisplay => FormatHelper.FormatSize(SizeBytes);
public string LastModifiedDisplay => LastModified.ToString("dd MMM yyyy");
}
3 changes: 2 additions & 1 deletion SysManager/SysManager/Models/DiskUsageEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// License: MIT

using CommunityToolkit.Mvvm.ComponentModel;
using SysManager.Helpers;

namespace SysManager.Models;

Expand All @@ -23,5 +24,5 @@ public sealed partial class DiskUsageEntry : ObservableObject
[ObservableProperty] private bool _isAccessDenied;

/// <summary>Formatted size for display.</summary>
public string SizeDisplay => CleanupCategory.HumanSize(SizeBytes);
public string SizeDisplay => FormatHelper.FormatSize(SizeBytes);
}
3 changes: 2 additions & 1 deletion SysManager/SysManager/Models/InstalledApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using SysManager.Helpers;

namespace SysManager.Models;

Expand All @@ -27,5 +28,5 @@ public sealed partial class InstalledApp : ObservableObject
[ObservableProperty] private string _quietUninstallString = "";

/// <summary>Formatted size for display.</summary>
public string SizeDisplay => SizeBytes > 0 ? CleanupCategory.HumanSize(SizeBytes) : "—";
public string SizeDisplay => SizeBytes > 0 ? FormatHelper.FormatSize(SizeBytes) : "—";
}
3 changes: 2 additions & 1 deletion SysManager/SysManager/Models/ProcessEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using SysManager.Helpers;

namespace SysManager.Models;

Expand Down Expand Up @@ -34,5 +35,5 @@ public sealed partial class ProcessEntry : ObservableObject
[ObservableProperty] private bool _canOpenFileLocation;

/// <summary>Formatted memory for display.</summary>
public string MemoryDisplay => CleanupCategory.HumanSize(MemoryBytes);
public string MemoryDisplay => FormatHelper.FormatSize(MemoryBytes);
}
4 changes: 3 additions & 1 deletion SysManager/SysManager/Models/TuneUpResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Author: laurentiu021 · https://github.com/laurentiu021/SystemManager
// License: MIT

using SysManager.Helpers;

namespace SysManager.Models;

/// <summary>
Expand Down Expand Up @@ -36,7 +38,7 @@ public sealed class TuneUpResult
public bool RamWarning => RamUsedPercent >= 85;

// ── Summary helpers ────────────────────────────────────────────────
public string FreedDisplay => CleanupCategory.HumanSize(TempBytesFreed);
public string FreedDisplay => FormatHelper.FormatSize(TempBytesFreed);

public int WarningCount
{
Expand Down
Loading
Loading