using System; using System.Collections; using System.Linq; using System.Reflection; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; namespace AibisDream.Huoshan.Tests.PlayMode { public sealed class ExpressionParticlePlayModeTests { private const string RuntimeAssemblyName = "Assembly-CSharp"; [Test] public void WordMode_TokenizesWholeWordsAtRuntime() { Type tokenizerType = RequireRuntimeType( "AibisDream.MiniGame.Language.ExpressionTextTokenizer"); Type modeType = RequireRuntimeType( "AibisDream.MiniGame.Language.ExpressionParticleUnitMode"); object wordMode = Enum.Parse(modeType, "Word"); MethodInfo tokenize = tokenizerType.GetMethod( "TokenizeText", BindingFlags.Public | BindingFlags.Static); Assert.That(tokenize, Is.Not.Null); IEnumerable result = (IEnumerable)tokenize.Invoke( null, new[] { "I am not self-doubt", wordMode }); string[] units = result.Cast() .Select(value => value.ToString()) .ToArray(); Assert.That( units, Is.EqualTo(new[] { "I", "am", "not", "self-doubt" })); } [UnityTest] public IEnumerator WordParticle_BoundsIncludesBothPointerEdges() { GameObject gameObject = new GameObject("WordParticlePlayModeTest"); try { Component particle = gameObject.AddComponent( RequireRuntimeType( "AibisDream.MiniGame.Language.CandidateParticle")); Invoke(particle, "ForceSetUnitText", "self-doubt"); yield return null; Bounds bounds = (Bounds)Invoke(particle, "GetVisualWorldBounds"); Assert.That(bounds.size.x, Is.GreaterThan(0f)); float leftDistance = (float)Invoke( particle, "DistanceToVisualBounds", new Vector2(bounds.min.x, bounds.center.y)); float rightDistance = (float)Invoke( particle, "DistanceToVisualBounds", new Vector2(bounds.max.x, bounds.center.y)); Assert.That(leftDistance, Is.EqualTo(0f).Within(0.0001f)); Assert.That(rightDistance, Is.EqualTo(0f).Within(0.0001f)); } finally { UnityEngine.Object.Destroy(gameObject); } yield return null; } [UnityTest] public IEnumerator LongWords_ConnectThroughVisualEdgeDistance() { GameObject firstObject = new GameObject("FirstWordParticle"); GameObject secondObject = new GameObject("SecondWordParticle"); try { Type particleType = RequireRuntimeType( "AibisDream.MiniGame.Language.CandidateParticle"); Component first = firstObject.AddComponent(particleType); Component second = secondObject.AddComponent(particleType); Invoke(first, "ForceSetUnitText", "internationalization"); Invoke(second, "ForceSetUnitText", "characteristically"); yield return null; Bounds firstBounds = (Bounds)Invoke(first, "GetVisualWorldBounds"); Bounds secondBounds = (Bounds)Invoke(second, "GetVisualWorldBounds"); secondObject.transform.position = new Vector3( firstBounds.extents.x + secondBounds.extents.x + 0.1f, 0f, 0f); Type geometryType = RequireRuntimeType( "AibisDream.MiniGame.Language.ExpressionParticleGeometry"); MethodInfo connection = geometryType.GetMethod( "TryGetConnection", BindingFlags.Public | BindingFlags.Static); object[] arguments = { first, second, 0.01f, 0.18f, Vector3.zero, Vector3.zero, 0f }; Assert.That(connection, Is.Not.Null); bool connected = (bool)connection.Invoke(null, arguments); Assert.That(connected, Is.True); Assert.That((float)arguments[6], Is.GreaterThan(0f)); Assert.That( Vector3.Distance( firstObject.transform.position, secondObject.transform.position), Is.GreaterThan(0.01f)); } finally { UnityEngine.Object.Destroy(firstObject); UnityEngine.Object.Destroy(secondObject); } yield return null; } private static Type RequireRuntimeType(string fullName) { Type type = Type.GetType($"{fullName}, {RuntimeAssemblyName}"); Assert.That(type, Is.Not.Null, fullName); return type; } private static object Invoke( object target, string methodName, params object[] arguments) { MethodInfo method = target.GetType().GetMethod( methodName, BindingFlags.Public | BindingFlags.Instance); Assert.That(method, Is.Not.Null, methodName); return method.Invoke(target, arguments); } } }