using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using DanroJump.Hardware.Com;
using NUnit.Framework;
using UnityEngine;
namespace DanroJump.Tests.EditMode
{
///
/// Тесты для проверки быстрого и безопасного закрытия (graceful shutdown) COM-системы.
///
public sealed class ComSystemShutdownTests
{
private GameObject systemGo;
private ComSystem comSystem;
private MockComTransport mockTransport;
[SetUp]
public void SetUp()
{
systemGo = new GameObject("Test_ComSystem");
comSystem = systemGo.AddComponent();
// Отключаем автоматический запуск и сохранение между сценами
SetPrivateField(comSystem, "startOnEnable", false);
SetPrivateField(comSystem, "dontDestroyOnLoad", false);
SetPrivateField(comSystem, "useEncryptedHandshake", false);
SetPrivateField(comSystem, "keepAlive", false);
}
[TearDown]
public void TearDown()
{
if (comSystem != null)
{
comSystem.StopSystem(false);
}
if (systemGo != null)
{
UnityEngine.Object.DestroyImmediate(systemGo);
}
mockTransport?.Dispose();
}
[Test]
public void ComSystem_StopSystem_CompletesImmediately()
{
// Настраиваем mock-окружение
var mockProvider = new MockComPortProvider();
mockTransport = new MockComTransport();
var mockFactory = new MockComTransportFactory(mockTransport);
comSystem.ConfigureInfrastructure(mockProvider, mockFactory);
// Запускаем систему
comSystem.StartSystem();
Assert.That(comSystem.IsRunning, Is.True);
// Проверяем время остановки
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
comSystem.StopSystem(sendShutdownCommands: false);
stopwatch.Stop();
Assert.That(comSystem.IsRunning, Is.False);
Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(500), "StopSystem took too long to complete!");
}
[Test]
public void ComSystem_StopSystem_WithShutdownCommands_CompletesImmediately()
{
var mockProvider = new MockComPortProvider();
mockTransport = new MockComTransport();
var mockFactory = new MockComTransportFactory(mockTransport);
comSystem.ConfigureInfrastructure(mockProvider, mockFactory);
comSystem.StartSystem();
Assert.That(comSystem.IsRunning, Is.True);
// Проверяем время остановки с отправкой команд выключения
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
comSystem.StopSystem(sendShutdownCommands: true);
stopwatch.Stop();
Assert.That(comSystem.IsRunning, Is.False);
Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(500), "StopSystem with shutdown commands took too long to complete!");
}
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 ArgumentException($"Field '{fieldName}' not found in type '{target.GetType().FullName}'");
}
field.SetValue(target, value);
}
private sealed class MockComPortProvider : IComPortProvider
{
public string[] GetPortNames() => new[] { "COM_TEST" };
}
private sealed class MockComTransportFactory : IComTransportFactory
{
private readonly IComTransport transport;
public MockComTransportFactory(IComTransport transport)
{
this.transport = transport;
}
public IComTransport Create(SerialSettings settings) => transport;
}
private sealed class MockComTransport : IComTransport
{
private readonly AutoResetEvent readEvent = new(false);
private readonly ConcurrentQueue readQueue = new();
private bool isOpen;
public bool IsOpen => isOpen;
public void Open()
{
isOpen = true;
}
public void Close()
{
isOpen = false;
readEvent.Set();
}
public void DiscardInBuffer() { }
public void DiscardOutBuffer() { }
public string ReadLine()
{
while (isOpen)
{
if (readQueue.TryDequeue(out var line))
{
return line;
}
readEvent.WaitOne(10);
}
throw new System.IO.IOException("Port is closed");
}
public void WriteLine(string value)
{
// Для простоты теста просто игнорируем команды записи
}
public void Dispose()
{
Close();
}
}
}
}