tbf/TBFTests/Rig/RegisterReaders/GenesisRegReader/implementations/GenesisSmartReaderStopTests.cs
Michal Buzik 0b73871518 Refactor GenesisSmartReader and add advanced test utilities:
- Introduce `ForceProcessingEnabled`, `InstallSuccessfulProcessingOverride`, and `EnqueueIncomingLine` helpers to streamline test setup.
- Add new test suite `GenesisSmartReaderStopTests` to validate `Stop` behavior for different processing states.
- Enhance `GenesisSmartReader` state management with a dedicated `Reset` method and improved `Stop` logic.
- Improve buffer initialization in `ProcessOptoLine` to address null-value edge cases.
- Update assembly version to `3.9.3043.1`.
2026-04-08 13:51:55 +02:00

153 lines
6.3 KiB
C#

using System;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TBF.Rig.RegisterReaders.GenesisRegReader;
using TBF.Rig.RegisterReaders.GenesisRegReader.common;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBFTests.Rig.RegisterReaders.GenesisRegReader.implementations
{
[TestClass]
public class GenesisSmartReaderStopTests
{
private const int ChannelCount = 3;
private static GenesisCfg CreateCfg()
{
Factory factory = new Factory();
return new GenesisCfg(factory);
}
private static void SetPrivateField(object target, string fieldName, object value)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
field.SetValue(target, value);
}
private static T GetPrivateField<T>(object target, string fieldName)
{
var field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new MissingFieldException(target.GetType().FullName, fieldName);
return (T)field.GetValue(target);
}
private static OptoTelegramRaw CreateOptoRecord(int channel, double volumeRawExt, double timestampExt)
{
return new OptoTelegramRaw
{
Flags = OptoTelegramFlags.OK,
VolumeRawExt = volumeRawExt,
TimestampExt = timestampExt,
iChannel = channel
};
}
private static void FillValidOptoDataForPostProcessing(GenesisSmartReader reader)
{
var optoData = GetPrivateField<OptoTelegramRaw[]>(reader, "optoData");
int idx = 0;
for (int group = 0; group < 6; group++)
{
optoData[idx++] = CreateOptoRecord(0, 10 + group, 100 + group);
optoData[idx++] = CreateOptoRecord(1, 20 + group, 100 + group);
optoData[idx++] = CreateOptoRecord(2, 30 + group, 100 + group);
}
SetPrivateField(reader, "optoDataCount", 18);
reader.TestStartTelegramIx = 1;
reader.TestEndTelegramIx = 17;
}
[TestMethod]
public void Stop_WhenStateIsCalculateStoredData_ShouldRunPostProcessing_AndFlushState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
FillValidOptoDataForPostProcessing(reader);
SetPrivateField(reader, "dataStreamState", DataStreamState.CalculateStoredData);
SetPrivateField(reader, "startDataProcessing", false);
SetPrivateField(reader, "_stopQueueData", true);
fake.Open();
reader.optoSerialPort = fake;
reader.Stop();
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
// These values are populated only if DataStreamPostProcessing() ran.
Assert.AreEqual((11.0 + 20.0 + 30.0) / 3.0, reader.VolumeLtrStartRaw, 1e-9,
"Expected post-processing to prepare raw start values.");
Assert.AreEqual((15.0 + 25.0 + 35.0) / 3.0, reader.VolumeLtrEndRaw, 1e-9,
"Expected post-processing to prepare raw end values.");
}
[TestMethod]
public void Stop_WhenStateIsProcessAndSave_ShouldNotRunPostProcessing_AndFlushState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
FillValidOptoDataForPostProcessing(reader);
SetPrivateField(reader, "dataStreamState", DataStreamState.ProcessAndSave);
SetPrivateField(reader, "startDataProcessing", true);
SetPrivateField(reader, "_stopQueueData", false);
fake.Open();
reader.optoSerialPort = fake;
reader.Stop();
var finalState = GetPrivateField<DataStreamState>(reader, "dataStreamState");
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
Assert.AreEqual(DataStreamState.Flush, finalState, "Stop() should finish in Flush state.");
Assert.IsTrue(stopQueueData, "Immediate stop path should stop queueing.");
Assert.IsFalse(fake.IsOpen, "Serial port should be closed by Stop().");
// Should still be default because DataStreamPostProcessing() must NOT run.
Assert.AreEqual(0.0, reader.VolumeLtrStartRaw, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
Assert.AreEqual(0.0, reader.VolumeLtrEndRaw, 1e-9,
"Post-processing should not run when previous state was ProcessAndSave.");
}
[TestMethod]
public void RequestImmediateStop_ShouldNotChangeCalculateStoredDataState()
{
var fake = new FakeSerialDriver();
var reader = new GenesisSmartReader(CreateCfg(), () => fake);
reader.Initialize();
SetPrivateField(reader, "dataStreamState", DataStreamState.CalculateStoredData);
SetPrivateField(reader, "startDataProcessing", true);
SetPrivateField(reader, "_stopQueueData", false);
reader.RequestImmediateStop();
var state = GetPrivateField<DataStreamState>(reader, "dataStreamState");
var stopQueueData = GetPrivateField<bool>(reader, "_stopQueueData");
var startDataProcessing = GetPrivateField<bool>(reader, "startDataProcessing");
Assert.AreEqual(DataStreamState.CalculateStoredData, state,
"RequestImmediateStop() must not destroy CalculateStoredData state.");
Assert.IsTrue(stopQueueData, "Immediate stop should stop queueing.");
Assert.IsFalse(startDataProcessing, "Immediate stop should disable active processing.");
}
}
}