279 lines
9.6 KiB
C#
279 lines
9.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using JetBrains.Annotations;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using TBF.Rig.RegisterReaders.PoseidonCmdStartStop;
|
|
|
|
namespace TBFTests.Rig.RegisterReaders.PoseidonCmdStartStop
|
|
{
|
|
[TestClass]
|
|
[TestSubject(typeof(CliRunner))]
|
|
public class CliRunnerTimeoutTest
|
|
{
|
|
private static string GetCmdSleepTestPath()
|
|
{
|
|
var serialPort = new SerialPortData("COM3", "cmdSleepTest.exe", 74);
|
|
return serialPort.SerialPortCmdClientPath;
|
|
}
|
|
|
|
private static void EnsureExeExists(string exePath)
|
|
{
|
|
if (!File.Exists(exePath))
|
|
{
|
|
Assert.Inconclusive(
|
|
$"Test executable '{exePath}' not found. " +
|
|
"Please ensure cmdSleepTest.exe exists in the test directory.");
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task Timeout_ShouldBeDetected_ForLongRunningTask()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
|
|
await Task.Delay(200);
|
|
|
|
bool timedOut = cliRunner.TimeOutReceived(100);
|
|
|
|
Assert.IsTrue(timedOut, "Timeout should have been detected.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task CancelUndoneTasksAsTimedOut_ShouldMarkRunningTaskAsTimedOut()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
|
|
await Task.Delay(150);
|
|
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
|
|
|
var taskInfo = cliRunner.TaskPool.Single();
|
|
|
|
Assert.IsNotNull(taskInfo);
|
|
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
|
Assert.IsFalse(taskInfo.UseResult, "Timed out task must not be used.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CancelUndoneTasksAsTimedOut_ShouldKeepCompletedTaskCompleted()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
|
|
|
try
|
|
{
|
|
cliRunner.WaitAll();
|
|
}
|
|
catch
|
|
{
|
|
// let assertions inspect states
|
|
}
|
|
|
|
cliRunner.RefreshTaskStates();
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
|
|
|
var taskInfo = cliRunner.TaskPool.Single();
|
|
|
|
Assert.IsNotNull(taskInfo);
|
|
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
|
Assert.IsTrue(taskInfo.UseResult, "Completed task should remain usable.");
|
|
|
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
|
Assert.IsNotNull(task);
|
|
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
|
Assert.IsNotNull(task.Result);
|
|
Assert.IsFalse(string.IsNullOrEmpty(task.Result.DeviceId));
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task CancelUndoneTasksAsTimedOut_ShouldKeepDoneTask_AndMarkUndoneTask()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50");
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
|
|
await Task.Delay(250);
|
|
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.AreEqual(2, cliRunner.TaskPool.Count);
|
|
Assert.AreEqual(1, cliRunner.TaskPool.Count(t => t.State == CliTaskState.Completed),
|
|
"Exactly one fast task should be completed before timeout handling.");
|
|
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
var doneTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.Completed);
|
|
var timedOutTask = cliRunner.TaskPool.Single(t => t.State == CliTaskState.TimedOut);
|
|
|
|
Assert.IsTrue(doneTask.UseResult);
|
|
Assert.IsFalse(timedOutTask.UseResult);
|
|
|
|
var completed = doneTask.Task as Task<JsonDataFromPoseidon>;
|
|
Assert.IsNotNull(completed);
|
|
Assert.AreEqual(TaskStatus.RanToCompletion, completed.Status);
|
|
Assert.IsNotNull(completed.Result);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task AreTasksDone_ShouldReturnTrue_AfterTimeoutCancellation()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
|
|
await Task.Delay(150);
|
|
|
|
Assert.IsFalse(cliRunner.AreTasksDone(), "Task should still be running before timeout cancellation.");
|
|
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.IsTrue(cliRunner.AreTasksDone(),
|
|
"After timed out tasks are marked, runner should report no running tasks.");
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task TimedOutTask_ShouldNotHaveUseResult()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
|
|
await Task.Delay(150);
|
|
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
var taskInfo = cliRunner.TaskPool.Single();
|
|
|
|
Assert.AreEqual(CliTaskState.TimedOut, taskInfo.State);
|
|
Assert.IsFalse(taskInfo.UseResult);
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task MultipleSlowTasks_ShouldAllBeMarkedTimedOut()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=5000");
|
|
}
|
|
|
|
await Task.Delay(200);
|
|
|
|
cliRunner.CancelUndoneTasksAsTimedOut();
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.AreEqual(5, cliRunner.TaskPool.Count);
|
|
Assert.AreEqual(5, cliRunner.TaskPool.Count(t => t.State == CliTaskState.TimedOut));
|
|
Assert.AreEqual(0, cliRunner.TaskPool.Count(t => t.UseResult));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void FastTask_WithInvalidJson_ShouldFinishButNotProduceUsefulResult()
|
|
{
|
|
string exePath = GetCmdSleepTestPath();
|
|
EnsureExeExists(exePath);
|
|
|
|
var cliRunner = new CliRunner(false);
|
|
|
|
cliRunner.AddRunAndCaptureJsonAsync<JsonDataFromPoseidon>(exePath, "sleep=50 invalidjson=true");
|
|
|
|
try
|
|
{
|
|
cliRunner.WaitAll();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
cliRunner.RefreshTaskStates();
|
|
|
|
Assert.AreEqual(1, cliRunner.TaskPool.Count);
|
|
|
|
var taskInfo = cliRunner.TaskPool.Single();
|
|
Assert.AreEqual(CliTaskState.Completed, taskInfo.State);
|
|
|
|
var task = taskInfo.Task as Task<JsonDataFromPoseidon>;
|
|
Assert.IsNotNull(task);
|
|
Assert.AreEqual(TaskStatus.RanToCompletion, task.Status);
|
|
|
|
// depending on your TryJsonStringDeserialize fallback,
|
|
// result may be null or an empty/default object
|
|
// so avoid asserting UseResult=false here
|
|
// instead assert no valid device id
|
|
if (task.Result != null)
|
|
{
|
|
Assert.IsTrue(string.IsNullOrEmpty(task.Result.DeviceId),
|
|
"Invalid JSON should not produce a valid DeviceId.");
|
|
}
|
|
}
|
|
|
|
[TestMethod]
|
|
public void SerialPortData_ZeroMeterType_UsesPoseidonCliDefault()
|
|
{
|
|
var serialPort = new SerialPortData("COM3", "HatCliDemo.exe", 0);
|
|
|
|
Assert.AreEqual(SerialPortData.DefaultPoseidonMeterType, serialPort.MeterType);
|
|
Assert.AreEqual(
|
|
"-p COM3 -m 74 --operation readall",
|
|
serialPort.DefaultArgSettings(SerialPortData.EMeterArg.AllParams));
|
|
}
|
|
|
|
[TestMethod]
|
|
public async Task CliProcess_ExitErrorWithoutJson_RecordsDiagnosticsAndReturnsNoReading()
|
|
{
|
|
string cmdExe = Environment.GetEnvironmentVariable("ComSpec") ?? @"C:\Windows\System32\cmd.exe";
|
|
var cliRunner = new CliRunner(false);
|
|
var info = new CliTaskInfo { Name = "missing-dependency-simulation" };
|
|
|
|
JsonDataFromPoseidon result = await cliRunner.RunAndCaptureJsonAsync<JsonDataFromPoseidon>(
|
|
cmdExe,
|
|
"/d /c \"echo Could not load file or assembly 'log4net' 1>&2 & exit /b 17\"",
|
|
info);
|
|
|
|
Assert.IsNull(result);
|
|
Assert.AreEqual(17, info.ExitCode);
|
|
StringAssert.Contains(info.FailureReason, "exit code 17");
|
|
StringAssert.Contains(info.StandardError, "log4net");
|
|
}
|
|
}
|
|
}
|