Danro-Jump/Assets/Tests/EditMode/Editor/ComSystemShutdownTests.cs
Danil Iarosh 9549d42ea3 Add unit tests for security contour, COM port shutdown and VFX service
- Äîáàâëåíû EditMode òåñòû äëÿ âåðèôèêàöèè ââîäà PIN-êîäà àâòîðèçàöèè ïåðåä îòêðûòèåì ìåíþ íàñòðîåê â MainMenuControllerServiceActionTests.cs.
- Ñîçäàíû íîâûå EditMode òåñòû VfxServiceTests.cs, ïîêðûâàþùèå null-safety è êîððåêòíîå ñîçäàíèå GameObject ñèñòåì ÷àñòèö äëÿ IVfxService è GameVfxService.
- Ñîçäàí íîâûé EditMode êëàññ òåñòîâ ComSystemShutdownTests.cs, ïðîâåðÿþùèé áûñòðîå è áåçîïàñíîå çàêðûòèå COM-ñèñòåìû.
- Èñïðàâëåí ïîðÿäîê âûçîâà RequestClose() â ServiceTextInputModalWindow.Confirm() äëÿ ïðåäîòâðàùåíèÿ çàêðûòèÿ ìîäàëüíûõ îêîí ïîâåðõ òåêóùåãî ïðè çàêðûòèè îêíà ââîäà.
- Èñïðàâëåíî óíè÷òîæåíèå îáúåêòîâ â GameVfxService.cs è JumpPlayerController.cs â EditMode ñ ïîìîùüþ ïðîâåðêè Application.isPlaying.
- Äîáàâëåíà ëåíèâàÿ èíèöèàëèçàöèÿ Rigidbody2D â ìåòîäàõ Bounce() è Die() â JumpPlayerController.cs äëÿ ïðåäîòâðàùåíèÿ NullReferenceException â èçîëèðîâàííûõ òåñòàõ.
- Âñå 174 EditMode è 7 PlayMode òåñòîâ óñïåøíî ïðîéäåíû â Unity Editor.
2026-06-23 15:38:21 +03:00

167 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// Тесты для проверки быстрого и безопасного закрытия (graceful shutdown) COM-системы.
/// </summary>
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<ComSystem>();
// Отключаем автоматический запуск и сохранение между сценами
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<string> 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();
}
}
}
}