124 lines
5.8 KiB
C#
124 lines
5.8 KiB
C#
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|