Danro-Jump/Assets/Tests/EditMode/Editor/MainMenuControllerServiceActionTests.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

331 lines
15 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.Generic;
using System.Reflection;
using DanroJump.Prizes.Qr;
using DanroJump.Settings;
using DanroJump.UI.ArcadeInput;
using DanroJump.UI.MainMenu;
using DanroJump.UI.Modals;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.UIElements;
namespace DanroJump.Tests.EditMode
{
public sealed class MainMenuControllerServiceActionTests
{
private GameObject gameObject;
[TearDown]
public void TearDown()
{
if (gameObject != null)
{
UnityEngine.Object.DestroyImmediate(gameObject);
}
}
[Test]
public void QrSettingsAction_OpensQrTierSettingsModal()
{
var controller = CreateController();
var settings = new StubSettingsService();
var qrSettingsRoot = CreateQrSettingsRoot();
var qrSettingsModal = new QrCodeSettingsModalWindow(qrSettingsRoot, settings);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "qrCodeSettingsModal", qrSettingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeServiceAction(
controller,
ServiceSettingsActionResult.Success(
"Настройки QR",
"Открыты настройки QR.",
ServiceSettingsKeys.QrPrizeTextSettings));
Assert.That(qrSettingsModal.IsOpen, Is.True);
Assert.That(qrSettingsRoot.Q<Label>("QrSettingsTierValue").text, Is.EqualTo("Бронзовый"));
Assert.That(qrSettingsRoot.Q<Label>("QrSettingsPrizeTextValue").text, Is.EqualTo("Бронза"));
qrSettingsModal.Dispose();
}
[Test]
public void OpenQrPrizeTextInput_SavesSelectedTierPrizeTextWithoutTechnicalTemplate()
{
var controller = CreateController();
var settings = new StubSettingsService();
var qrSettingsModal = new QrCodeSettingsModalWindow(CreateQrSettingsRoot(), settings);
var textInputRoot = CreateTextInputRoot();
var textInputModal = new ServiceTextInputModalWindow(textInputRoot);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "serviceTextInputModal", textInputModal);
TestObjectFactory.SetPrivateField(controller, "qrCodeSettingsModal", qrSettingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeOpenQrPrizeTextInput(controller, QrPrizeTier.Silver, ServiceSettingsKeys.QrPrizeTextSilver);
Assert.That(textInputModal.IsOpen, Is.True);
Assert.That(textInputRoot.Q<Label>("ServiceTextInputSetting").text, Is.EqualTo("Серебряный QR"));
Assert.That(textInputRoot.Q<Label>("ServiceTextInputPreview").text, Is.EqualTo("Серебро"));
Assert.That(textInputRoot.Q<Label>("ServiceTextInputPreview").text, Does.Not.Contain("{score}"));
Assert.That(textInputModal.Confirm(), Is.True);
Assert.That(settings.LastSetStringKey, Is.EqualTo(ServiceSettingsKeys.QrPrizeTextSilver));
Assert.That(settings.LastSetStringValue, Is.EqualTo("Серебро"));
Assert.That(settings.SaveCalls, Is.EqualTo(1));
qrSettingsModal.Dispose();
textInputModal.Dispose();
}
[Test]
public void OpenSettings_RequiresPinCodeInput()
{
var controller = CreateController();
var settings = new StubSettingsService();
var settingsModal = new SettingsModalWindow(new VisualElement(), settings, null);
var textInputRoot = CreateTextInputRoot();
var textInputModal = new ServiceTextInputModalWindow(textInputRoot);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "serviceTextInputModal", textInputModal);
TestObjectFactory.SetPrivateField(controller, "settingsModal", settingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeOpenSettings(controller);
Assert.That(textInputModal.IsOpen, Is.True);
Assert.That(settingsModal.IsOpen, Is.False);
Assert.That(textInputRoot.Q<Label>("ServiceTextInputTitle").text, Is.EqualTo("Авторизация"));
Assert.That(textInputRoot.Q<Label>("ServiceTextInputSetting").text, Is.EqualTo("Введите PIN-код для доступа к настройкам"));
settingsModal.Dispose();
textInputModal.Dispose();
}
[Test]
public void OpenSettings_WithCorrectPIN_OpensSettingsModal()
{
var controller = CreateController();
var settings = new StubSettingsService();
var settingsModal = new SettingsModalWindow(new VisualElement(), settings, null);
var textInputRoot = CreateTextInputRoot();
var textInputModal = new ServiceTextInputModalWindow(textInputRoot);
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "serviceTextInputModal", textInputModal);
TestObjectFactory.SetPrivateField(controller, "settingsModal", settingsModal);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeOpenSettings(controller);
var profile = new ArcadeStringInputProfile("0123456789", 4);
var correctState = new ArcadeStringInputState(profile, "1234");
TestObjectFactory.SetPrivateField(textInputModal, "state", correctState);
Assert.That(textInputModal.Confirm(), Is.True);
Assert.That(settingsModal.IsOpen, Is.True);
settingsModal.Dispose();
textInputModal.Dispose();
}
[Test]
public void OpenSettings_WithIncorrectPIN_DoesNotOpenSettingsModalAndPlaysError()
{
var controller = CreateController();
var settings = new StubSettingsService();
var settingsModal = new SettingsModalWindow(new VisualElement(), settings, null);
var textInputRoot = CreateTextInputRoot();
var textInputModal = new ServiceTextInputModalWindow(textInputRoot);
var stubAudio = new StubAudioService();
TestObjectFactory.SetPrivateField(controller, "serviceSettingsService", settings);
TestObjectFactory.SetPrivateField(controller, "serviceTextInputModal", textInputModal);
TestObjectFactory.SetPrivateField(controller, "settingsModal", settingsModal);
TestObjectFactory.SetPrivateField(controller, "audioService", stubAudio);
TestObjectFactory.SetPrivateField(controller, "modalStack", new ModalStackController(new VisualElement(), null));
InvokeOpenSettings(controller);
var profile = new ArcadeStringInputProfile("0123456789", 4);
var incorrectState = new ArcadeStringInputState(profile, "9999");
TestObjectFactory.SetPrivateField(textInputModal, "state", incorrectState);
Assert.That(textInputModal.Confirm(), Is.True);
Assert.That(settingsModal.IsOpen, Is.False);
Assert.That(stubAudio.LastPlayEvent, Is.EqualTo(DanroJump.Audio.GameAudioEvent.UiError));
settingsModal.Dispose();
textInputModal.Dispose();
}
private MainMenuController CreateController()
{
gameObject = new GameObject("MainMenuControllerServiceActionTest");
return gameObject.AddComponent<MainMenuController>();
}
private static void InvokeServiceAction(
MainMenuController controller,
ServiceSettingsActionResult result)
{
var method = typeof(MainMenuController).GetMethod(
"HandleServiceActionActivated",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
method.Invoke(controller, new object[] { null, result });
}
private static void InvokeOpenQrPrizeTextInput(
MainMenuController controller,
QrPrizeTier tier,
string settingKey)
{
var method = typeof(MainMenuController).GetMethod(
"OpenQrPrizeTextInput",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
method.Invoke(controller, new object[] { tier, settingKey });
}
private static void InvokeOpenSettings(MainMenuController controller)
{
var method = typeof(MainMenuController).GetMethod(
"OpenSettings",
BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(method, Is.Not.Null);
method.Invoke(controller, null);
}
private static VisualElement CreateQrSettingsRoot()
{
var root = new VisualElement { name = "QrSettingsModal" };
root.Add(new Label { name = "QrSettingsTierValue" });
root.Add(new Label { name = "QrSettingsPrizeTextValue" });
root.Add(new Label { name = "QrSettingsStatus" });
root.Add(new Button { name = "QrSettingsTierPreviousButton" });
root.Add(new Button { name = "QrSettingsTierNextButton" });
root.Add(new Button { name = "QrSettingsEditTextButton" });
root.Add(new Button { name = "QrSettingsCloseButton" });
return root;
}
private static VisualElement CreateTextInputRoot()
{
var root = new VisualElement { name = "ServiceTextInputModal" };
root.Add(new Label { name = "ServiceTextInputTitle" });
root.Add(new Label { name = "ServiceTextInputSetting" });
root.Add(new Label { name = "ServiceTextInputPreview" });
root.Add(new VisualElement { name = "ServiceTextInputSlots" });
root.Add(new Label { name = "ServiceTextInputSymbol" });
root.Add(new Label { name = "ServiceTextInputStatus" });
root.Add(new Button { name = "ServiceTextInputConfirmButton" });
root.Add(new Button { name = "ServiceTextInputDeleteButton" });
root.Add(new Button { name = "ServiceTextInputCancelButton" });
return root;
}
private sealed class StubAudioService : DanroJump.Audio.IGameAudioService
{
public bool IsReady => true;
public bool IsMusicPlaying => false;
public DanroJump.Audio.GameMusicCue? CurrentMusicCue => null;
public DanroJump.Audio.GameAudioEvent? LastPlayEvent { get; private set; }
public void Play(DanroJump.Audio.GameAudioEvent eventId) => LastPlayEvent = eventId;
public void Play(DanroJump.Audio.GameAudioEvent eventId, Vector3 worldPosition) => LastPlayEvent = eventId;
public void StartMusic() { }
public void PlayMusic(DanroJump.Audio.GameMusicCue cue) { }
public void StopMusic() { }
public void ApplyServiceVolumes() { }
}
private sealed class StubSettingsService : IServiceSettingsService
{
private readonly Dictionary<string, ServiceSettingDefinition> definitions = new();
private readonly Dictionary<string, string> values = new();
public StubSettingsService()
{
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextBronze, "Бронзовый QR", "Бронза");
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextSilver, "Серебряный QR", "Серебро");
AddStringDefinition(ServiceSettingsKeys.QrPrizeTextGold, "Золотой QR", "Золото");
}
public string LastSetStringKey { get; private set; }
public string LastSetStringValue { get; private set; }
public int SaveCalls { get; private set; }
public ServiceSettingsDatabase Database => null;
public event Action SettingsLoaded { add { } remove { } }
public event Action SettingsSaved { add { } remove { } }
public event Action<string, ServiceSettingValue> SettingChanged { add { } remove { } }
public bool TryGetDefinition(string key, out ServiceSettingDefinition definition)
{
return definitions.TryGetValue(key, out definition);
}
public IReadOnlyDictionary<string, ServiceSettingValue> Snapshot()
{
return new Dictionary<string, ServiceSettingValue>();
}
public ServiceSettingValue Get(string key)
{
return ServiceSettingValue.String(GetString(key));
}
public bool GetBool(string key) => false;
public int GetInt(string key) => 0;
public float GetFloat(string key) => 0f;
public string GetString(string key)
{
return values.TryGetValue(key, out var value) ? value : string.Empty;
}
public int GetOptionIndex(string key) => 0;
public void Load() { }
public void Save() => SaveCalls++;
public void ReloadExternalChanges() { }
public void ApplyAll() { }
public void ResetAllToDefaults() { }
public void ResetGroupToDefaults(string groupName) { }
public bool Set(string key, ServiceSettingValue value) => true;
public bool SetExternal(string key, ServiceSettingValue value) => true;
public bool SetBool(string key, bool value) => true;
public bool SetInt(string key, int value) => true;
public bool SetFloat(string key, float value) => true;
public bool SetString(string key, string value)
{
LastSetStringKey = key;
LastSetStringValue = value;
values[key] = value;
return true;
}
public bool SetOptionIndex(string key, int optionIndex) => true;
private void AddStringDefinition(string key, string displayName, string value)
{
values[key] = value;
var definition = TestObjectFactory.CreateSettingDefinition(
key,
ServiceSettingValueType.String,
ServiceSettingValue.String(value));
TestObjectFactory.SetPrivateField(definition, "displayName", displayName);
definitions[key] = definition;
}
}
}
}