EerieVillage/Assets/Tests/Editor/CloneSkillTests.cs

124 lines
5.8 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 NUnit.Framework;
using UnityEngine;
using EerieVillage.Skills;
using EerieVillage.Skills.Effectors;
namespace EerieVillage.Skills.Tests.Editor
{
/// <summary>
/// A10 분신 스킬 EditMode 테스트 (BT12-Dev-Clone · 2026-05-15 δ 단계).
///
/// PD 명세 5항목 + PD 결정 4건 정합 검증.
/// PlayMode 의존 (Time.unscaledTime·Update) 영역은 본 EditMode에서 제외 — PD Play 검증 영역.
/// </summary>
public class CloneSkillTests
{
// ─────────────────────────────────────────────────────────────
// 1. PD 결정 상수 검증 (BaseCooldown 25·MinionLifetime 12·딜레이 0.5·offset 1·alpha 0.5)
// ─────────────────────────────────────────────────────────────
[Test]
public void T01_CloneInstance_PdConstantsMatch()
{
Assert.AreEqual(12f, CloneInstance.LIFETIME_SECONDS,
"PD 결정 (2026-05-15) — 분신 lifetime 12초.");
Assert.AreEqual(0.5f, CloneInstance.FIRE_DELAY_SECONDS,
"PD 명세 — 공격 시작 딜레이 0.5초.");
Assert.AreEqual(1f, CloneInstance.SPAWN_OFFSET_X,
"PD 명세 — facing 반대 방향 1유닛.");
Assert.AreEqual(0.5f, CloneInstance.SPRITE_ALPHA,
"PD 명세 — 반투명 alpha 0.5.");
}
[Test]
public void T02_PlayerSkillInventory_CloneDamageMultiplierIsHalf()
{
// PD 명세 — 분신 공격력 50% 반감 (= 0.5f multiplier)
Assert.AreEqual(0.5f, PlayerSkillInventory.CLONE_DAMAGE_MULTIPLIER,
"PD 명세 4번 — 분신 damage 50% 반감.");
}
// ─────────────────────────────────────────────────────────────
// 2. helper 메서드 동작 검증 (γ 단계 anchor·facing 분기)
// ─────────────────────────────────────────────────────────────
[Test]
public void T03_GetSpawnAnchor_ReturnsPlayerPositionWhenNotCloneFire()
{
var go = new GameObject("PlayerStub");
go.AddComponent<Platformer.Mechanics.Health>();
var inv = go.AddComponent<PlayerSkillInventory>();
go.transform.position = new Vector3(5f, 3f, 0f);
inv.IsCloneFireActive = false;
Vector2 anchor = inv.GetSpawnAnchor();
Assert.AreEqual(5f, anchor.x, "기본 영역 — Player x 위치 반환.");
Assert.AreEqual(3f, anchor.y, "기본 영역 — Player y 위치 반환.");
Object.DestroyImmediate(go);
}
[Test]
public void T04_GetSpawnAnchor_ReturnsCloneOriginWhenCloneFireActive()
{
var go = new GameObject("PlayerStub");
go.AddComponent<Platformer.Mechanics.Health>();
var inv = go.AddComponent<PlayerSkillInventory>();
go.transform.position = new Vector3(5f, 3f, 0f);
inv.IsCloneFireActive = true;
inv.CloneFireOrigin = new Vector2(-2f, 7f);
Vector2 anchor = inv.GetSpawnAnchor();
Assert.AreEqual(-2f, anchor.x, "분신 발동 영역 — 분신 위치 반환.");
Assert.AreEqual(7f, anchor.y, "분신 발동 영역 — 분신 위치 반환.");
Object.DestroyImmediate(go);
}
[Test]
public void T05_GetSpawnFacing_ReturnsCloneFacingWhenCloneFireActive()
{
var go = new GameObject("PlayerStub");
go.AddComponent<Platformer.Mechanics.Health>();
var inv = go.AddComponent<PlayerSkillInventory>();
inv.IsCloneFireActive = true;
inv.CloneFireFacingX = -1f; // 분신 왼쪽 facing 고정
Vector2 facing = inv.GetSpawnFacing(null);
Assert.AreEqual(-1f, facing.x, "분신 facing 고정 (x sign).");
Assert.AreEqual(0f, facing.y, "분신 facing y = 0.");
Object.DestroyImmediate(go);
}
// ─────────────────────────────────────────────────────────────
// 3. CloneInstance Singleton·재귀 skip 검증
// ─────────────────────────────────────────────────────────────
[Test]
public void T06_CloneInstance_CurrentIsNullInitially()
{
// 정적 Singleton 초기 영역 null (또는 이전 테스트 잔존 정리)
if (CloneInstance.Current != null)
Object.DestroyImmediate(CloneInstance.Current.gameObject);
Assert.IsNull(CloneInstance.Current, "분신 미spawn 영역 Current = null.");
}
[Test]
public void T07_EnqueuePlayerFire_IgnoresA10RecursionPrep()
{
// 분신이 A10 발동 hook 영역 무한 재귀 방지 영역 — null·CardId == "A10" 즉시 return
// 본 테스트는 hook 영역 진입 자체가 null safe 영역 확증.
var go = new GameObject("CloneStub");
var instance = go.AddComponent<CloneInstance>();
// null runtime 영역 즉시 return — Exception X
Assert.DoesNotThrow(() => instance.EnqueuePlayerFire(null),
"null runtime hook 영역 NullReferenceException X.");
Object.DestroyImmediate(go);
}
}
}