added unit tests

This commit is contained in:
2026-05-30 22:07:19 -05:00
parent c54b5dc4f4
commit 5f88f71e61
14 changed files with 1036 additions and 40 deletions
@@ -0,0 +1 @@
C:/Users/jasonross/.gemini/config/projects/5ff4f24c-d4a3-4a12-ba23-13f71a7b0b49.json
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<Platforms>x64</Platforms>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,118 @@
using System;
using System.IO;
using System.Reflection;
using LibreHardwareMonitor.Windows.WinUI.Services;
using Windows.UI;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.Services;
public class AppSettingsTests : IDisposable
{
private readonly AppSettings _settings;
private readonly string _tempFile;
public AppSettingsTests()
{
_tempFile = Path.GetTempFileName();
// Use reflection to instantiate AppSettings with custom filename
var ctor = typeof(AppSettings).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null);
_settings = (AppSettings)ctor.Invoke(new object[] { _tempFile });
}
public void Dispose()
{
if (File.Exists(_tempFile))
{
File.Delete(_tempFile);
}
}
[Fact]
public void FileName_ReturnsCorrectPath()
{
Assert.Equal(_tempFile, _settings.FileName);
}
[Fact]
public void SetAndGetString()
{
_settings.SetValue("testStr", "hello");
Assert.True(_settings.Contains("testStr"));
Assert.Equal("hello", _settings.GetValue("testStr", "default"));
Assert.Equal("default", _settings.GetValue("missing", "default"));
}
[Fact]
public void SetAndGetInt()
{
_settings.SetValue("testInt", 42);
Assert.Equal(42, _settings.GetValue("testInt", 0));
Assert.Equal(10, _settings.GetValue("missing", 10));
}
[Fact]
public void SetAndGetFloat()
{
_settings.SetValue("testFloat", 3.14f);
Assert.Equal(3.14f, _settings.GetValue("testFloat", 0f));
Assert.Equal(1.5f, _settings.GetValue("missing", 1.5f));
}
[Fact]
public void SetAndGetDouble()
{
_settings.SetValue("testDouble", 3.14159);
Assert.Equal(3.14159, _settings.GetValue("testDouble", 0d));
Assert.Equal(1.5d, _settings.GetValue("missing", 1.5d));
}
[Fact]
public void SetAndGetBool()
{
_settings.SetValue("testBool", true);
Assert.True(_settings.GetValue("testBool", false));
Assert.False(_settings.GetValue("missing", false));
}
[Fact]
public void SetAndGetColor()
{
var color = Color.FromArgb(255, 100, 150, 200);
_settings.SetValue("testColor", color);
var retrievedColor = _settings.GetValue("testColor", Color.FromArgb(0, 0, 0, 0));
Assert.Equal(color.A, retrievedColor.A);
Assert.Equal(color.R, retrievedColor.R);
Assert.Equal(color.G, retrievedColor.G);
Assert.Equal(color.B, retrievedColor.B);
var defaultColor = Color.FromArgb(10, 20, 30, 40);
Assert.Equal(defaultColor, _settings.GetValue("missing", defaultColor));
}
[Fact]
public void Remove_DeletesKey()
{
_settings.SetValue("toRemove", "value");
Assert.True(_settings.Contains("toRemove"));
_settings.Remove("toRemove");
Assert.False(_settings.Contains("toRemove"));
}
[Fact]
public void SaveAndLoad_PersistsData()
{
_settings.SetValue("persistKey", "persistValue");
_settings.Save();
// Create new instance pointing to same file
var ctor = typeof(AppSettings).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(string) }, null);
var loadedSettings = (AppSettings)ctor.Invoke(new object[] { _tempFile });
loadedSettings.Load();
Assert.Equal("persistValue", loadedSettings.GetValue("persistKey", ""));
}
}
@@ -0,0 +1,54 @@
using System;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Windows.WinUI.Services;
using Moq;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.Services;
public class LoggerTests
{
[Fact]
public void Constructor_SubscribesToComputerEvents()
{
var mockComputer = new Mock<IComputer>();
var logger = new Logger(mockComputer.Object);
// We can't directly test if the event was subscribed, but we can verify default properties
Assert.Equal(LoggerFileRotation.PerSession, logger.FileRotationMethod);
Assert.Equal(TimeSpan.FromSeconds(1), logger.LoggingInterval);
}
[Fact]
public void Properties_CanBeSet()
{
var mockComputer = new Mock<IComputer>();
var logger = new Logger(mockComputer.Object)
{
FileRotationMethod = LoggerFileRotation.Daily,
LoggingInterval = TimeSpan.FromMinutes(5)
};
Assert.Equal(LoggerFileRotation.Daily, logger.FileRotationMethod);
Assert.Equal(TimeSpan.FromMinutes(5), logger.LoggingInterval);
}
[Fact]
public void Log_DoesNotThrow_WhenNoSensors()
{
var mockComputer = new Mock<IComputer>();
var logger = new Logger(mockComputer.Object);
// We can't fully mock the file system easily here without abstraction,
// but if there are no sensors it will just create a basic CSV.
// Actually, it tries to create a file in the app directory, which might throw in some environments.
// Let's just assure it doesn't crash when interval hasn't passed.
logger.LoggingInterval = TimeSpan.FromHours(1);
logger.Log(); // Should execute
// Calling again immediately should return early without doing anything
logger.Log();
}
}
@@ -0,0 +1,59 @@
using System;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Windows.WinUI.Services;
using Moq;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.Services;
public class UpdateVisitorTests
{
[Fact]
public void VisitComputer_CallsTraverse()
{
var mockComputer = new Mock<IComputer>();
var visitor = new UpdateVisitor();
visitor.VisitComputer(mockComputer.Object);
mockComputer.Verify(c => c.Traverse(visitor), Times.Once);
}
[Fact]
public void VisitHardware_CallsUpdateAndAcceptsOnSubHardware()
{
var visitor = new UpdateVisitor();
var mockHardware = new Mock<IHardware>();
var mockSubHardware1 = new Mock<IHardware>();
var mockSubHardware2 = new Mock<IHardware>();
mockHardware.Setup(h => h.SubHardware).Returns(new[] { mockSubHardware1.Object, mockSubHardware2.Object });
visitor.VisitHardware(mockHardware.Object);
mockHardware.Verify(h => h.Update(), Times.Once);
mockSubHardware1.Verify(h => h.Accept(visitor), Times.Once);
mockSubHardware2.Verify(h => h.Accept(visitor), Times.Once);
}
[Fact]
public void VisitSensor_DoesNothing()
{
var visitor = new UpdateVisitor();
var mockSensor = new Mock<ISensor>();
visitor.VisitSensor(mockSensor.Object);
// Assert it does not throw
}
[Fact]
public void VisitParameter_DoesNothing()
{
var visitor = new UpdateVisitor();
var mockParameter = new Mock<IParameter>();
visitor.VisitParameter(mockParameter.Object);
// Assert it does not throw
}
}
@@ -0,0 +1,10 @@
namespace LibreHardwareMonitor.Windows.WinUI.Tests;
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
@@ -0,0 +1,193 @@
using System;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Windows.WinUI.Utilities;
using LibreHardwareMonitor.Windows.WinUI.ViewModels;
using Moq;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.Utilities;
public class SensorFormatterTests
{
[Fact]
public void GetFormatString_ReturnsCorrectFormat_ForAllSensorTypes()
{
var mockSensor = new Mock<ISensor>();
// Test a few specific types
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Voltage);
Assert.Equal("{0:F3} V", SensorFormatter.GetFormatString(mockSensor.Object));
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Load);
Assert.Equal("{0:F1} %", SensorFormatter.GetFormatString(mockSensor.Object));
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
Assert.Equal("{0:F1} \u00B0C", SensorFormatter.GetFormatString(mockSensor.Object));
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Fan);
Assert.Equal("{0:F0} RPM", SensorFormatter.GetFormatString(mockSensor.Object));
// Test default case
mockSensor.Setup(s => s.SensorType).Returns((SensorType)999);
Assert.Equal("{0:F1}", SensorFormatter.GetFormatString(mockSensor.Object));
}
[Fact]
public void FormatValue_NullValue_ReturnsDash()
{
var mockSensor = new Mock<ISensor>();
var result = SensorFormatter.FormatValue(mockSensor.Object, null, TemperatureUnit.Celsius);
Assert.Equal("-", result);
}
[Fact]
public void FormatValue_Celsius_ReturnsCelsiusString()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
var result = SensorFormatter.FormatValue(mockSensor.Object, 25.5f, TemperatureUnit.Celsius);
Assert.Equal("25.5 \u00B0C", result);
}
[Fact]
public void FormatValue_Fahrenheit_ReturnsFahrenheitString()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
var result = SensorFormatter.FormatValue(mockSensor.Object, 25.5f, TemperatureUnit.Fahrenheit);
// 25.5 * 1.8 + 32 = 77.9
Assert.Equal("77.9 \u00B0F", result);
}
[Fact]
public void FormatValue_Throughput_ReturnsCorrectString()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Throughput);
mockSensor.Setup(s => s.Name).Returns("Other");
// Below 1MB
var result1 = SensorFormatter.FormatValue(mockSensor.Object, 512f, TemperatureUnit.Celsius);
Assert.Equal("0.5 KB/s", result1);
// Above 1MB
var result2 = SensorFormatter.FormatValue(mockSensor.Object, 2097152f, TemperatureUnit.Celsius);
Assert.Equal("2.0 MB/s", result2);
}
[Fact]
public void FormatValue_Throughput_ConnectionSpeed_ReturnsCorrectString()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Throughput);
mockSensor.Setup(s => s.Name).Returns("Connection Speed");
Assert.Equal("100Mbps", SensorFormatter.FormatValue(mockSensor.Object, 100000000f, TemperatureUnit.Celsius));
Assert.Equal("1Gbps", SensorFormatter.FormatValue(mockSensor.Object, 1000000000f, TemperatureUnit.Celsius));
Assert.Equal("500 bps", SensorFormatter.FormatValue(mockSensor.Object, 500f, TemperatureUnit.Celsius));
Assert.Equal("2.0 Kbps", SensorFormatter.FormatValue(mockSensor.Object, 2048f, TemperatureUnit.Celsius));
Assert.Equal("2.0 Mbps", SensorFormatter.FormatValue(mockSensor.Object, 2097152f, TemperatureUnit.Celsius));
Assert.Equal("2.0 Gbps", SensorFormatter.FormatValue(mockSensor.Object, 2147483648f, TemperatureUnit.Celsius));
}
[Fact]
public void FormatValue_TimeSpan_ReturnsCorrectString()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.TimeSpan);
var result = SensorFormatter.FormatValue(mockSensor.Object, 3600f, TemperatureUnit.Celsius);
Assert.Equal("1:00:00", result); // g format for 1 hour
}
[Fact]
public void FormatValue_Default_ReturnsFormattedFloat()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.SensorType).Returns((SensorType)999);
var result = SensorFormatter.FormatValue(mockSensor.Object, 12.34f, TemperatureUnit.Celsius);
Assert.Equal("12.3", result);
}
[Fact]
public void GetPlotValue_NullValue_ReturnsNull()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Value).Returns((float?)null);
var result = SensorFormatter.GetPlotValue(mockSensor.Object, TemperatureUnit.Celsius);
Assert.Null(result);
}
[Fact]
public void GetPlotValue_TemperatureFahrenheit_ReturnsConvertedValue()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Value).Returns(25.5f);
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
var result = SensorFormatter.GetPlotValue(mockSensor.Object, TemperatureUnit.Fahrenheit);
Assert.Equal(77.9, result.Value, 1);
}
[Fact]
public void GetPlotValue_OtherSensor_ReturnsRawValue()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Value).Returns(25.5f);
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Voltage);
var result = SensorFormatter.GetPlotValue(mockSensor.Object, TemperatureUnit.Celsius);
Assert.Equal(25.5f, result);
}
// A mock interface combining ISensor and ICriticalSensorLimits for testing
public interface ICriticalSensorMock : ISensor, ICriticalSensorLimits, ISensorLimits
{
}
[Fact]
public void GetToolTip_WithLimits_ReturnsFormattedToolTip()
{
var mockSensor = new Mock<ICriticalSensorMock>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
mockSensor.Setup(s => s.CriticalLowLimit).Returns(10f);
mockSensor.Setup(s => s.CriticalHighLimit).Returns(90f);
mockSensor.Setup(s => s.LowLimit).Returns(20f);
mockSensor.Setup(s => s.HighLimit).Returns(80f);
var result = SensorFormatter.GetToolTip(mockSensor.Object, TemperatureUnit.Celsius);
Assert.Contains("Critical range: 10.0 \u00B0C to 90.0 \u00B0C.", result);
Assert.Contains("Normal range: 20.0 \u00B0C to 80.0 \u00B0C.", result);
}
[Fact]
public void GetToolTip_WithOnlyMinLimit_ReturnsFormattedToolTip()
{
var mockSensor = new Mock<ICriticalSensorMock>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Voltage);
mockSensor.Setup(s => s.CriticalLowLimit).Returns(1.0f);
mockSensor.Setup(s => s.CriticalHighLimit).Returns((float?)null);
var result = SensorFormatter.GetToolTip(mockSensor.Object, TemperatureUnit.Celsius);
Assert.Contains("Minimal critical value: 1.000 V.", result);
}
[Fact]
public void GetToolTip_WithOnlyMaxLimit_ReturnsFormattedToolTip()
{
var mockSensor = new Mock<ICriticalSensorMock>();
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Voltage);
mockSensor.Setup(s => s.CriticalLowLimit).Returns((float?)null);
mockSensor.Setup(s => s.CriticalHighLimit).Returns(2.0f);
var result = SensorFormatter.GetToolTip(mockSensor.Object, TemperatureUnit.Celsius);
Assert.Contains("Maximal critical value: 2.000 V.", result);
}
}
@@ -0,0 +1,100 @@
using System;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Windows.WinUI.Utilities;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.Utilities;
public class SensorTypeDisplayTests
{
[Fact]
public void GetText_ReturnsCorrectString_ForAllKnownTypes()
{
Assert.Equal("Voltages", SensorTypeDisplay.GetText(SensorType.Voltage));
Assert.Equal("Currents", SensorTypeDisplay.GetText(SensorType.Current));
Assert.Equal("Clocks", SensorTypeDisplay.GetText(SensorType.Clock));
Assert.Equal("Load", SensorTypeDisplay.GetText(SensorType.Load));
Assert.Equal("Temperatures", SensorTypeDisplay.GetText(SensorType.Temperature));
Assert.Equal("Fans", SensorTypeDisplay.GetText(SensorType.Fan));
Assert.Equal("Flows", SensorTypeDisplay.GetText(SensorType.Flow));
Assert.Equal("Controls", SensorTypeDisplay.GetText(SensorType.Control));
Assert.Equal("Levels", SensorTypeDisplay.GetText(SensorType.Level));
Assert.Equal("Powers", SensorTypeDisplay.GetText(SensorType.Power));
Assert.Equal("Data", SensorTypeDisplay.GetText(SensorType.Data));
Assert.Equal("Data", SensorTypeDisplay.GetText(SensorType.SmallData));
Assert.Equal("Factors", SensorTypeDisplay.GetText(SensorType.Factor));
Assert.Equal("Frequencies", SensorTypeDisplay.GetText(SensorType.Frequency));
Assert.Equal("Throughput", SensorTypeDisplay.GetText(SensorType.Throughput));
Assert.Equal("Times", SensorTypeDisplay.GetText(SensorType.TimeSpan));
Assert.Equal("Timings", SensorTypeDisplay.GetText(SensorType.Timing));
Assert.Equal("Capacities", SensorTypeDisplay.GetText(SensorType.Energy));
Assert.Equal("Noise Levels", SensorTypeDisplay.GetText(SensorType.Noise));
Assert.Equal("Conductivities", SensorTypeDisplay.GetText(SensorType.Conductivity));
Assert.Equal("Humidity Levels", SensorTypeDisplay.GetText(SensorType.Humidity));
}
[Fact]
public void GetText_ReturnsToString_ForUnknownType()
{
SensorType unknownType = (SensorType)999;
Assert.Equal("999", SensorTypeDisplay.GetText(unknownType));
}
[Fact]
public void GetGlyph_ReturnsCorrectGlyph_ForAllKnownTypes()
{
Assert.Equal("\uE945", SensorTypeDisplay.GetGlyph(SensorType.Voltage));
Assert.Equal("\uE945", SensorTypeDisplay.GetGlyph(SensorType.Current));
Assert.Equal("\uE916", SensorTypeDisplay.GetGlyph(SensorType.Clock));
Assert.Equal("\uE9D9", SensorTypeDisplay.GetGlyph(SensorType.Load));
Assert.Equal("\uE9CA", SensorTypeDisplay.GetGlyph(SensorType.Temperature));
Assert.Equal("\uE9F3", SensorTypeDisplay.GetGlyph(SensorType.Fan));
Assert.Equal("\uE9D5", SensorTypeDisplay.GetGlyph(SensorType.Flow));
Assert.Equal("\uE713", SensorTypeDisplay.GetGlyph(SensorType.Control));
Assert.Equal("\uE9D2", SensorTypeDisplay.GetGlyph(SensorType.Level));
Assert.Equal("\uE7E8", SensorTypeDisplay.GetGlyph(SensorType.Power));
Assert.Equal("\uE8AB", SensorTypeDisplay.GetGlyph(SensorType.Data));
Assert.Equal("\uE8AB", SensorTypeDisplay.GetGlyph(SensorType.SmallData));
Assert.Equal("\uE9D2", SensorTypeDisplay.GetGlyph(SensorType.Factor));
Assert.Equal("\uE916", SensorTypeDisplay.GetGlyph(SensorType.Frequency));
Assert.Equal("\uE9D5", SensorTypeDisplay.GetGlyph(SensorType.Throughput));
Assert.Equal("\uE121", SensorTypeDisplay.GetGlyph(SensorType.TimeSpan));
Assert.Equal("\uE121", SensorTypeDisplay.GetGlyph(SensorType.Timing));
Assert.Equal("\uEBAA", SensorTypeDisplay.GetGlyph(SensorType.Energy));
Assert.Equal("\uE767", SensorTypeDisplay.GetGlyph(SensorType.Noise));
Assert.Equal("\uE945", SensorTypeDisplay.GetGlyph(SensorType.Conductivity));
Assert.Equal("\uE9CA", SensorTypeDisplay.GetGlyph(SensorType.Humidity));
}
[Fact]
public void GetGlyph_ReturnsDefault_ForUnknownType()
{
SensorType unknownType = (SensorType)999;
Assert.Equal("\uE9D9", SensorTypeDisplay.GetGlyph(unknownType));
}
[Fact]
public void GetHardwareGlyph_ReturnsCorrectGlyph_ForAllKnownTypes()
{
Assert.Equal("\uE950", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Motherboard));
Assert.Equal("\uE950", SensorTypeDisplay.GetHardwareGlyph(HardwareType.SuperIO));
Assert.Equal("\uE950", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Cpu));
Assert.Equal("\uE8AB", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Memory));
Assert.Equal("\uE7F4", SensorTypeDisplay.GetHardwareGlyph(HardwareType.GpuNvidia));
Assert.Equal("\uE7F4", SensorTypeDisplay.GetHardwareGlyph(HardwareType.GpuAmd));
Assert.Equal("\uE7F4", SensorTypeDisplay.GetHardwareGlyph(HardwareType.GpuIntel));
Assert.Equal("\uEDA2", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Storage));
Assert.Equal("\uE968", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Network));
Assert.Equal("\uE9F3", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Cooler));
Assert.Equal("\uE950", SensorTypeDisplay.GetHardwareGlyph(HardwareType.EmbeddedController));
Assert.Equal("\uE7E8", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Psu));
Assert.Equal("\uEBAA", SensorTypeDisplay.GetHardwareGlyph(HardwareType.Battery));
}
[Fact]
public void GetHardwareGlyph_ReturnsDefault_ForUnknownType()
{
HardwareType unknownType = (HardwareType)999;
Assert.Equal("\uE950", SensorTypeDisplay.GetHardwareGlyph(unknownType));
}
}
@@ -0,0 +1,185 @@
using System;
using System.Linq;
using LibreHardwareMonitor.Hardware;
using LibreHardwareMonitor.Windows.WinUI.Services;
using LibreHardwareMonitor.Windows.WinUI.ViewModels;
using Moq;
using Xunit;
using Microsoft.UI.Xaml;
using Windows.UI;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.ViewModels;
public class SensorTreeItemViewModelTests
{
private AppSettings CreateMockSettings()
{
return AppSettings.LoadDefault();
}
[Fact]
public void CreateRoot_ReturnsRootKind()
{
var vm = SensorTreeItemViewModel.CreateRoot("Root Node");
Assert.Equal(SensorTreeItemKind.Root, vm.Kind);
Assert.Equal("Root Node", vm.Text);
Assert.Null(vm.Sensor);
Assert.Null(vm.Hardware);
Assert.Equal("\uE7F4", vm.IconGlyph);
Assert.True(vm.IsExpanded);
Assert.Empty(vm.Children);
}
[Fact]
public void Text_CanBeSetForRoot()
{
var vm = SensorTreeItemViewModel.CreateRoot("Root");
vm.Text = "New Root";
Assert.Equal("New Root", vm.Text);
}
[Fact]
public void FromHardware_CreatesHierarchy()
{
var mockHardware = new Mock<IHardware>();
mockHardware.Setup(h => h.Name).Returns("My Hardware");
mockHardware.Setup(h => h.HardwareType).Returns(HardwareType.Cpu);
mockHardware.Setup(h => h.Identifier).Returns(new Identifier("cpu", "0"));
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Name).Returns("Core 0");
mockSensor.Setup(s => s.SensorType).Returns(SensorType.Temperature);
mockSensor.Setup(s => s.Index).Returns(0);
mockSensor.Setup(s => s.Identifier).Returns(new Identifier("cpu", "0", "temperature", "0"));
mockHardware.Setup(h => h.Sensors).Returns(new[] { mockSensor.Object });
mockHardware.Setup(h => h.SubHardware).Returns(Array.Empty<IHardware>());
var settings = CreateMockSettings();
var vm = SensorTreeItemViewModel.FromHardware(mockHardware.Object, settings);
Assert.Equal(SensorTreeItemKind.Hardware, vm.Kind);
Assert.Equal("My Hardware", vm.Text);
Assert.Same(mockHardware.Object, vm.Hardware);
Assert.Single(vm.Children); // One SensorType group
var typeGroup = vm.Children[0];
Assert.Equal(SensorTreeItemKind.SensorType, typeGroup.Kind);
Assert.Equal("Temperatures", typeGroup.Text);
Assert.Single(typeGroup.Children); // One sensor
var sensorVm = typeGroup.Children[0];
Assert.Equal(SensorTreeItemKind.Sensor, sensorVm.Kind);
Assert.Equal("Core 0", sensorVm.Text);
Assert.Same(mockSensor.Object, sensorVm.Sensor);
}
[Fact]
public void Plot_SetsPlotPropertyAndSettings()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Identifier).Returns(new Identifier("cpu", "0"));
var mockHardware = new Mock<IHardware>();
mockHardware.Setup(h => h.Identifier).Returns(new Identifier("hw"));
mockHardware.Setup(h => h.Sensors).Returns(new[] { mockSensor.Object });
mockHardware.Setup(h => h.SubHardware).Returns(Array.Empty<IHardware>());
var settings = CreateMockSettings();
var vm = SensorTreeItemViewModel.FromHardware(mockHardware.Object, settings);
// Root has no sensor, so Plot won't do anything
vm.Plot = true;
Assert.False(vm.Plot);
// Get SensorVM
var sensorVm = vm.Children.First().Children.First();
sensorVm.Plot = true;
Assert.True(sensorVm.Plot);
}
[Fact]
public void PenColor_SetsPropertyAndSettings()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Identifier).Returns(new Identifier("cpu", "0"));
var mockHardware = new Mock<IHardware>();
mockHardware.Setup(h => h.Sensors).Returns(new[] { mockSensor.Object });
mockHardware.Setup(h => h.Identifier).Returns(new Identifier("hw"));
mockHardware.Setup(h => h.SubHardware).Returns(Array.Empty<IHardware>());
var settings = CreateMockSettings();
var vm = SensorTreeItemViewModel.FromHardware(mockHardware.Object, settings);
var sensorVm = vm.Children.First().Children.First();
Assert.Null(sensorVm.PenColor);
var color = Color.FromArgb(255, 100, 100, 100);
sensorVm.PenColor = color;
Assert.Equal(color, sensorVm.PenColor);
sensorVm.PenColor = null;
Assert.Null(sensorVm.PenColor);
}
[Fact]
public void SetShowHiddenSensors_UpdatesVisibility()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Identifier).Returns(new Identifier("cpu", "0"));
var mockHardware = new Mock<IHardware>();
mockHardware.Setup(h => h.Sensors).Returns(new[] { mockSensor.Object });
mockHardware.Setup(h => h.Identifier).Returns(new Identifier("hw"));
mockHardware.Setup(h => h.SubHardware).Returns(Array.Empty<IHardware>());
var settings = CreateMockSettings();
var vm = SensorTreeItemViewModel.FromHardware(mockHardware.Object, settings);
var typeGroup = vm.Children.First();
var sensorVm = typeGroup.Children.First();
sensorVm.IsVisible = false;
Assert.Equal(Visibility.Collapsed, sensorVm.RowVisibility);
// typeGroup doesn't update automatically in ViewModel when child changes, so skip assert
vm.SetShowHiddenSensors(true);
Assert.Equal(Visibility.Visible, sensorVm.RowVisibility);
Assert.Equal(Visibility.Visible, typeGroup.RowVisibility);
}
[Fact]
public void SetColumnVisibility_UpdatesAllChildren()
{
var root = SensorTreeItemViewModel.CreateRoot("Root");
root.SetColumnVisibility(false, true, true);
Assert.Equal(Visibility.Collapsed, root.ValueColumnVisibility);
Assert.Equal(Visibility.Visible, root.MinColumnVisibility);
Assert.Equal(Visibility.Visible, root.MaxColumnVisibility);
}
[Fact]
public void RefreshValues_UpdatesProperties()
{
var mockSensor = new Mock<ISensor>();
mockSensor.Setup(s => s.Identifier).Returns(new Identifier("cpu", "0"));
mockSensor.Setup(s => s.Value).Returns(50f);
var mockHardware = new Mock<IHardware>();
mockHardware.Setup(h => h.Sensors).Returns(new[] { mockSensor.Object });
mockHardware.Setup(h => h.Identifier).Returns(new Identifier("hw"));
mockHardware.Setup(h => h.SubHardware).Returns(Array.Empty<IHardware>());
var vm = SensorTreeItemViewModel.FromHardware(mockHardware.Object, CreateMockSettings());
var sensorVm = vm.Children.First().Children.First();
mockSensor.Setup(s => s.Value).Returns(60f); // change value
sensorVm.RefreshValues(); // This triggers PropertyChanged
Assert.Contains("60.0", sensorVm.Value);
}
}
@@ -0,0 +1,101 @@
using System;
using System.ComponentModel;
using Windows.UI;
using LibreHardwareMonitor.Windows.WinUI.ViewModels;
using Xunit;
namespace LibreHardwareMonitor.Windows.WinUI.Tests.ViewModels;
public class SimpleViewModelsTests
{
private class TestViewModel : ViewModelBase
{
private string _testProperty;
public string TestProperty
{
get => _testProperty;
set => SetProperty(ref _testProperty, value);
}
public void TriggerPropertyChanged(string propertyName)
{
OnPropertyChanged(propertyName);
}
}
[Fact]
public void ViewModelBase_SetProperty_ChangesValueAndRaisesEvent()
{
var vm = new TestViewModel();
string changedProperty = null;
vm.PropertyChanged += (sender, args) => changedProperty = args.PropertyName;
// Change value
vm.TestProperty = "NewValue";
vm.TestProperty = "NewValue";
Assert.Equal("NewValue", vm.TestProperty);
Assert.Equal(nameof(TestViewModel.TestProperty), changedProperty);
}
[Fact]
public void ViewModelBase_SetProperty_SameValue_DoesNotRaiseEvent()
{
var vm = new TestViewModel();
vm.TestProperty = "Value";
bool eventRaised = false;
vm.PropertyChanged += (sender, args) => eventRaised = true;
vm.TestProperty = "Value"; // Set same value
Assert.False(eventRaised);
}
[Fact]
public void ViewModelBase_OnPropertyChanged_RaisesEvent()
{
var vm = new TestViewModel();
string changedProperty = null;
vm.PropertyChanged += (sender, args) => changedProperty = args.PropertyName;
vm.TriggerPropertyChanged("CustomProperty");
Assert.Equal("CustomProperty", changedProperty);
}
[Fact]
public void PlotPointViewModel_Constructor_SetsProperties()
{
var timestamp = new DateTime(2023, 1, 1);
var vm = new PlotPointViewModel(timestamp, 42.5);
Assert.Equal(timestamp, vm.Timestamp);
Assert.Equal(42.5, vm.Value);
}
[Fact]
public void PlotSeriesViewModel_Constructor_SetsProperties()
{
var color = Color.FromArgb(255, 255, 0, 0);
var vm = new PlotSeriesViewModel("sensor1", "Sensor Name", color);
Assert.Equal("sensor1", vm.SensorIdentifier);
Assert.Equal("Sensor Name", vm.Name);
Assert.Equal(color, vm.Color);
Assert.NotNull(vm.Points);
Assert.Empty(vm.Points);
}
[Fact]
public void PlotSeriesViewModel_SetColor_ChangesColor()
{
var color1 = Color.FromArgb(255, 255, 0, 0);
var color2 = Color.FromArgb(255, 0, 255, 0);
var vm = new PlotSeriesViewModel("sensor1", "Sensor Name", color1);
vm.Color = color2;
Assert.Equal(color2, vm.Color);
}
}
+66 -30
View File
@@ -3,12 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.11018.127
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitor.Windows.WinUI", "LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj", "{49D41E03-7028-4B28-9F5E-4558D0E26EA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitorLib", "LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj", "{B0397530-545A-471D-BB74-027AE456DF1A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitor.Windows.Forms", "LibreHardwareMonitor.Windows.Forms\LibreHardwareMonitor.Windows.Forms.csproj", "{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitor.Windows.WinUI", "LibreHardwareMonitor.Windows.WinUI\LibreHardwareMonitor.Windows.WinUI.csproj", "{49D41E03-7028-4B28-9F5E-4558D0E26EA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitorLib", "LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj", "{B0397530-545A-471D-BB74-027AE456DF1A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreHardwareMonitor.Windows.Forms", "LibreHardwareMonitor.Windows.Forms\LibreHardwareMonitor.Windows.Forms.csproj", "{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aga.Controls", "Aga.Controls\Aga.Controls.csproj", "{E73BB233-D88B-44A7-A98F-D71EE158381D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Controls", "Controls", "{23F54DBD-8998-4E22-A4E4-60F4F77F9B65}"
@@ -19,64 +19,100 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibreHardwareMonitorLib.Tests", "LibreHardwareMonitorLib.Tests\LibreHardwareMonitorLib.Tests.csproj", "{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Debug|Any CPU = Debug|Any CPU
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|ARM64.Build.0 = Debug|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x64.ActiveCfg = Debug|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x64.Build.0 = Debug|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x86.ActiveCfg = Debug|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x86.Build.0 = Debug|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|ARM64.ActiveCfg = Release|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|ARM64.Build.0 = Release|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x64.ActiveCfg = Release|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x64.Build.0 = Release|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x86.ActiveCfg = Release|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x86.Build.0 = Release|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|ARM64.Build.0 = Debug|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x64.ActiveCfg = Debug|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x64.Build.0 = Debug|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x86.ActiveCfg = Debug|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x86.Build.0 = Debug|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|ARM64.ActiveCfg = Release|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|ARM64.Build.0 = Release|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x64.ActiveCfg = Release|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x64.Build.0 = Release|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x86.ActiveCfg = Release|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x86.Build.0 = Release|x86
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|ARM64.Build.0 = Debug|ARM64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|x64.ActiveCfg = Debug|x64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|x64.Build.0 = Debug|x64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|x86.ActiveCfg = Debug|x86
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|x86.Build.0 = Debug|x86
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|ARM64.ActiveCfg = Release|ARM64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|ARM64.Build.0 = Release|ARM64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|x64.ActiveCfg = Release|x64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|x64.Build.0 = Release|x64
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|x86.ActiveCfg = Release|x86
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|x86.Build.0 = Release|x86
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49D41E03-7028-4B28-9F5E-4558D0E26EA2}.Release|Any CPU.Build.0 = Release|Any CPU
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|ARM64.Build.0 = Debug|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x64.ActiveCfg = Debug|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x64.Build.0 = Debug|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x86.ActiveCfg = Debug|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|x86.Build.0 = Debug|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0397530-545A-471D-BB74-027AE456DF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|ARM64.ActiveCfg = Release|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|ARM64.Build.0 = Release|ARM64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x64.ActiveCfg = Release|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x64.Build.0 = Release|x64
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x86.ActiveCfg = Release|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|x86.Build.0 = Release|x86
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0397530-545A-471D-BB74-027AE456DF1A}.Release|Any CPU.Build.0 = Release|Any CPU
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|ARM64.Build.0 = Debug|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x64.ActiveCfg = Debug|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x64.Build.0 = Debug|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x86.ActiveCfg = Debug|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|x86.Build.0 = Debug|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|ARM64.ActiveCfg = Release|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|ARM64.Build.0 = Release|ARM64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x64.ActiveCfg = Release|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x64.Build.0 = Release|x64
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x86.ActiveCfg = Release|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|x86.Build.0 = Release|x86
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5E0C1F7-9E9B-46F2-AC88-8C9C1C923880}.Release|Any CPU.Build.0 = Release|Any CPU
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|ARM64.Build.0 = Debug|ARM64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|x64.ActiveCfg = Debug|x64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|x64.Build.0 = Debug|x64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|x86.ActiveCfg = Debug|x86
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|x86.Build.0 = Debug|x86
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|ARM64.ActiveCfg = Release|ARM64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|ARM64.Build.0 = Release|ARM64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|x64.ActiveCfg = Release|x64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|x64.Build.0 = Release|x64
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|x86.ActiveCfg = Release|x86
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|x86.Build.0 = Release|x86
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E73BB233-D88B-44A7-A98F-D71EE158381D}.Release|Any CPU.Build.0 = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|ARM64.Build.0 = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|x64.Build.0 = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|x86.Build.0 = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|ARM64.ActiveCfg = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|ARM64.Build.0 = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|x64.ActiveCfg = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|x64.Build.0 = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|x86.ActiveCfg = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|x86.Build.0 = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF286D81-C1D0-47B2-AD60-9F60CAE602F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using LibreHardwareMonitor.Hardware;
using Xunit;
namespace LibreHardwareMonitorLib.Tests.Hardware;
public class ComputerTests
{
[Fact]
public void OpenAndClose_WorksCorrectly_AndCompletesHardwareDiscoveryTask()
{
var computer = new Computer
{
IsCpuEnabled = false,
IsGpuEnabled = false,
IsMemoryEnabled = false,
IsMotherboardEnabled = false,
IsControllerEnabled = false,
IsNetworkEnabled = false,
IsStorageEnabled = false,
IsBatteryEnabled = false,
IsPsuEnabled = false,
IsPowerMonitorEnabled = false
};
// HardwareDiscoveryTask should be available
Assert.NotNull(computer.HardwareDiscoveryTask);
// Open the computer
computer.Open();
// HardwareDiscoveryTask should complete because all are disabled, so no long-running task
Assert.True(computer.HardwareDiscoveryTask.IsCompleted);
// Close the computer
computer.Close();
// Ensure it doesn't throw and remains in a good state
Assert.True(computer.HardwareDiscoveryTask.IsCompleted);
}
[Fact]
public async Task OpenAsync_WorksCorrectly_AndCompletesHardwareDiscoveryTask()
{
var computer = new Computer
{
IsCpuEnabled = false,
IsGpuEnabled = false,
IsMemoryEnabled = false,
IsMotherboardEnabled = false,
IsControllerEnabled = false,
IsNetworkEnabled = false,
IsStorageEnabled = false,
IsBatteryEnabled = false,
IsPsuEnabled = false,
IsPowerMonitorEnabled = false
};
await computer.OpenAsync();
// The HardwareDiscoveryTask should complete successfully.
Assert.True(computer.HardwareDiscoveryTask.IsCompletedSuccessfully);
computer.Close();
}
[Fact]
public void Reset_ClearsHardwareAndCompletesDiscovery()
{
var computer = new Computer
{
IsCpuEnabled = false
};
computer.Open();
Assert.True(computer.HardwareDiscoveryTask.IsCompleted);
computer.Reset();
Assert.Empty(computer.Hardware);
Assert.True(computer.HardwareDiscoveryTask.IsCompleted);
}
}
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibreHardwareMonitorLib\LibreHardwareMonitorLib.csproj" />
</ItemGroup>
</Project>
+10 -10
View File
@@ -1,4 +1,4 @@
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
@@ -68,7 +68,7 @@ public class Computer : IComputer
private SMBios _smbios;
private bool _storageEnabled;
private CancellationTokenSource _deferredGroupCancellationTokenSource;
private TaskCompletionSource _deferredGroupCompletionSource = CreateCompletedTaskCompletionSource();
private TaskCompletionSource<object> _deferredGroupCompletionSource = CreateCompletedTaskCompletionSource();
private List<Task> _deferredGroupTasks = [];
/// <summary>
@@ -864,7 +864,7 @@ public class Computer : IComputer
CancelDeferredGroupRun();
lock (_deferredGroupLock)
{
_deferredGroupCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_deferredGroupCompletionSource = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_deferredGroupTasks = [];
}
@@ -875,7 +875,7 @@ public class Computer : IComputer
{
CancellationTokenSource cancellationTokenSource = _deferredGroupCancellationTokenSource;
_deferredGroupCancellationTokenSource = null;
TaskCompletionSource completionSource;
TaskCompletionSource<object> completionSource;
lock (_deferredGroupLock)
{
@@ -894,14 +894,14 @@ public class Computer : IComputer
completionSource.TrySetCanceled();
}
private static TaskCompletionSource CreateCompletedTaskCompletionSource()
private static TaskCompletionSource<object> CreateCompletedTaskCompletionSource()
{
TaskCompletionSource completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
completionSource.SetResult();
TaskCompletionSource<object> completionSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
completionSource.SetResult(null);
return completionSource;
}
private void CompleteDeferredGroupRun(TaskCompletionSource completionSource)
private void CompleteDeferredGroupRun(TaskCompletionSource<object> completionSource)
{
bool completed;
lock (_deferredGroupLock)
@@ -909,7 +909,7 @@ public class Computer : IComputer
if (!ReferenceEquals(completionSource, _deferredGroupCompletionSource))
return;
completed = completionSource.TrySetResult();
completed = completionSource.TrySetResult(null);
}
if (completed)
@@ -918,7 +918,7 @@ public class Computer : IComputer
private void CompleteDeferredGroupRunWhenRegistered()
{
TaskCompletionSource completionSource;
TaskCompletionSource<object> completionSource;
Task[] tasks;
lock (_deferredGroupLock)
{