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 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(); Image selectedImage = imageObject.AddComponent(); 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 images = (Dictionary)pickerImages.GetValue(module); images[8] = selectedImage; confirm.Invoke(module, null); FieldInfo spriteCache = typeof(PlayerProfileModule).GetField("avatarSpriteCache", BindingFlags.Instance | BindingFlags.NonPublic); Dictionary cache = (Dictionary)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(); Image avatarImage = avatarObject.AddComponent(); 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 cache = (Dictionary)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(); 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(); RectTransform previewFrameRect = previewFrame.GetComponent(); RectTransform previewAvatarRect = previewAvatar.GetComponent(); previewPanel.transform.SetParent(pickerRoot.transform, false); previewFrame.transform.SetParent(previewPanel.transform, false); previewAvatar.transform.SetParent(previewFrame.transform, false); previewAvatar.AddComponent(); 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(); profileButton.transform.SetParent(hudRoot.transform, false); avatarFrame.transform.SetParent(profileButton.transform, false); avatarIcon.transform.SetParent(avatarFrame.transform, false); avatarIcon.AddComponent(); 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(); identityPanel.transform.SetParent(panelRoot.transform, false); avatarFrame.transform.SetParent(identityPanel.transform, false); avatarIcon.transform.SetParent(avatarFrame.transform, false); avatarIcon.AddComponent(); 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 }