Files

358 lines
16 KiB
C#

using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using XGame;
#if UNITY_EDITOR
using XGame.EditorTools;
#endif
public sealed class PlayerProfileModuleTests
{
[TearDown]
public void TearDown()
{
PlayerProfileStore.ResetToDefault();
}
[Test]
public void DefaultProfile_HasPlayableCombatStats()
{
PlayerProfileData profile = PlayerProfileData.CreateDefault();
Assert.That(profile.Nickname, Is.EqualTo("Player"));
Assert.That(profile.Uid, Is.EqualTo("0"));
Assert.That(profile.Lv, Is.EqualTo(1));
Assert.That(profile.HpMax, Is.GreaterThan(0));
Assert.That(profile.Hp, Is.EqualTo(profile.HpMax));
Assert.That(profile.MpMax, Is.GreaterThan(0));
Assert.That(profile.Mp, Is.EqualTo(profile.MpMax));
Assert.That(profile.Ad, Is.GreaterThan(0));
Assert.That(profile.MoveSpeed, Is.GreaterThan(0f));
}
[Test]
public void Replace_SanitizesInvalidValuesAndRaisesChanged()
{
int changed = 0;
Action<PlayerProfileData> handler = _ => changed++;
PlayerProfileStore.Changed += handler;
try
{
PlayerProfileStore.Replace(new PlayerProfileData
{
Nickname = "Alice",
Uid = "42",
AvatarId = -1,
RoleType = PlayerRoleType.Mage,
Lv = -5,
Exp = -20,
Hp = 999,
HpMax = 100,
Mp = -3,
MpMax = 50,
AtkSpeed = -1f,
MoveSpeed = -2f
});
}
finally
{
PlayerProfileStore.Changed -= handler;
}
PlayerProfileData current = PlayerProfileStore.Current;
Assert.That(changed, Is.EqualTo(1));
Assert.That(current.AvatarId, Is.EqualTo(0));
Assert.That(current.Lv, Is.EqualTo(1));
Assert.That(current.Exp, Is.EqualTo(0));
Assert.That(current.Hp, Is.EqualTo(100));
Assert.That(current.Mp, Is.EqualTo(0));
Assert.That(current.AtkSpeed, Is.GreaterThan(0f));
Assert.That(current.MoveSpeed, Is.GreaterThan(0f));
}
[Test]
public void Experience01_ClampsAgainstLevelRequirement()
{
PlayerProfileData profile = PlayerProfileData.CreateDefault();
profile.Lv = 2;
profile.Exp = 150;
Assert.That(profile.Experience01, Is.EqualTo(0.75f).Within(0.001f));
profile.Exp = 999;
Assert.That(profile.Experience01, Is.EqualTo(1f));
}
[Test]
public void AvatarCatalog_NormalizesLegacyAndInvalidIdsToDefault()
{
Assert.That(PlayerAvatarCatalog.NormalizeId(0), Is.EqualTo(PlayerAvatarCatalog.DefaultAvatarId));
Assert.That(PlayerAvatarCatalog.NormalizeId(-3), Is.EqualTo(PlayerAvatarCatalog.DefaultAvatarId));
Assert.That(PlayerAvatarCatalog.NormalizeId(PlayerAvatarCatalog.Count + 1), Is.EqualTo(PlayerAvatarCatalog.DefaultAvatarId));
}
[Test]
public void AvatarCatalog_MapsIdsToTwoDigitGamerPicPaths()
{
Assert.That(PlayerAvatarCatalog.GetAssetPath(1), Is.EqualTo("Assets/Game/Art/UI/Texture/Icon/GamerPic/GamerPic_01.png"));
Assert.That(PlayerAvatarCatalog.GetAssetPath(9), Is.EqualTo("Assets/Game/Art/UI/Texture/Icon/GamerPic/GamerPic_09.png"));
Assert.That(PlayerAvatarCatalog.GetAssetPath(54), Is.EqualTo("Assets/Game/Art/UI/Texture/Icon/GamerPic/GamerPic_54.png"));
Assert.That(PlayerAvatarCatalog.GetAssetPath(0), Is.EqualTo(PlayerAvatarCatalog.GetAssetPath(PlayerAvatarCatalog.DefaultAvatarId)));
}
[Test]
public void AvatarPickerJson_DeclaresAllAvatarButtons()
{
string projectRoot = System.IO.Directory.GetParent(Application.dataPath).Parent.FullName;
string jsonPath = System.IO.Path.Combine(projectRoot, "Doc/UIPrefabCreater/UI_PlayerAvatarPicker.json");
string json = System.IO.File.ReadAllText(jsonPath);
for (int avatarId = 1; avatarId <= PlayerAvatarCatalog.Count; avatarId++)
{
string suffix = avatarId.ToString("00", System.Globalization.CultureInfo.InvariantCulture);
StringAssert.Contains("Btn_Avatar_" + suffix, json);
StringAssert.Contains("GamerPic_" + suffix + ".png", json);
}
}
[Test]
public void ConfirmedAvatarUpdate_ChangesOnlyAvatarId()
{
PlayerProfileData original = PlayerProfileData.CreateDefault();
original.Nickname = "Alice";
original.Gold = 123;
original.AvatarId = 1;
PlayerProfileStore.Replace(original);
PlayerProfileStore.Update(profile => profile.AvatarId = 17);
PlayerProfileData current = PlayerProfileStore.Current;
Assert.That(current.AvatarId, Is.EqualTo(17));
Assert.That(current.Nickname, Is.EqualTo("Alice"));
Assert.That(current.Gold, Is.EqualTo(123));
}
[Test]
public void ConfirmAvatarPicker_CachesSelectedPickerSpriteForProfileRefresh()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject imageObject = new GameObject("SelectedAvatar");
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
Image selectedImage = imageObject.AddComponent<Image>();
Texture2D texture = new Texture2D(1, 1);
Sprite selectedSprite = Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f));
selectedImage.sprite = selectedSprite;
try
{
FieldInfo pendingAvatarId = typeof(PlayerProfileModule).GetField("pendingAvatarId", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo pickerImages = typeof(PlayerProfileModule).GetField("avatarPickerAvatarImages", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo confirm = typeof(PlayerProfileModule).GetMethod("ConfirmAvatarPicker", BindingFlags.Instance | BindingFlags.NonPublic);
pendingAvatarId.SetValue(module, 8);
Dictionary<int, Image> images = (Dictionary<int, Image>)pickerImages.GetValue(module);
images[8] = selectedImage;
confirm.Invoke(module, null);
FieldInfo spriteCache = typeof(PlayerProfileModule).GetField("avatarSpriteCache", BindingFlags.Instance | BindingFlags.NonPublic);
Dictionary<int, Sprite> cache = (Dictionary<int, Sprite>)spriteCache.GetValue(module);
Assert.That(PlayerProfileStore.Current.AvatarId, Is.EqualTo(8));
Assert.That(cache[8], Is.SameAs(selectedSprite));
}
finally
{
UnityEngine.Object.DestroyImmediate(selectedSprite);
UnityEngine.Object.DestroyImmediate(texture);
UnityEngine.Object.DestroyImmediate(imageObject);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
[Test]
public void CacheAvatarPickerControls_CachesAvatarSpritesFromPickerImages()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject pickerRoot = new GameObject("AvatarPicker");
GameObject avatarObject = new GameObject("Img_Avatar_08");
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
Image avatarImage = avatarObject.AddComponent<Image>();
Texture2D texture = new Texture2D(1, 1);
Sprite avatarSprite = Sprite.Create(texture, new Rect(0, 0, 1, 1), new Vector2(0.5f, 0.5f));
avatarImage.sprite = avatarSprite;
avatarObject.transform.SetParent(pickerRoot.transform, false);
try
{
FieldInfo pickerView = typeof(PlayerProfileModule).GetField("avatarPickerView", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo cacheControls = typeof(PlayerProfileModule).GetMethod("CacheAvatarPickerControls", BindingFlags.Instance | BindingFlags.NonPublic);
FieldInfo spriteCache = typeof(PlayerProfileModule).GetField("avatarSpriteCache", BindingFlags.Instance | BindingFlags.NonPublic);
pickerView.SetValue(module, pickerRoot);
cacheControls.Invoke(module, null);
Dictionary<int, Sprite> cache = (Dictionary<int, Sprite>)spriteCache.GetValue(module);
Assert.That(cache[8], Is.SameAs(avatarSprite));
}
finally
{
UnityEngine.Object.DestroyImmediate(avatarSprite);
UnityEngine.Object.DestroyImmediate(texture);
UnityEngine.Object.DestroyImmediate(pickerRoot);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
[Test]
public void CacheAvatarPickerControls_MovesSelectedStateAboveAvatarFrame()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject pickerRoot = new GameObject("AvatarPicker");
GameObject slot = new GameObject("AvatarSlot_08");
GameObject button = new GameObject("Btn_Avatar_08");
GameObject avatar = new GameObject("Img_Avatar_08");
GameObject selected = new GameObject("Img_Selected_08");
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
slot.transform.SetParent(pickerRoot.transform, false);
button.transform.SetParent(slot.transform, false);
avatar.transform.SetParent(slot.transform, false);
selected.transform.SetParent(slot.transform, false);
try
{
FieldInfo pickerView = typeof(PlayerProfileModule).GetField("avatarPickerView", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo cacheControls = typeof(PlayerProfileModule).GetMethod("CacheAvatarPickerControls", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.That(selected.transform.GetSiblingIndex(), Is.GreaterThan(button.transform.GetSiblingIndex()));
pickerView.SetValue(module, pickerRoot);
cacheControls.Invoke(module, null);
Assert.That(selected.transform.GetSiblingIndex(), Is.GreaterThan(button.transform.GetSiblingIndex()));
Assert.That(selected.transform.GetSiblingIndex(), Is.GreaterThan(avatar.transform.GetSiblingIndex()));
}
finally
{
UnityEngine.Object.DestroyImmediate(pickerRoot);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
[Test]
public void CacheAvatarPickerControls_MovesPreviewAvatarBehindPreviewFrame()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject pickerRoot = new GameObject("AvatarPicker", typeof(RectTransform));
GameObject previewPanel = new GameObject("Panel_Preview", typeof(RectTransform));
GameObject previewFrame = new GameObject("Img_PreviewFrame", typeof(RectTransform));
GameObject previewAvatar = new GameObject("Img_PreviewAvatar", typeof(RectTransform));
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
RectTransform previewFrameRect = previewFrame.GetComponent<RectTransform>();
RectTransform previewAvatarRect = previewAvatar.GetComponent<RectTransform>();
previewPanel.transform.SetParent(pickerRoot.transform, false);
previewFrame.transform.SetParent(previewPanel.transform, false);
previewAvatar.transform.SetParent(previewFrame.transform, false);
previewAvatar.AddComponent<Image>();
previewFrameRect.sizeDelta = new Vector2(112f, 112f);
previewAvatarRect.sizeDelta = new Vector2(92f, 92f);
try
{
FieldInfo pickerView = typeof(PlayerProfileModule).GetField("avatarPickerView", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo cacheControls = typeof(PlayerProfileModule).GetMethod("CacheAvatarPickerControls", BindingFlags.Instance | BindingFlags.NonPublic);
pickerView.SetValue(module, pickerRoot);
cacheControls.Invoke(module, null);
Assert.That(previewAvatar.transform.parent, Is.EqualTo(previewPanel.transform));
Assert.That(previewAvatar.transform.GetSiblingIndex(), Is.LessThan(previewFrame.transform.GetSiblingIndex()));
Assert.That(previewAvatarRect.sizeDelta.x, Is.LessThanOrEqualTo(65f));
Assert.That(previewAvatarRect.sizeDelta.y, Is.LessThanOrEqualTo(65f));
}
finally
{
UnityEngine.Object.DestroyImmediate(pickerRoot);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
[Test]
public void CacheHudControls_MovesAvatarIconBehindAvatarFrame()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject hudRoot = new GameObject("HudRoot");
GameObject profileButton = new GameObject("Btn_Profile");
GameObject avatarFrame = new GameObject("Img_AvatarFrame");
GameObject avatarIcon = new GameObject("Img_AvatarIcon");
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
profileButton.transform.SetParent(hudRoot.transform, false);
avatarFrame.transform.SetParent(profileButton.transform, false);
avatarIcon.transform.SetParent(avatarFrame.transform, false);
avatarIcon.AddComponent<Image>();
try
{
FieldInfo hudView = typeof(PlayerProfileModule).GetField("hudView", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo cacheControls = typeof(PlayerProfileModule).GetMethod("CacheHudControls", BindingFlags.Instance | BindingFlags.NonPublic);
hudView.SetValue(module, hudRoot);
cacheControls.Invoke(module, null);
Assert.That(avatarIcon.transform.parent, Is.EqualTo(profileButton.transform));
Assert.That(avatarIcon.transform.GetSiblingIndex(), Is.LessThan(avatarFrame.transform.GetSiblingIndex()));
}
finally
{
UnityEngine.Object.DestroyImmediate(hudRoot);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
[Test]
public void ArrangeAvatarIconBehindFrame_MovesDetailAvatarIconBehindAvatarFrame()
{
GameObject moduleObject = new GameObject("PlayerProfileModuleTest");
GameObject panelRoot = new GameObject("PanelRoot");
GameObject identityPanel = new GameObject("Panel_Identity");
GameObject avatarFrame = new GameObject("Img_AvatarFrame");
GameObject avatarIcon = new GameObject("Img_AvatarIcon");
PlayerProfileModule module = moduleObject.AddComponent<PlayerProfileModule>();
identityPanel.transform.SetParent(panelRoot.transform, false);
avatarFrame.transform.SetParent(identityPanel.transform, false);
avatarIcon.transform.SetParent(avatarFrame.transform, false);
avatarIcon.AddComponent<Image>();
try
{
MethodInfo arrangeAvatarIcon = typeof(PlayerProfileModule).GetMethod("ArrangeAvatarIconBehindFrame", BindingFlags.Instance | BindingFlags.NonPublic);
arrangeAvatarIcon.Invoke(module, new object[] { panelRoot });
Assert.That(avatarIcon.transform.parent, Is.EqualTo(identityPanel.transform));
Assert.That(avatarIcon.transform.GetSiblingIndex(), Is.LessThan(avatarFrame.transform.GetSiblingIndex()));
}
finally
{
UnityEngine.Object.DestroyImmediate(panelRoot);
UnityEngine.Object.DestroyImmediate(moduleObject);
}
}
#if UNITY_EDITOR
[Test]
public void PlayerProfileJsonFiles_PassPrefabBuilderValidation()
{
Assert.That(JsonUIPrefabBuilder.Validate("../Doc/UIPrefabCreater/UI_PlayerProfileHud.json").Ok, Is.True);
Assert.That(JsonUIPrefabBuilder.Validate("../Doc/UIPrefabCreater/UI_PlayerProfilePanel.json").Ok, Is.True);
Assert.That(JsonUIPrefabBuilder.Validate("../Doc/UIPrefabCreater/UI_PlayerAvatarPicker.json").Ok, Is.True);
}
#endif
}