887 lines
41 KiB
C#
887 lines
41 KiB
C#
using System.Collections;
|
|
using System.Reflection;
|
|
using BumpCombat.Audio;
|
|
using BumpCombat.Combat;
|
|
using BumpCombat.Core;
|
|
using BumpCombat.Enemies;
|
|
using BumpCombat.Player;
|
|
using BumpCombat.Progression;
|
|
using BumpCombat.Spawning;
|
|
using BumpCombat.UI;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.InputSystem.LowLevel;
|
|
using UnityEngine.InputSystem.UI;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.TestTools;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.UI;
|
|
|
|
namespace BumpCombat.PlayModeTests
|
|
{
|
|
public sealed class PresentationAudioMenuTests
|
|
{
|
|
private const string SceneName = "CombatPrototype";
|
|
private float originalMaster;
|
|
private float originalMusic;
|
|
private float originalSfx;
|
|
private bool capturedVolumes;
|
|
private InputSettings.BackgroundBehavior previousBackgroundBehavior;
|
|
private InputSettings.EditorInputBehaviorInPlayMode previousEditorInputBehavior;
|
|
private bool previousRunInBackground;
|
|
private Keyboard virtualKeyboard;
|
|
private bool virtualKeyboardInputActive;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = true;
|
|
Time.timeScale = 1f;
|
|
capturedVolumes = false;
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
EndVirtualKeyboardInput();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
if (service != null && capturedVolumes)
|
|
{
|
|
service.SetMasterVolume(originalMaster);
|
|
service.SetMusicVolume(originalMusic);
|
|
service.SetSfxVolume(originalSfx);
|
|
}
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
Time.timeScale = 1f;
|
|
}
|
|
|
|
[Test]
|
|
public void DevelopmentStartVisibility_RequiresEditorOrDevelopmentBuild()
|
|
{
|
|
MethodInfo predicate = typeof(RunMenuController).GetMethod(
|
|
"IsDevelopmentStartVisible",
|
|
BindingFlags.NonPublic | BindingFlags.Static);
|
|
Assert.That(predicate, Is.Not.Null);
|
|
Assert.That(predicate.Invoke(null, new object[] { false, false }), Is.False);
|
|
Assert.That(predicate.Invoke(null, new object[] { true, false }), Is.True);
|
|
Assert.That(predicate.Invoke(null, new object[] { false, true }), Is.True);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator TitleStart_UnlocksRunAfterExplicitStart()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
Assert.That(manager, Is.Not.Null);
|
|
Assert.That(manager.IsTitleScreen, Is.True);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.False);
|
|
Assert.That(Time.timeScale, Is.Zero);
|
|
|
|
Keyboard keyboard = BeginVirtualKeyboardInput();
|
|
try
|
|
{
|
|
yield return PressAndRelease(keyboard, Key.Space);
|
|
Assert.That(manager.IsTitleScreen, Is.True,
|
|
"Space opens the mode submenu without starting a run.");
|
|
yield return PressAndRelease(keyboard, Key.Enter);
|
|
}
|
|
finally
|
|
{
|
|
EndVirtualKeyboardInput();
|
|
}
|
|
Assert.That(manager.IsTitleScreen, Is.False);
|
|
Assert.That(manager.IsProductionRun, Is.False);
|
|
Assert.That(manager.UseDebugEventTimes, Is.True);
|
|
Assert.That(manager.IsArtifactChargeUnlocked, Is.True);
|
|
ArtifactRewardController rewards =
|
|
Object.FindAnyObjectByType<ArtifactRewardController>();
|
|
Assert.That(rewards, Is.Not.Null);
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
RunTutorialController tutorials =
|
|
Object.FindAnyObjectByType<RunTutorialController>();
|
|
Assert.That(tutorials.DebugCurrentTutorial,
|
|
Is.EqualTo(RunTutorialController.TutorialKind.Artifact));
|
|
Assert.That(tutorials.DebugContinue(), Is.True);
|
|
Assert.That(tutorials.DebugCurrentTutorial,
|
|
Is.EqualTo(RunTutorialController.TutorialKind.Guard));
|
|
Assert.That(tutorials.DebugContinue(), Is.True);
|
|
Assert.That(tutorials.DebugCurrentTutorial,
|
|
Is.EqualTo(RunTutorialController.TutorialKind.ArtifactEnhancement));
|
|
Assert.That(tutorials.DebugContinue(), Is.True);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.True);
|
|
Assert.That(Time.timeScale, Is.EqualTo(1f));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator TitleProductionStart_IsSecondKeyboardItemAndLabelsFit()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
Transform menu = Object.FindAnyObjectByType<RunMenuController>()
|
|
.transform.Find("Run Menu");
|
|
Text title = menu.Find("Title").GetComponent<Text>();
|
|
Assert.That(title.text, Is.EqualTo("Tiny Tackle Heroes"));
|
|
float titlePreferredWidth = title.cachedTextGeneratorForLayout
|
|
.GetPreferredWidth(
|
|
title.text,
|
|
title.GetGenerationSettings(Vector2.zero))
|
|
/ title.pixelsPerUnit;
|
|
Assert.That(title.cachedTextGeneratorForLayout.lineCount, Is.EqualTo(1));
|
|
Assert.That(titlePreferredWidth,
|
|
Is.LessThanOrEqualTo(title.rectTransform.rect.width),
|
|
"The game title must fit in its single-line title label.");
|
|
string[] expectedLabels = { "게임시작", "설정", "업적", "나가기" };
|
|
for (int i = 0; i < expectedLabels.Length; i++)
|
|
{
|
|
Text label = menu.Find($"Button {i + 1}/Label").GetComponent<Text>();
|
|
Assert.That(label.text, Is.EqualTo(expectedLabels[i]));
|
|
float preferredWidth = label.cachedTextGeneratorForLayout
|
|
.GetPreferredWidth(
|
|
label.text,
|
|
label.GetGenerationSettings(Vector2.zero))
|
|
/ label.pixelsPerUnit;
|
|
Assert.That(label.cachedTextGeneratorForLayout.lineCount, Is.EqualTo(1));
|
|
float labelWidth = label.rectTransform.rect.width;
|
|
Assert.That(
|
|
preferredWidth,
|
|
Is.LessThanOrEqualTo(labelWidth),
|
|
$"{expectedLabels[i]} must fit in its single-line menu label.");
|
|
}
|
|
|
|
Keyboard keyboard = BeginVirtualKeyboardInput();
|
|
try
|
|
{
|
|
yield return PressAndRelease(keyboard, Key.Enter);
|
|
Assert.That(manager.IsTitleScreen, Is.True,
|
|
"Opening the mode submenu must not begin a run.");
|
|
|
|
Transform submenu = menu;
|
|
bool showDevelopment = Application.isEditor || Debug.isDebugBuild;
|
|
var submenuLabels = new System.Collections.Generic.List<string>();
|
|
if (showDevelopment)
|
|
{
|
|
submenuLabels.Add("개발용 시작");
|
|
}
|
|
submenuLabels.Add("기본 모드");
|
|
submenuLabels.Add("챌린지 모드(준비 중)");
|
|
submenuLabels.Add("하드코어 모드(준비 중)");
|
|
submenuLabels.Add("뒤로");
|
|
for (int i = 0; i < submenuLabels.Count; i++)
|
|
{
|
|
Button button = submenu.Find($"Button {i + 1}").GetComponent<Button>();
|
|
Text label = button.GetComponentInChildren<Text>();
|
|
Assert.That(button.gameObject.activeSelf, Is.True);
|
|
Assert.That(label.text, Is.EqualTo(submenuLabels[i]));
|
|
float preferredWidth = label.cachedTextGeneratorForLayout
|
|
.GetPreferredWidth(
|
|
label.text,
|
|
label.GetGenerationSettings(Vector2.zero))
|
|
/ label.pixelsPerUnit;
|
|
Assert.That(label.cachedTextGeneratorForLayout.lineCount, Is.EqualTo(1));
|
|
Assert.That(preferredWidth, Is.LessThanOrEqualTo(label.rectTransform.rect.width),
|
|
$"{label.text} must fit in its single-line menu label.");
|
|
Assert.That(button.interactable,
|
|
Is.EqualTo(i < submenuLabels.Count - 3 || i == submenuLabels.Count - 1),
|
|
label.text);
|
|
}
|
|
|
|
int placeholderIndex = showDevelopment ? 2 : 1;
|
|
for (int i = placeholderIndex; i < placeholderIndex + 2; i++)
|
|
{
|
|
submenu.Find($"Button {i + 1}").GetComponent<Button>().onClick.Invoke();
|
|
Assert.That(manager.IsTitleScreen, Is.True,
|
|
"A preparation placeholder cannot launch a run.");
|
|
}
|
|
|
|
if (showDevelopment)
|
|
{
|
|
yield return PressAndRelease(keyboard, Key.DownArrow);
|
|
}
|
|
Assert.That(
|
|
EventSystem.current.currentSelectedGameObject
|
|
.GetComponentInChildren<Text>().text,
|
|
Is.EqualTo("기본 모드"));
|
|
yield return PressAndRelease(keyboard, Key.DownArrow);
|
|
Assert.That(
|
|
EventSystem.current.currentSelectedGameObject
|
|
.GetComponentInChildren<Text>().text,
|
|
Is.EqualTo("뒤로"),
|
|
"Keyboard navigation skips disabled mode placeholders.");
|
|
yield return PressAndRelease(keyboard, Key.UpArrow);
|
|
Assert.That(
|
|
EventSystem.current.currentSelectedGameObject
|
|
.GetComponentInChildren<Text>().text,
|
|
Is.EqualTo("기본 모드"));
|
|
yield return PressAndRelease(keyboard, Key.Enter);
|
|
}
|
|
finally
|
|
{
|
|
EndVirtualKeyboardInput();
|
|
}
|
|
|
|
Assert.That(manager.IsTitleScreen, Is.False);
|
|
Assert.That(manager.IsProductionRun, Is.True);
|
|
Assert.That(manager.UseDebugEventTimes, Is.False);
|
|
Assert.That(manager.IsGuardUnlocked, Is.False);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator UiInitialization_ReusesEventSystemAndPreservesPointerModule()
|
|
{
|
|
yield return LoadScene();
|
|
EventSystem original = EventSystem.current;
|
|
Assert.That(original, Is.Not.Null);
|
|
Object.Destroy(original.gameObject);
|
|
yield return null;
|
|
|
|
EnsurePresentationEventSystem(false);
|
|
EventSystem eventSystem = EventSystem.current;
|
|
Assert.That(eventSystem, Is.Not.Null);
|
|
Assert.That(CountActiveSceneEventSystems(), Is.EqualTo(1));
|
|
InputSystemUIInputModule module =
|
|
eventSystem.GetComponent<InputSystemUIInputModule>();
|
|
Assert.That(module, Is.Not.Null);
|
|
Assert.That(module.point, Is.Not.Null);
|
|
Assert.That(module.leftClick, Is.Not.Null);
|
|
Assert.That(module.scrollWheel, Is.Not.Null);
|
|
Assert.That(module.move, Is.Not.Null);
|
|
Assert.That(module.submit, Is.Not.Null);
|
|
Assert.That(module.cancel, Is.Not.Null);
|
|
var pointAction = module.point;
|
|
var clickAction = module.leftClick;
|
|
var scrollAction = module.scrollWheel;
|
|
var moveAction = module.move;
|
|
var submitAction = module.submit;
|
|
var cancelAction = module.cancel;
|
|
|
|
// Exercise HUD -> menu -> HUD initialization, including repeats.
|
|
EnsurePresentationEventSystem(false);
|
|
Assert.That(module.move, Is.SameAs(moveAction));
|
|
Assert.That(module.submit, Is.SameAs(submitAction));
|
|
Assert.That(module.cancel, Is.SameAs(cancelAction));
|
|
EnsurePresentationEventSystem(true);
|
|
EnsurePresentationEventSystem(false);
|
|
Assert.That(EventSystem.current, Is.SameAs(eventSystem));
|
|
Assert.That(CountActiveSceneEventSystems(), Is.EqualTo(1));
|
|
Assert.That(eventSystem.GetComponents<BaseInputModule>(),
|
|
Has.Length.EqualTo(1));
|
|
Assert.That(eventSystem.GetComponent<InputSystemUIInputModule>(),
|
|
Is.SameAs(module));
|
|
Assert.That(module.move, Is.Null);
|
|
Assert.That(module.submit, Is.Null);
|
|
Assert.That(module.cancel, Is.Null);
|
|
Assert.That(module.point, Is.SameAs(pointAction));
|
|
Assert.That(module.leftClick, Is.SameAs(clickAction));
|
|
Assert.That(module.scrollWheel, Is.SameAs(scrollAction));
|
|
|
|
Object.Destroy(module);
|
|
yield return null;
|
|
StandaloneInputModule existingModule =
|
|
eventSystem.gameObject.AddComponent<StandaloneInputModule>();
|
|
existingModule.enabled = false;
|
|
EnsurePresentationEventSystem(false);
|
|
EnsurePresentationEventSystem(true);
|
|
Assert.That(eventSystem.GetComponent<BaseInputModule>(),
|
|
Is.SameAs(existingModule));
|
|
Assert.That(eventSystem.GetComponent<InputSystemUIInputModule>(),
|
|
Is.Null);
|
|
Assert.That(CountActiveSceneEventSystems(), Is.EqualTo(1));
|
|
|
|
InputSystemUIInputModule secondaryModule =
|
|
eventSystem.gameObject.AddComponent<InputSystemUIInputModule>();
|
|
var secondaryPoint = secondaryModule.point;
|
|
var secondaryClick = secondaryModule.leftClick;
|
|
var secondaryScroll = secondaryModule.scrollWheel;
|
|
EnsurePresentationEventSystem(false);
|
|
EnsurePresentationEventSystem(true);
|
|
EnsurePresentationEventSystem(false);
|
|
Assert.That(eventSystem.GetComponent<BaseInputModule>(),
|
|
Is.SameAs(existingModule));
|
|
Assert.That(eventSystem.GetComponent<InputSystemUIInputModule>(),
|
|
Is.SameAs(secondaryModule));
|
|
Assert.That(eventSystem.GetComponents<BaseInputModule>(),
|
|
Has.Length.EqualTo(2));
|
|
Assert.That(secondaryModule.move, Is.Null);
|
|
Assert.That(secondaryModule.submit, Is.Null);
|
|
Assert.That(secondaryModule.cancel, Is.Null);
|
|
Assert.That(secondaryModule.point, Is.SameAs(secondaryPoint));
|
|
Assert.That(secondaryModule.leftClick, Is.SameAs(secondaryClick));
|
|
Assert.That(secondaryModule.scrollWheel, Is.SameAs(secondaryScroll));
|
|
|
|
Object.Destroy(secondaryModule);
|
|
Object.Destroy(existingModule);
|
|
yield return null;
|
|
EnsurePresentationEventSystem(false);
|
|
InputSystemUIInputModule restoredModule =
|
|
eventSystem.GetComponent<InputSystemUIInputModule>();
|
|
Assert.That(restoredModule, Is.Not.Null);
|
|
Assert.That(eventSystem.GetComponents<BaseInputModule>(),
|
|
Has.Length.EqualTo(1));
|
|
Assert.That(CountActiveSceneEventSystems(), Is.EqualTo(1));
|
|
EnsurePresentationEventSystem(true);
|
|
Assert.That(restoredModule.move, Is.Null);
|
|
Assert.That(restoredModule.submit, Is.Null);
|
|
Assert.That(restoredModule.cancel, Is.Null);
|
|
Assert.That(restoredModule.point, Is.Not.Null);
|
|
Assert.That(restoredModule.leftClick, Is.Not.Null);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator ArtifactSelection_SharedCardsKeepIconAndTextLayout()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = true;
|
|
yield return LoadScene();
|
|
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
ArtifactRewardController rewards =
|
|
Object.FindAnyObjectByType<ArtifactRewardController>();
|
|
Assert.That(manager.BeginRun(), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
|
|
GameObject panel = GameObject.Find("Artifact Reward Selection");
|
|
Text optionText = panel.transform.Find("Option 1").GetComponent<Text>();
|
|
Image card = panel.transform.Find("Option 1 Card").GetComponent<Image>();
|
|
Image icon = panel.transform.Find("Option 1 Icon").GetComponent<Image>();
|
|
RectTransform textRect = optionText.rectTransform;
|
|
RectTransform cardRect = card.rectTransform;
|
|
RectTransform iconRect = icon.rectTransform;
|
|
Assert.That(panel.GetComponent<Image>().type, Is.EqualTo(Image.Type.Sliced));
|
|
Assert.That(optionText.alignment, Is.EqualTo(TextAnchor.MiddleCenter));
|
|
Assert.That(optionText.font, Is.SameAs(
|
|
Resources.Load<Font>("Presentation/Fonts/Galmuri9")));
|
|
Assert.That(optionText.raycastTarget, Is.False);
|
|
Assert.That(textRect.sizeDelta, Is.EqualTo(new Vector2(380f, 64f)));
|
|
Assert.That(cardRect.anchorMin, Is.EqualTo(textRect.anchorMin));
|
|
Assert.That(cardRect.anchorMax, Is.EqualTo(textRect.anchorMax));
|
|
Assert.That(cardRect.pivot, Is.EqualTo(textRect.pivot));
|
|
Assert.That(cardRect.anchoredPosition,
|
|
Is.EqualTo(textRect.anchoredPosition + Vector2.up * 25f));
|
|
Assert.That(cardRect.sizeDelta, Is.EqualTo(new Vector2(380f, 128f)));
|
|
Assert.That(card.transform.GetSiblingIndex(),
|
|
Is.EqualTo(optionText.transform.GetSiblingIndex() - 2));
|
|
Assert.That(icon.transform.GetSiblingIndex(),
|
|
Is.EqualTo(optionText.transform.GetSiblingIndex() - 1));
|
|
Assert.That(card.raycastTarget, Is.False);
|
|
Assert.That(icon.preserveAspect, Is.True);
|
|
Assert.That(icon.raycastTarget, Is.False);
|
|
Assert.That(iconRect.anchorMin, Is.EqualTo(textRect.anchorMin));
|
|
Assert.That(iconRect.anchorMax, Is.EqualTo(textRect.anchorMax));
|
|
Assert.That(iconRect.pivot, Is.EqualTo(textRect.pivot));
|
|
Assert.That(iconRect.anchoredPosition,
|
|
Is.EqualTo(textRect.anchoredPosition + Vector2.up * 45f));
|
|
Assert.That(iconRect.sizeDelta, Is.EqualTo(new Vector2(48f, 48f)));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator MenuKeyboard_SettingsSliderAndBack_AreSingleStep()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
Keyboard keyboard = BeginVirtualKeyboardInput();
|
|
try
|
|
{
|
|
yield return PressAndRelease(keyboard, Key.DownArrow);
|
|
GameObject selected = EventSystem.current.currentSelectedGameObject;
|
|
Assert.That(selected, Is.Not.Null);
|
|
Assert.That(selected.GetComponentInChildren<Text>().text, Is.EqualTo("설정"));
|
|
|
|
yield return PressAndRelease(keyboard, Key.Enter);
|
|
Slider slider = GameObject.Find("Master Slider").GetComponent<Slider>();
|
|
Assert.That(slider.gameObject.activeSelf, Is.True);
|
|
slider.value = 0.5f;
|
|
float before = slider.value;
|
|
|
|
yield return PressAndRelease(keyboard, Key.RightArrow);
|
|
Assert.That(slider.value, Is.EqualTo(before + 0.05f).Within(0.001f));
|
|
|
|
yield return PressAndRelease(keyboard, Key.Escape);
|
|
Assert.That(slider.gameObject.activeSelf, Is.False);
|
|
Assert.That(manager.IsTitleScreen, Is.True);
|
|
Assert.That(service.MasterVolume, Is.EqualTo(slider.value).Within(0.001f));
|
|
}
|
|
finally
|
|
{
|
|
EndVirtualKeyboardInput();
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator SettingsFocus_IsVisibleAndPointerBackSyncsKeyboardSelection()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
RunMenuController menuController = Object.FindAnyObjectByType<RunMenuController>();
|
|
Assert.That(menuController, Is.Not.Null);
|
|
Transform menu = menuController.transform.Find("Run Menu");
|
|
Text inputHelp = menu.Find("Menu Input Help").GetComponent<Text>();
|
|
Assert.That(inputHelp.text, Is.EqualTo("항목 이동: 위/아래 선택: Enter/Space"));
|
|
Assert.That(inputHelp.rectTransform.anchorMin, Is.EqualTo(Vector2.zero));
|
|
Assert.That(inputHelp.alignment, Is.EqualTo(TextAnchor.MiddleLeft));
|
|
Assert.That(menu.Find("Button 1/Focus Marker").gameObject.activeSelf, Is.True);
|
|
|
|
Keyboard keyboard = BeginVirtualKeyboardInput();
|
|
try
|
|
{
|
|
menu.Find("Button 2").GetComponent<Button>().onClick.Invoke();
|
|
yield return null;
|
|
Assert.That(inputHelp.text, Is.EqualTo(
|
|
"항목 이동: 위/아래 음량 조절: 왼쪽/오른쪽\n뒤로 선택: Enter/Space 즉시 뒤로: Esc"));
|
|
|
|
GameObject masterFocus = menu.Find("Settings Focus 0").gameObject;
|
|
GameObject musicFocus = menu.Find("Settings Focus 1").gameObject;
|
|
GameObject backFocus = menu.Find("Settings Focus 3").gameObject;
|
|
Slider masterSlider = menu.Find("Master Slider").GetComponent<Slider>();
|
|
Button backButton = menu.Find("Button 4").GetComponent<Button>();
|
|
Assert.That(masterFocus.activeSelf, Is.True);
|
|
Assert.That(EventSystem.current.currentSelectedGameObject, Is.EqualTo(masterSlider.gameObject));
|
|
|
|
yield return PressAndRelease(keyboard, Key.DownArrow);
|
|
Assert.That(masterFocus.activeSelf, Is.False);
|
|
Assert.That(musicFocus.activeSelf, Is.True);
|
|
|
|
ExecuteEvents.Execute(
|
|
backButton.gameObject,
|
|
new PointerEventData(EventSystem.current),
|
|
ExecuteEvents.pointerEnterHandler);
|
|
Assert.That(EventSystem.current.currentSelectedGameObject, Is.EqualTo(backButton.gameObject));
|
|
Assert.That(backFocus.activeSelf, Is.True);
|
|
|
|
yield return PressAndRelease(keyboard, Key.DownArrow);
|
|
Assert.That(masterFocus.activeSelf, Is.True);
|
|
Assert.That(backFocus.activeSelf, Is.False);
|
|
Assert.That(EventSystem.current.currentSelectedGameObject, Is.EqualTo(masterSlider.gameObject));
|
|
|
|
yield return PressAndRelease(keyboard, Key.UpArrow);
|
|
yield return PressAndRelease(keyboard, Key.Enter);
|
|
}
|
|
finally
|
|
{
|
|
EndVirtualKeyboardInput();
|
|
}
|
|
|
|
Assert.That(manager.IsTitleScreen, Is.True);
|
|
Assert.That(inputHelp.text, Is.EqualTo("항목 이동: 위/아래 선택: Enter/Space"));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator PauseAndSelection_KeepEachOtherInterlocked()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
Assert.That(manager.BeginRun(), Is.True);
|
|
yield return null;
|
|
ArtifactRewardController rewards =
|
|
Object.FindAnyObjectByType<ArtifactRewardController>();
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
|
|
CloseArtifactTutorialModal(manager);
|
|
|
|
GameObject ownerObject = new("Selection Owner Test");
|
|
SelectionOwner owner = ownerObject.AddComponent<SelectionOwner>();
|
|
try
|
|
{
|
|
Assert.That(manager.TryOpenSelection(owner), Is.True);
|
|
Assert.That(manager.TryOpenPause(), Is.False);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.False);
|
|
Assert.That(manager.CloseSelection(owner), Is.True);
|
|
Assert.That(manager.TryOpenPause(), Is.True);
|
|
Text inputHelp = GameObject.Find("Menu Input Help").GetComponent<Text>();
|
|
Assert.That(inputHelp.text, Is.EqualTo(
|
|
"항목 이동: 위/아래 선택: Enter/Space 계속: Esc"));
|
|
Transform menu = Object.FindAnyObjectByType<RunMenuController>().transform.Find("Run Menu");
|
|
Assert.That(menu.Find("Button 1/Focus Marker").gameObject.activeSelf, Is.True);
|
|
Assert.That(manager.TryOpenSelection(owner), Is.False);
|
|
Assert.That(manager.ClosePause(), Is.True);
|
|
}
|
|
finally
|
|
{
|
|
Object.Destroy(ownerObject);
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator StartupSelection_RecoversWhenPausedBeforeItsFirstFrame()
|
|
{
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
ArtifactRewardController rewards =
|
|
Object.FindAnyObjectByType<ArtifactRewardController>();
|
|
Assert.That(manager, Is.Not.Null);
|
|
Assert.That(rewards, Is.Not.Null);
|
|
|
|
Assert.That(manager.BeginRun(), Is.True);
|
|
Assert.That(manager.TryOpenPause(), Is.True);
|
|
Assert.That(manager.IsPaused, Is.True);
|
|
|
|
// The startup request is queued by BeginRun. A paused frame must
|
|
// leave it pending so resuming can open the first color step.
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.False);
|
|
Assert.That(manager.IsSelectionOpen, Is.False);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.False);
|
|
|
|
Assert.That(manager.ClosePause(), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugShownCount, Is.EqualTo(2));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(0).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Green));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(1).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Green));
|
|
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugShownCount, Is.EqualTo(2));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(0).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Red));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(1).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Red));
|
|
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugShownCount, Is.EqualTo(2));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(0).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Blue));
|
|
Assert.That(
|
|
rewards.DebugGetShownArtifact(1).ArtifactColor,
|
|
Is.EqualTo(ArtifactColor.Blue));
|
|
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
|
|
CloseArtifactTutorialModal(manager);
|
|
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.False);
|
|
Assert.That(manager.IsSelectionOpen, Is.False);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.True);
|
|
Assert.That(
|
|
Object.FindAnyObjectByType<ActiveArtifactController>().OwnedArtifactCount,
|
|
Is.EqualTo(3));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator AudioService_LoadsContractClipsAndCapsVoices()
|
|
{
|
|
yield return LoadScene();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
Assert.That(service, Is.Not.Null);
|
|
Assert.That(service.VoiceCount, Is.EqualTo(8));
|
|
string[] ids =
|
|
{
|
|
"ui_move", "ui_confirm", "hit_light", "hit_heavy",
|
|
"player_hurt", "enemy_defeat", "xp_pickup", "level_up",
|
|
"gauge_ready", "run_end", "charge_start", "artifact_dash",
|
|
"artifact_pulse", "artifact_ray", "artifact_cyclone",
|
|
"artifact_thunder", "artifact_arc", "bgm_courtyard", "bgm_boss",
|
|
};
|
|
for (int i = 0; i < ids.Length; i++)
|
|
{
|
|
Assert.That(service.DebugHasClip(ids[i]), Is.True, ids[i]);
|
|
}
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator VolumeSettings_PersistNormalizedValues()
|
|
{
|
|
yield return LoadScene();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
service.SetMasterVolume(0.31f);
|
|
service.SetMusicVolume(0.62f);
|
|
service.SetSfxVolume(0.47f);
|
|
Assert.That(service.MasterVolume, Is.EqualTo(0.31f).Within(0.0001f));
|
|
Assert.That(service.MusicVolume, Is.EqualTo(0.62f).Within(0.0001f));
|
|
Assert.That(service.SfxVolume, Is.EqualTo(0.47f).Within(0.0001f));
|
|
Assert.That(PlayerPrefs.GetFloat("BumpCombat.MasterVolume"), Is.EqualTo(0.31f).Within(0.0001f));
|
|
Assert.That(PlayerPrefs.GetFloat("BumpCombat.MusicVolume"), Is.EqualTo(0.62f).Within(0.0001f));
|
|
Assert.That(PlayerPrefs.GetFloat("BumpCombat.SfxVolume"), Is.EqualTo(0.47f).Within(0.0001f));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator SuccessfulArtifactCast_UsesArtifactClipOnce()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
yield return LoadScene();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
ActiveArtifactController artifacts =
|
|
Object.FindAnyObjectByType<ActiveArtifactController>();
|
|
artifacts.AddMovementCharge(20f);
|
|
Assert.That(artifacts.TryUseCurrent(false), Is.True);
|
|
Assert.That(service.LastPlayedClipId, Is.EqualTo("artifact_dash"));
|
|
Assert.That(artifacts.TryUseCurrent(false), Is.False);
|
|
Assert.That(service.LastPlayedClipId, Is.EqualTo("artifact_dash"));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator BossSpawn_SwitchesToBossMusic()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
yield return LoadScene();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
GameObject midBossButtonObject = GameObject.Find("Debug Spawn MidBoss");
|
|
GameObject finalBossButtonObject = GameObject.Find("Debug Spawn FinalBoss");
|
|
Assert.That(midBossButtonObject, Is.Not.Null);
|
|
Assert.That(finalBossButtonObject, Is.Not.Null);
|
|
Button midBossButton = midBossButtonObject.GetComponent<Button>();
|
|
Button finalBossButton = finalBossButtonObject.GetComponent<Button>();
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(1));
|
|
Assert.That(midBossButton, Is.Not.Null);
|
|
Assert.That(finalBossButton, Is.Not.Null);
|
|
midBossButton.onClick.Invoke();
|
|
Assert.That(service.ActiveMusicClipId, Is.EqualTo("bgm_boss"));
|
|
Assert.That(
|
|
CountPlayingMusicSources(),
|
|
Is.EqualTo(1),
|
|
"Starting boss music must stop ambient music before the fade-in.");
|
|
yield return null;
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(1));
|
|
|
|
finalBossButton.onClick.Invoke();
|
|
Assert.That(service.ActiveMusicClipId, Is.EqualTo("bgm_boss"));
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(1));
|
|
yield return null;
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(1));
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator AudioService_DestroyAndReadd_ClearsLegacyMusicChildren()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = true;
|
|
RunManager.ForceProductionModeForTests = false;
|
|
yield return LoadScene();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
Canvas canvas = service.GetComponentInParent<Canvas>();
|
|
GameObject legacyObject = new("Music A");
|
|
legacyObject.transform.SetParent(service.transform, false);
|
|
AudioSource legacySource = legacyObject.AddComponent<AudioSource>();
|
|
AudioClip legacyClip = AudioClip.Create("Legacy Music", 4410, 1, 44100, false);
|
|
legacySource.clip = legacyClip;
|
|
legacySource.loop = true;
|
|
legacySource.Play();
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(2));
|
|
|
|
Object.Destroy(service);
|
|
yield return null;
|
|
Assert.That(CountPlayingMusicSources(), Is.Zero);
|
|
|
|
canvas.gameObject.AddComponent<BumpCombatAudioService>();
|
|
yield return null;
|
|
Assert.That(CountPlayingMusicSources(), Is.EqualTo(1));
|
|
Object.Destroy(legacyClip);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator MusicDuckAndGameOverFade_StopsMusicSource()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = true;
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
float titleGain = service.CurrentMusicOutputGain;
|
|
Assert.That(titleGain, Is.GreaterThan(0f));
|
|
|
|
Assert.That(manager.BeginRun(), Is.True);
|
|
yield return null;
|
|
ArtifactRewardController rewards =
|
|
Object.FindAnyObjectByType<ArtifactRewardController>();
|
|
Assert.That(rewards.DebugIsSelectionVisible, Is.True);
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
Assert.That(rewards.DebugChooseOption(0), Is.True);
|
|
yield return null;
|
|
|
|
CloseArtifactTutorialModal(manager);
|
|
|
|
Assert.That(service.CurrentMusicOutputGain, Is.GreaterThan(titleGain));
|
|
|
|
service.SetMasterVolume(0f);
|
|
RunManager.ForceProductionModeForTests = false;
|
|
SpawnDirector director = Object.FindAnyObjectByType<SpawnDirector>();
|
|
Assert.That(director.TryDebugSpawnEventEnemy(RunTimedEvent.MidBoss), Is.True);
|
|
yield return null;
|
|
Assert.That(service.CurrentMusicOutputGain, Is.Zero);
|
|
|
|
manager.EndRun();
|
|
yield return new WaitForSecondsRealtime(0.6f);
|
|
Assert.That(service.AreMusicSourcesStopped, Is.True);
|
|
}
|
|
|
|
[UnityTest]
|
|
public IEnumerator SpawnDirector_DoesNotWaveSpawnWhileTitleIsBlocked()
|
|
{
|
|
ArtifactRewardController.GrantCatalogForTests = false;
|
|
RunManager.ForceProductionModeForTests = true;
|
|
yield return LoadScene();
|
|
RunManager manager = Object.FindAnyObjectByType<RunManager>();
|
|
SpawnDirector director = Object.FindAnyObjectByType<SpawnDirector>();
|
|
Assert.That(manager.IsTitleScreen, Is.True);
|
|
typeof(SpawnDirector)
|
|
.GetField("nextWaveTime", BindingFlags.Instance | BindingFlags.NonPublic)
|
|
.SetValue(director, -1f);
|
|
yield return null;
|
|
Assert.That(EnemyController.AliveCount, Is.Zero);
|
|
Assert.That(RunManager.GameplayInputEnabled, Is.False);
|
|
}
|
|
|
|
private IEnumerator LoadScene()
|
|
{
|
|
AsyncOperation load = SceneManager.LoadSceneAsync(SceneName);
|
|
while (!load.isDone)
|
|
{
|
|
yield return null;
|
|
}
|
|
yield return null;
|
|
BumpCombatAudioService service =
|
|
Object.FindAnyObjectByType<BumpCombatAudioService>();
|
|
if (service != null && !capturedVolumes)
|
|
{
|
|
originalMaster = service.MasterVolume;
|
|
originalMusic = service.MusicVolume;
|
|
originalSfx = service.SfxVolume;
|
|
capturedVolumes = true;
|
|
}
|
|
}
|
|
|
|
private static IEnumerator PressAndRelease(Keyboard keyboard, Key key)
|
|
{
|
|
InputSystem.QueueStateEvent(keyboard, new KeyboardState(key));
|
|
yield return null;
|
|
Assert.That(keyboard[key].isPressed, Is.True);
|
|
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
|
|
yield return null;
|
|
Assert.That(keyboard[key].isPressed, Is.False);
|
|
}
|
|
|
|
private static void EnsurePresentationEventSystem(
|
|
bool clearMenuNavigationActions)
|
|
{
|
|
System.Type styleType = typeof(RunHUD).Assembly.GetType(
|
|
"BumpCombat.UI.PresentationUiStyle",
|
|
true);
|
|
MethodInfo ensureMethod = styleType.GetMethod(
|
|
"EnsureEventSystem",
|
|
BindingFlags.Public | BindingFlags.Static);
|
|
Assert.That(ensureMethod, Is.Not.Null);
|
|
ensureMethod.Invoke(null, new object[] { clearMenuNavigationActions });
|
|
}
|
|
|
|
private static void CloseArtifactTutorialModal(RunManager manager)
|
|
{
|
|
RunTutorialController tutorials =
|
|
Object.FindAnyObjectByType<RunTutorialController>();
|
|
Assert.That(tutorials, Is.Not.Null);
|
|
Assert.That(tutorials.DebugIsVisible, Is.True,
|
|
"The first acquired artifact opens its tutorial modal before the next interaction.");
|
|
Assert.That(tutorials.DebugContinue(), Is.True);
|
|
Assert.That(tutorials.DebugIsVisible, Is.False);
|
|
Assert.That(manager.IsSelectionOpen, Is.False);
|
|
}
|
|
|
|
private static int CountActiveSceneEventSystems()
|
|
{
|
|
int count = 0;
|
|
GameObject[] roots = SceneManager.GetActiveScene().GetRootGameObjects();
|
|
foreach (GameObject root in roots)
|
|
{
|
|
count += root.GetComponentsInChildren<EventSystem>(true).Length;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
private static int CountPlayingMusicSources()
|
|
{
|
|
AudioSource[] sources = Object.FindObjectsByType<AudioSource>(
|
|
FindObjectsInactive.Include,
|
|
FindObjectsSortMode.None);
|
|
int playingCount = 0;
|
|
for (int i = 0; i < sources.Length; i++)
|
|
{
|
|
if (sources[i].name.StartsWith("Music", System.StringComparison.Ordinal)
|
|
&& sources[i].isPlaying)
|
|
{
|
|
playingCount++;
|
|
}
|
|
}
|
|
return playingCount;
|
|
}
|
|
|
|
private Keyboard BeginVirtualKeyboardInput()
|
|
{
|
|
Assert.That(virtualKeyboardInputActive, Is.False);
|
|
previousBackgroundBehavior = InputSystem.settings.backgroundBehavior;
|
|
previousEditorInputBehavior = InputSystem.settings.editorInputBehaviorInPlayMode;
|
|
previousRunInBackground = Application.runInBackground;
|
|
InputSystem.settings.backgroundBehavior =
|
|
InputSettings.BackgroundBehavior.IgnoreFocus;
|
|
InputSystem.settings.editorInputBehaviorInPlayMode =
|
|
InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView;
|
|
Application.runInBackground = true;
|
|
virtualKeyboard = InputSystem.AddDevice<Keyboard>();
|
|
virtualKeyboard.MakeCurrent();
|
|
Assert.That(Keyboard.current, Is.SameAs(virtualKeyboard));
|
|
virtualKeyboardInputActive = true;
|
|
return virtualKeyboard;
|
|
}
|
|
|
|
private void EndVirtualKeyboardInput()
|
|
{
|
|
if (!virtualKeyboardInputActive)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (virtualKeyboard != null && virtualKeyboard.added)
|
|
{
|
|
InputSystem.RemoveDevice(virtualKeyboard);
|
|
}
|
|
|
|
InputSystem.settings.backgroundBehavior = previousBackgroundBehavior;
|
|
InputSystem.settings.editorInputBehaviorInPlayMode = previousEditorInputBehavior;
|
|
Application.runInBackground = previousRunInBackground;
|
|
virtualKeyboard = null;
|
|
virtualKeyboardInputActive = false;
|
|
}
|
|
|
|
private sealed class SelectionOwner : MonoBehaviour
|
|
{
|
|
}
|
|
}
|
|
}
|