diff --git a/src/Core/AutoOptimizationPolicy.cs b/src/Core/AutoOptimizationPolicy.cs
new file mode 100644
index 00000000..121ba395
--- /dev/null
+++ b/src/Core/AutoOptimizationPolicy.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace WinMemoryCleaner
+{
+ internal static class AutoOptimizationPolicy
+ {
+ internal static Enums.Memory.Optimization.Reason? GetReason(
+ DateTimeOffset now,
+ DateTimeOffset lastAutoOptimizationByInterval,
+ DateTimeOffset lastAutoOptimizationByMemoryUsage,
+ int autoOptimizationInterval,
+ int autoOptimizationMemoryUsage,
+ int freePhysicalMemoryPercentage)
+ {
+ if (autoOptimizationInterval > 0 &&
+ now.Subtract(lastAutoOptimizationByInterval).TotalHours >= autoOptimizationInterval)
+ {
+ return Enums.Memory.Optimization.Reason.Schedule;
+ }
+
+ if (autoOptimizationMemoryUsage > 0 &&
+ freePhysicalMemoryPercentage < autoOptimizationMemoryUsage &&
+ now.Subtract(lastAutoOptimizationByMemoryUsage).TotalMinutes >= Constants.App.AutoOptimizationMemoryUsageInterval)
+ {
+ return Enums.Memory.Optimization.Reason.LowMemory;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/Test/AutoOptimizationPolicyTests.cs b/src/Test/AutoOptimizationPolicyTests.cs
new file mode 100644
index 00000000..09a7f301
--- /dev/null
+++ b/src/Test/AutoOptimizationPolicyTests.cs
@@ -0,0 +1,103 @@
+using NUnit.Framework;
+using System;
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+namespace WinMemoryCleaner.AutomationTests
+{
+ [TestFixture]
+ public class AutoOptimizationPolicyTests
+ {
+ [Test]
+ public void DisabledBoth_ReturnsNull()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now.AddHours(-24), now.AddMinutes(-30), 0, 0, 0);
+
+ Assert.IsNull(result);
+ }
+
+ [Test]
+ public void Schedule_WhenJustBeforeDue_ReturnsNull()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now.AddHours(-1).AddTicks(1), now, 1, 0, 0);
+
+ Assert.IsNull(result);
+ }
+
+ [Test]
+ public void Schedule_WhenExactlyDue_ReturnsSchedule()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now.AddHours(-1), now, 1, 0, 0);
+
+ Assert.AreEqual(Enums.Memory.Optimization.Reason.Schedule, result);
+ }
+
+ [Test]
+ public void LowMemory_WhenFreePercentageIsBelowThreshold_ReturnsLowMemory()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now, now.AddMinutes(-Constants.App.AutoOptimizationMemoryUsageInterval), 0, 40, 39);
+
+ Assert.AreEqual(Enums.Memory.Optimization.Reason.LowMemory, result);
+ }
+
+ [Test]
+ public void LowMemory_WhenFreePercentageEqualsThreshold_ReturnsNull()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now, now.AddMinutes(-Constants.App.AutoOptimizationMemoryUsageInterval), 0, 40, 40);
+
+ Assert.IsNull(result);
+ }
+
+ [Test]
+ public void LowMemory_WhenCooldownIsJustBeforeDue_ReturnsNull()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now, now.AddMinutes(-Constants.App.AutoOptimizationMemoryUsageInterval).AddTicks(1), 0, 40, 39);
+
+ Assert.IsNull(result);
+ }
+
+ [Test]
+ public void LowMemory_WhenCooldownIsExactlyDue_ReturnsLowMemory()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now, now.AddMinutes(-Constants.App.AutoOptimizationMemoryUsageInterval), 0, 40, 39);
+
+ Assert.AreEqual(Enums.Memory.Optimization.Reason.LowMemory, result);
+ }
+
+ [Test]
+ public void Schedule_WhenBothTriggersAreDue_ReturnsSchedule()
+ {
+ var now = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now.AddHours(-1), now.AddMinutes(-Constants.App.AutoOptimizationMemoryUsageInterval), 1, 40, 39);
+
+ Assert.AreEqual(Enums.Memory.Optimization.Reason.Schedule, result);
+ }
+
+ [Test]
+ public void Schedule_WhenNonZeroIntervalCrossesDateBoundary_ReturnsSchedule()
+ {
+ var now = new DateTimeOffset(2024, 1, 2, 0, 0, 0, TimeSpan.Zero);
+
+ var result = AutoOptimizationPolicy.GetReason(now, now.AddHours(-7), now, 7, 35, 100);
+
+ Assert.AreEqual(Enums.Memory.Optimization.Reason.Schedule, result);
+ }
+ }
+}
+
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
diff --git a/src/ViewModel/MainViewModel.cs b/src/ViewModel/MainViewModel.cs
index 84feb147..bd65bcdd 100644
--- a/src/ViewModel/MainViewModel.cs
+++ b/src/ViewModel/MainViewModel.cs
@@ -1636,24 +1636,23 @@ private void MonitorApp()
{
if (CanOptimize)
{
- // Interval
- if (Settings.AutoOptimizationInterval > 0 &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByInterval).TotalHours >= Settings.AutoOptimizationInterval)
+ var reason = AutoOptimizationPolicy.GetReason(
+ DateTimeOffset.Now,
+ _lastAutoOptimizationByInterval,
+ _lastAutoOptimizationByMemoryUsage,
+ Settings.AutoOptimizationInterval,
+ Settings.AutoOptimizationMemoryUsage,
+ Computer.Memory.Physical.Free.Percentage);
+
+ if (reason.HasValue)
{
- OptimizeAsync(Enums.Memory.Optimization.Reason.Schedule);
+ OptimizeAsync(reason.Value);
- _lastAutoOptimizationByInterval = DateTimeOffset.Now;
- continue;
- }
-
- // Memory usage
- if (Settings.AutoOptimizationMemoryUsage > 0 &&
- Computer.Memory.Physical.Free.Percentage < Settings.AutoOptimizationMemoryUsage &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByMemoryUsage).TotalMinutes >= Constants.App.AutoOptimizationMemoryUsageInterval)
- {
- OptimizeAsync(Enums.Memory.Optimization.Reason.LowMemory);
+ if (reason.Value == Enums.Memory.Optimization.Reason.Schedule)
+ _lastAutoOptimizationByInterval = DateTimeOffset.Now;
+ else
+ _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
- _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
continue;
}
}
diff --git a/src/WinMemoryCleaner.csproj b/src/WinMemoryCleaner.csproj
index 6bd06c24..5bb722d2 100644
--- a/src/WinMemoryCleaner.csproj
+++ b/src/WinMemoryCleaner.csproj
@@ -118,6 +118,7 @@
+
@@ -158,6 +159,7 @@
+
diff --git a/src/WindowsService/WinService.cs b/src/WindowsService/WinService.cs
index a77aee8a..11c0b2e1 100644
--- a/src/WindowsService/WinService.cs
+++ b/src/WindowsService/WinService.cs
@@ -116,24 +116,23 @@ private void OnTimer(object sender, ElapsedEventArgs e)
// Update memory info
_computer.Memory = _computerService.Memory;
- // Interval
- if (Settings.AutoOptimizationInterval > 0 &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByInterval).TotalHours >= Settings.AutoOptimizationInterval)
+ var reason = AutoOptimizationPolicy.GetReason(
+ DateTimeOffset.Now,
+ _lastAutoOptimizationByInterval,
+ _lastAutoOptimizationByMemoryUsage,
+ Settings.AutoOptimizationInterval,
+ Settings.AutoOptimizationMemoryUsage,
+ _computer.Memory.Physical.Free.Percentage);
+
+ if (reason.HasValue)
{
- DependencyInjection.Container.Resolve().Optimize(Enums.Memory.Optimization.Reason.Schedule, Settings.MemoryAreas);
+ DependencyInjection.Container.Resolve().Optimize(reason.Value, Settings.MemoryAreas);
- _lastAutoOptimizationByInterval = DateTimeOffset.Now;
- return;
- }
-
- // Memory usage
- if (Settings.AutoOptimizationMemoryUsage > 0 &&
- _computer.Memory.Physical.Free.Percentage < Settings.AutoOptimizationMemoryUsage &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByMemoryUsage).TotalMinutes >= Constants.App.AutoOptimizationMemoryUsageInterval)
- {
- DependencyInjection.Container.Resolve().Optimize(Enums.Memory.Optimization.Reason.LowMemory, Settings.MemoryAreas);
+ if (reason.Value == Enums.Memory.Optimization.Reason.Schedule)
+ _lastAutoOptimizationByInterval = DateTimeOffset.Now;
+ else
+ _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
- _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
return;
}
}