Add characterization tests; consolidate SensorFormatter switches
Establishes a safety net before refactoring, and lands the first behavior-preserving cleanup. Tests (no production behavior change): - SensorFormatter: full per-type coverage of GetFormatString, FormatValue, and GetPlotUnit across every SensorType. - RemoteWebServer: extract testable seams (ResolveRoute, VerifyCredentials, internal pure helpers) and pin routing (incl. the no-hijack rule), query and Prometheus parsing, JSON/metric shape, credential semantics, and the legacy SHA-256 vector. - HardwareMonitorService: pin the enable-flag -> settings-key mapping. - Logger: add a TimeProvider/base-dir test seam; deterministic rotation tests. - Add [InternalsVisibleTo] for the test project. Cleanup: - Collapse SensorFormatter's three parallel SensorType switches into one GetFormat source of truth; dedupe CelsiusToFahrenheit. Verified identical by the new characterization tests. 167 tests pass (was 55). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,97 @@ public class SensorFormatterTests
|
||||
Assert.Equal("{0:F1}", SensorFormatter.GetFormatString(mockSensor.Object));
|
||||
}
|
||||
|
||||
// Characterization: pins the GetFormatString mapping for every SensorType so the
|
||||
// three parallel SensorType switches can be safely collapsed into one lookup table.
|
||||
[Theory]
|
||||
[InlineData(SensorType.Voltage, "{0:F3} V")]
|
||||
[InlineData(SensorType.Current, "{0:F3} A")]
|
||||
[InlineData(SensorType.Clock, "{0:F1} MHz")]
|
||||
[InlineData(SensorType.Load, "{0:F1} %")]
|
||||
[InlineData(SensorType.Temperature, "{0:F1} °C")]
|
||||
[InlineData(SensorType.Fan, "{0:F0} RPM")]
|
||||
[InlineData(SensorType.Flow, "{0:F1} L/h")]
|
||||
[InlineData(SensorType.Control, "{0:F1} %")]
|
||||
[InlineData(SensorType.Level, "{0:F1} %")]
|
||||
[InlineData(SensorType.Power, "{0:F1} W")]
|
||||
[InlineData(SensorType.Data, "{0:F1} GB")]
|
||||
[InlineData(SensorType.SmallData, "{0:F1} MB")]
|
||||
[InlineData(SensorType.Factor, "{0:F3}")]
|
||||
[InlineData(SensorType.Frequency, "{0:F1} Hz")]
|
||||
[InlineData(SensorType.Throughput, "{0:F1} B/s")]
|
||||
[InlineData(SensorType.TimeSpan, "{0:g}")]
|
||||
[InlineData(SensorType.Timing, "{0:F3} ns")]
|
||||
[InlineData(SensorType.Energy, "{0:F0} mWh")]
|
||||
[InlineData(SensorType.Noise, "{0:F0} dBA")]
|
||||
[InlineData(SensorType.Conductivity, "{0:F1} µS/cm")]
|
||||
[InlineData(SensorType.Humidity, "{0:F0} %")]
|
||||
[InlineData((SensorType)999, "{0:F1}")]
|
||||
public void GetFormatString_AllSensorTypes(SensorType sensorType, string expected)
|
||||
{
|
||||
var mockSensor = new Mock<ISensor>();
|
||||
mockSensor.Setup(s => s.SensorType).Returns(sensorType);
|
||||
Assert.Equal(expected, SensorFormatter.GetFormatString(mockSensor.Object));
|
||||
}
|
||||
|
||||
// Characterization: pins the GetPlotUnit mapping for every SensorType (Celsius).
|
||||
[Theory]
|
||||
[InlineData(SensorType.Voltage, "V")]
|
||||
[InlineData(SensorType.Current, "A")]
|
||||
[InlineData(SensorType.Clock, "MHz")]
|
||||
[InlineData(SensorType.Load, "%")]
|
||||
[InlineData(SensorType.Temperature, "°C")]
|
||||
[InlineData(SensorType.Fan, "RPM")]
|
||||
[InlineData(SensorType.Flow, "L/h")]
|
||||
[InlineData(SensorType.Control, "%")]
|
||||
[InlineData(SensorType.Level, "%")]
|
||||
[InlineData(SensorType.Power, "W")]
|
||||
[InlineData(SensorType.Data, "GB")]
|
||||
[InlineData(SensorType.SmallData, "MB")]
|
||||
[InlineData(SensorType.Factor, "1")]
|
||||
[InlineData(SensorType.Frequency, "Hz")]
|
||||
[InlineData(SensorType.Throughput, "B/s")]
|
||||
[InlineData(SensorType.TimeSpan, "s")]
|
||||
[InlineData(SensorType.Timing, "ns")]
|
||||
[InlineData(SensorType.Energy, "mWh")]
|
||||
[InlineData(SensorType.Noise, "dBA")]
|
||||
[InlineData(SensorType.Conductivity, "µS/cm")]
|
||||
[InlineData(SensorType.Humidity, "%")]
|
||||
[InlineData((SensorType)999, "")]
|
||||
public void GetPlotUnit_AllSensorTypes_Celsius(SensorType sensorType, string expected)
|
||||
{
|
||||
Assert.Equal(expected, SensorFormatter.GetPlotUnit(sensorType, TemperatureUnit.Celsius));
|
||||
}
|
||||
|
||||
// Characterization: pins FormatValue for the "regular" {0:Fn} unit types (Celsius).
|
||||
// Throughput, TimeSpan, and Temperature-Fahrenheit have their own dedicated tests.
|
||||
[Theory]
|
||||
[InlineData(SensorType.Voltage, 1.234f, "1.234 V")]
|
||||
[InlineData(SensorType.Current, 2.5f, "2.500 A")]
|
||||
[InlineData(SensorType.Clock, 3500f, "3500.0 MHz")]
|
||||
[InlineData(SensorType.Load, 42.5f, "42.5 %")]
|
||||
[InlineData(SensorType.Temperature, 50.5f, "50.5 °C")]
|
||||
[InlineData(SensorType.Fan, 1200f, "1200 RPM")]
|
||||
[InlineData(SensorType.Flow, 10.5f, "10.5 L/h")]
|
||||
[InlineData(SensorType.Control, 75.5f, "75.5 %")]
|
||||
[InlineData(SensorType.Level, 60.5f, "60.5 %")]
|
||||
[InlineData(SensorType.Power, 95.5f, "95.5 W")]
|
||||
[InlineData(SensorType.Data, 8.5f, "8.5 GB")]
|
||||
[InlineData(SensorType.SmallData, 256.5f, "256.5 MB")]
|
||||
[InlineData(SensorType.Factor, 1.234f, "1.234")]
|
||||
[InlineData(SensorType.Frequency, 60.5f, "60.5 Hz")]
|
||||
[InlineData(SensorType.Timing, 1.234f, "1.234 ns")]
|
||||
[InlineData(SensorType.Energy, 1500f, "1500 mWh")]
|
||||
[InlineData(SensorType.Noise, 45f, "45 dBA")]
|
||||
[InlineData(SensorType.Conductivity, 12.5f, "12.5 µS/cm")]
|
||||
[InlineData(SensorType.Humidity, 55f, "55 %")]
|
||||
[InlineData((SensorType)999, 12.34f, "12.3")]
|
||||
public void FormatValue_RegularTypes_Celsius(SensorType sensorType, float value, string expected)
|
||||
{
|
||||
var mockSensor = new Mock<ISensor>();
|
||||
mockSensor.Setup(s => s.SensorType).Returns(sensorType);
|
||||
Assert.Equal(expected, SensorFormatter.FormatValue(mockSensor.Object, value, TemperatureUnit.Celsius));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FormatValue_NullValue_ReturnsDash()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user