@@ -2,59 +2,84 @@ namespace UnityJS.Integration.CharacterController.EditModeTests
22{
33 using System . Collections ;
44 using NUnit . Framework ;
5+ using Unity . Collections ;
56 using Unity . Entities ;
67 using Unity . Mathematics ;
8+ using Unity . Transforms ;
79 using UnityEngine ;
810 using UnityEngine . TestTools ;
11+ using UnityJS . Entities . Components ;
12+ using UnityJS . Entities . Core ;
913 using UnityJS . Integrations . Editor ;
1014 using UnityJS . Runtime ;
1115
1216 /// <summary>
13- /// E2E tests verifying CharacterController integration: fixture script reads
14- /// _testInput and writes ECSCharacterControl, stamina, and jump state.
15- /// No scene loading — entities created programmatically .
17+ /// E2E tests verifying CharacterController integration.
18+ /// The fixture character_input system script reads _testInput and writes
19+ /// ECSCharacterControl/Stats on matching entities.
1620 /// </summary>
1721 [ TestFixture ]
1822 public class JsCharacterControllerE2ETests
1923 {
24+ /// <summary>
25+ /// Creates an entity with character components and a JsEntityId.
26+ /// No script attached — the system script finds it via ecs.query.
27+ /// </summary>
28+ static Entity CreateCharacterEntity ( EntityManager em )
29+ {
30+ var types = new NativeList < ComponentType > ( 6 , Allocator . Temp ) ;
31+ types . Add ( ComponentType . ReadWrite < JsEntityId > ( ) ) ;
32+ types . Add ( ComponentType . ReadWrite < LocalTransform > ( ) ) ;
33+ types . Add ( ComponentType . ReadWrite < ECSCharacterControl > ( ) ) ;
34+ types . Add ( ComponentType . ReadWrite < ECSCharacterStats > ( ) ) ;
35+ types . Add ( ComponentType . ReadWrite < ECSCharacterState > ( ) ) ;
36+ types . Add ( ComponentType . ReadWrite < ECSCharacterFixedInput > ( ) ) ;
37+ var entity = em . CreateEntity ( types . AsArray ( ) ) ;
38+ types . Dispose ( ) ;
39+
40+ var entityId = JsEntityRegistry . AllocateId ( ) ;
41+ JsEntityRegistry . RegisterImmediate ( entity , entityId , em ) ;
42+ em . SetComponentData ( entity , new JsEntityId { value = entityId } ) ;
43+ em . SetComponentData ( entity , LocalTransform . FromPosition ( float3 . zero ) ) ;
44+ em . SetComponentData ( entity , ECSCharacterStats . Default ( ) ) ;
45+
46+ return entity ;
47+ }
48+
2049 [ UnityTest ]
2150 public IEnumerator CharacterInput_MoveVector_WrittenFromTestInput ( )
2251 {
52+ // Register fixture search path BEFORE play mode so JsSystemRunner discovers the system
2353 var fixturesPath = IntegrationTestHarness . GetFixturesPath (
2454 "Integrations/CharacterController/Fixtures~"
2555 ) ;
2656 var compiledPath = IntegrationTestHarness . CompileFixtures ( fixturesPath ) ;
2757
2858 yield return new EnterPlayMode ( ) ;
2959
30- using var searchPath = IntegrationTestHarness . UseSearchPath ( compiledPath ) ;
31- var em = World . DefaultGameObjectInjectionWorld . EntityManager ;
32- var entity = IntegrationTestHarness . CreateScriptedEntity (
33- em ,
34- "systems/character_input" ,
35- ComponentType . ReadWrite < ECSCharacterControl > ( ) ,
36- ComponentType . ReadWrite < ECSCharacterStats > ( ) ,
37- ComponentType . ReadWrite < ECSCharacterState > ( ) ,
38- ComponentType . ReadWrite < ECSCharacterFixedInput > ( )
39- ) ;
40- em . SetComponentData ( entity , ECSCharacterStats . Default ( ) ) ;
60+ // Register search path so system discovery finds systems/character_input
61+ using var searchPath = IntegrationTestHarness . UseIsolatedSearchPath ( compiledPath ) ;
62+
63+ var world = World . DefaultGameObjectInjectionWorld ;
64+ var em = world . EntityManager ;
65+
66+ // Create target entity with character components (system finds it via query)
67+ var entity = CreateCharacterEntity ( em ) ;
4168
42- // Wait for fulfillment + query pipeline:
43- // Frame 1: fulfillment processes request
44- // Frame 2: script ticks, query registers as pending
45- // Frame 3: JsSystemRunner flushes pending query, precomputes
46- // Frame 4: script ticks with query results
47- // Frame 5+: stable
69+ // Wait for system discovery + query pipeline:
70+ // Frame 1: JsSystemRunner.EnsureVmReady discovers and loads systems/character_input
71+ // Frame 2: system's onUpdate runs, query registers as pending
72+ // Frame 3: PrewarmComponentQueries creates EntityQuery, precomputes
73+ // Frame 4+: system's query returns the entity, writes moveVector
4874 for ( var frame = 0 ; frame < 10 ; frame ++ )
4975 yield return null ;
5076
5177 IntegrationTestHarness . AssertNoJsErrors ( "character input setup" ) ;
52- IntegrationTestHarness . AssertEntitiesFulfilled ( em , entity ) ;
5378
5479 // Inject movement input
5580 IntegrationTestHarness . SetTestInput ( moveX : 1f , moveZ : 0.5f ) ;
5681
57- // Let the script tick with the input applied
82+ // Let the system tick with input applied
5883 for ( var frame = 0 ; frame < 10 ; frame ++ )
5984 yield return null ;
6085
@@ -79,30 +104,21 @@ public IEnumerator CharacterInput_Sprint_DrainsStamina()
79104
80105 yield return new EnterPlayMode ( ) ;
81106
82- using var searchPath = IntegrationTestHarness . UseSearchPath ( compiledPath ) ;
83- var em = World . DefaultGameObjectInjectionWorld . EntityManager ;
84- var entity = IntegrationTestHarness . CreateScriptedEntity (
85- em ,
86- "systems/character_input" ,
87- ComponentType . ReadWrite < ECSCharacterControl > ( ) ,
88- ComponentType . ReadWrite < ECSCharacterStats > ( ) ,
89- ComponentType . ReadWrite < ECSCharacterState > ( ) ,
90- ComponentType . ReadWrite < ECSCharacterFixedInput > ( )
91- ) ;
92- em . SetComponentData ( entity , ECSCharacterStats . Default ( ) ) ;
107+ using var searchPath = IntegrationTestHarness . UseIsolatedSearchPath ( compiledPath ) ;
93108
94- // Wait for fulfillment + query pipeline
109+ var world = World . DefaultGameObjectInjectionWorld ;
110+ var em = world . EntityManager ;
111+ var entity = CreateCharacterEntity ( em ) ;
112+
113+ // Wait for system discovery + query pipeline
95114 for ( var frame = 0 ; frame < 10 ; frame ++ )
96115 yield return null ;
97116
98- IntegrationTestHarness . AssertEntitiesFulfilled ( em , entity ) ;
99-
100117 var initialStamina = em . GetComponentData < ECSCharacterStats > ( entity ) . stamina ;
101118
102119 // Inject sprint input
103120 IntegrationTestHarness . SetTestInput ( moveX : 1f , sprint : true ) ;
104121
105- // Let the script tick with sprint active
106122 for ( var frame = 0 ; frame < 10 ; frame ++ )
107123 yield return null ;
108124
@@ -162,23 +178,5 @@ public IEnumerator MultiplePlayModeCycles_CharacterIntegration_Stable()
162178 yield return new ExitPlayMode ( ) ;
163179 }
164180 }
165- static unsafe string EvalString ( JsRuntimeManager vm , string code )
166- {
167- var sourceBytes = System . Text . Encoding . UTF8 . GetBytes ( code + '\0 ' ) ;
168- var fileBytes = System . Text . Encoding . UTF8 . GetBytes ( "<diag>\0 " ) ;
169- fixed ( byte * pSrc = sourceBytes , pFile = fileBytes )
170- {
171- var result = UnityJS . QJS . QJS . JS_Eval ( vm . Context , pSrc , sourceBytes . Length - 1 , pFile ,
172- UnityJS . QJS . QJS . JS_EVAL_TYPE_GLOBAL ) ;
173- if ( UnityJS . QJS . QJS . IsException ( result ) )
174- {
175- UnityJS . QJS . QJS . JS_FreeValue ( vm . Context , UnityJS . QJS . QJS . JS_GetException ( vm . Context ) ) ;
176- return "<exception>" ;
177- }
178- var str = UnityJS . QJS . QJS . ToManagedString ( vm . Context , result ) ;
179- UnityJS . QJS . QJS . JS_FreeValue ( vm . Context , result ) ;
180- return str ?? "<null>" ;
181- }
182- }
183181 }
184182}
0 commit comments