1+ using System ;
2+ using System . Collections ;
3+ using NUnit . Framework ;
4+ using UnityScreenNavigator . Runtime . Foundation ;
5+
6+ namespace UnityScreenNavigator . Tests . PlayMode . Foundation
7+ {
8+ internal sealed class CoroutineHandleTest
9+ {
10+ [ Test ]
11+ public void Constructor_初期化後_StatusがNotNullかつ未完了であること ( )
12+ {
13+ // Arrange
14+ var simpleRoutine = SimpleRoutine ( ) ;
15+
16+ // Act
17+ var handle = new CoroutineHandle ( simpleRoutine ) ;
18+
19+ // Assert
20+ Assert . That ( handle . Status , Is . Not . Null ) ;
21+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
22+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
23+ Assert . That ( handle . Status . Exception , Is . Null ) ;
24+ }
25+
26+ [ Test ]
27+ public void Step_コルーチンが初回MoveNextで完了する場合_Completedを返しStatusが完了になること ( )
28+ {
29+ // Arrange
30+ // MoveNext() が false を返す IEnumerator (ステップがないため)
31+ var routine = SimpleRoutine ( ) ;
32+ var handle = new CoroutineHandle ( routine ) ;
33+
34+ // Act
35+ var result = handle . Step ( ) ;
36+
37+ // Assert
38+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Completed ) ) ;
39+ Assert . That ( handle . Status . IsCompleted , Is . True ) ;
40+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
41+ Assert . That ( handle . Status . Exception , Is . Null ) ;
42+ }
43+
44+ [ Test ]
45+ public void Step_コルーチンがnullをyieldする場合_Continueを返しStatusが未完了であること ( )
46+ {
47+ // Arrange
48+ var routine = SimpleRoutine ( ( ) => null ) ;
49+ var handle = new CoroutineHandle ( routine ) ;
50+
51+ // Act
52+ var result = handle . Step ( ) ;
53+
54+ // Assert
55+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Continue ) ) ;
56+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
57+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
58+ }
59+
60+ [ Test ]
61+ public void Step_コルーチンが未完了のAsyncStatusをyieldする場合_Continueを返しStatusが未完了であること ( )
62+ {
63+ // Arrange
64+ var awaitedStatus = new AsyncStatus ( ) ;
65+ var routine = SimpleRoutine ( ( ) => awaitedStatus ) ;
66+ var handle = new CoroutineHandle ( routine ) ;
67+
68+ // Act
69+ var result = handle . Step ( ) ;
70+
71+ // Assert
72+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Continue ) ) ;
73+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
74+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
75+ }
76+
77+ [ Test ]
78+ public void Step_コルーチンが正常完了済みのAsyncStatusをyieldし次に完了する場合_Completedを返すこと ( )
79+ {
80+ // Arrange
81+ var awaitedStatus = new AsyncStatus ( ) ;
82+ awaitedStatus . MarkCompleted ( ) ; // 事前に完了させておく
83+ // 最初に awaitedStatus を yield し、その直後にコルーチンが完了する (MoveNext が false を返す)
84+ var finalRoutine = SimpleRoutine ( ( ) => awaitedStatus ) ;
85+ var handle = new CoroutineHandle ( finalRoutine ) ;
86+
87+ // Act
88+ // 1回目のStepでawaitedStatusを処理し、コルーチン内のwhile(true)ループにより
89+ // 即座に次の _routine.MoveNext() が試行され、それが false を返すため Completed になる。
90+ var result = handle . Step ( ) ;
91+
92+
93+ // Assert
94+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Completed ) , "完了したAsyncStatusを処理し、コルーチンも即座に完了する" ) ;
95+ Assert . That ( handle . Status . IsCompleted , Is . True ) ;
96+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
97+ }
98+
99+ [ Test ]
100+ public void Step_コルーチンが正常完了済みのAsyncStatusをyieldし次にnullをyieldする場合_Continueを返すこと ( )
101+ {
102+ // Arrange
103+ var awaitedStatus = new AsyncStatus ( ) ;
104+ awaitedStatus . MarkCompleted ( ) ;
105+
106+ // awaitedStatus を yield した後に、さらに null を yield するコルーチン
107+ var routine = SimpleRoutine ( ( ) => awaitedStatus , ( ) => null ) ;
108+ var handle = new CoroutineHandle ( routine ) ;
109+
110+ // Act
111+ // 最初のStepで awaitedStatus を処理し、内部ループで次の yield return null まで進み、そこで停止する
112+ var result = handle . Step ( ) ;
113+
114+ // Assert
115+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Continue ) ) ;
116+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
117+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
118+ }
119+
120+
121+ [ Test ]
122+ public void Step_コルーチンが失敗済みのAsyncStatusをyieldする場合_Faultedを返しStatusが失敗になること ( )
123+ {
124+ // Arrange
125+ var awaitedStatus = new AsyncStatus ( ) ;
126+ var awaitedException = new InvalidOperationException ( "Awaited task failed" ) ;
127+ awaitedStatus . MarkFaulted ( awaitedException ) ;
128+
129+ var routine = SimpleRoutine ( ( ) => awaitedStatus ) ;
130+ var handle = new CoroutineHandle ( routine ) ;
131+
132+ // Act
133+ var result = handle . Step ( ) ;
134+
135+ // Assert
136+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Faulted ) ) ;
137+ Assert . That ( handle . Status . IsCompleted , Is . True ) ;
138+ Assert . That ( handle . Status . IsFaulted , Is . True ) ;
139+ Assert . That ( handle . Status . Exception , Is . EqualTo ( awaitedException ) ) ;
140+ }
141+
142+ [ Test ]
143+ public void Step_コルーチン実行中に例外が発生する場合_Faultedを返しStatusが失敗になること ( )
144+ {
145+ // Arrange
146+ var exceptionToThrow = new ArithmeticException ( "Coroutine calculation error" ) ;
147+ var routine = SimpleRoutine ( ( ) => throw exceptionToThrow ) ;
148+ var handle = new CoroutineHandle ( routine ) ;
149+
150+ // Act
151+ // Step内で例外をキャッチし、適切に処理することをテスト
152+ var result = CoroutineStepResult . Continue ; // 初期値
153+ Exception caughtInTest = null ;
154+ try
155+ {
156+ // CoroutineHandle.Step() は MoveNext() を呼び出し、そこで例外が発生する。
157+ // Step() メソッド自体が例外をスローしないことを確認。
158+ result = handle . Step ( ) ;
159+ }
160+ catch ( Exception ex )
161+ {
162+ // このブロックが実行された場合、CoroutineHandle.Step() が例外を外にスローしたことになる。
163+ // これは期待される動作ではない。
164+ caughtInTest = ex ;
165+ }
166+
167+ // Assert
168+ Assert . That ( caughtInTest , Is . Null , "CoroutineHandle.Step() は例外を外部にスローすべきではない" ) ;
169+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Faulted ) ) ;
170+ Assert . That ( handle . Status . IsCompleted , Is . True ) ;
171+ Assert . That ( handle . Status . IsFaulted , Is . True ) ;
172+ Assert . That ( handle . Status . Exception , Is . TypeOf < ArithmeticException > ( ) ) ;
173+ Assert . That ( handle . Status . Exception , Is . SameAs ( exceptionToThrow ) ) ;
174+ }
175+
176+ [ Test ]
177+ public void Step_既に完了しているコルーチンに対してStepを呼ぶ場合_Completedを返すこと ( )
178+ {
179+ // Arrange
180+ var routine = SimpleRoutine ( ) ; // すぐ完了するコルーチン
181+ var handle = new CoroutineHandle ( routine ) ;
182+ handle . Step ( ) ; // これで完了する
183+
184+ // Act
185+ var result = handle . Step ( ) ; // 再度Stepを呼ぶ
186+
187+ // Assert
188+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Completed ) ) ;
189+ Assert . That ( handle . Status . IsCompleted , Is . True ) ; // 状態は維持
190+ }
191+
192+ [ Test ]
193+ public void Step_既に失敗しているコルーチンに対してStepを呼ぶ場合_Faultedを返すこと ( )
194+ {
195+ // Arrange
196+ var exceptionToThrow = new AccessViolationException ( "Failed" ) ;
197+ var routine = SimpleRoutine ( ( ) => throw exceptionToThrow ) ;
198+ var handle = new CoroutineHandle ( routine ) ;
199+ handle . Step ( ) ;
200+
201+ // Act
202+ var result = handle . Step ( ) ; // 再度Stepを呼ぶ
203+
204+ // Assert
205+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Faulted ) ) ;
206+ Assert . That ( handle . Status . IsFaulted , Is . True ) ;
207+ Assert . That ( handle . Status . IsCompleted , Is . True ) ;
208+ }
209+
210+
211+ [ Test ]
212+ public void Step_コルーチンがAsyncStatus以外のオブジェクトをyieldする場合_Continueを返すこと ( )
213+ {
214+ // Arrange
215+ var routine = SimpleRoutine ( ( ) => 123 ) ; // int を yield する
216+ var handle = new CoroutineHandle ( routine ) ;
217+
218+ // Act
219+ var result = handle . Step ( ) ;
220+
221+ // Assert
222+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Continue ) ) ;
223+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
224+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
225+ }
226+
227+ [ Test ]
228+ public void Step_複数ステップ後にコルーチンが完了する場合_正しくCompletedになること ( )
229+ {
230+ // Arrange
231+ var routine = SimpleRoutine ( ( ) => null , ( ) => null , ( ) => "step 3 then complete" ) ; // 3回 yield して完了
232+ var handle = new CoroutineHandle ( routine ) ;
233+
234+ // Act & Assert
235+ // 1回目のStep
236+ var result1 = handle . Step ( ) ;
237+ Assert . That ( result1 , Is . EqualTo ( CoroutineStepResult . Continue ) , "Step 1 should continue" ) ;
238+ Assert . That ( handle . Status . IsCompleted , Is . False , "Status after step 1 should not be completed" ) ;
239+
240+ // 2回目のStep
241+ var result2 = handle . Step ( ) ;
242+ Assert . That ( result2 , Is . EqualTo ( CoroutineStepResult . Continue ) , "Step 2 should continue" ) ;
243+ Assert . That ( handle . Status . IsCompleted , Is . False , "Status after step 2 should not be completed" ) ;
244+
245+ // 3回目のStep (ここで最後の yield が行われる)
246+ var result3 = handle . Step ( ) ;
247+ Assert . That ( result3 ,
248+ Is . EqualTo ( CoroutineStepResult . Continue ) ,
249+ "Step 3 should continue as it yields 'step 3 then complete'" ) ;
250+ Assert . That ( handle . Status . IsCompleted , Is . False , "Status after step 3 should not be completed" ) ;
251+
252+ // 4回目のStep (ここで MoveNext() が false を返し、コルーチンが完了する)
253+ var result4 = handle . Step ( ) ;
254+ Assert . That ( result4 , Is . EqualTo ( CoroutineStepResult . Completed ) , "Step 4 should complete the coroutine" ) ;
255+ Assert . That ( handle . Status . IsCompleted , Is . True , "Status after step 4 should be completed" ) ;
256+ Assert . That ( handle . Status . IsFaulted , Is . False ) ;
257+ }
258+
259+ [ Test ]
260+ public void Step_完了済みAsyncStatusをyield後次のステップがnullの場合_Continueを返すこと ( )
261+ {
262+ // Arrange
263+ var completedAsyncStatus = new AsyncStatus ( ) ;
264+ completedAsyncStatus . MarkCompleted ( ) ;
265+
266+ var routine = SimpleRoutine ( ( ) => completedAsyncStatus , ( ) => null ) ;
267+ var handle = new CoroutineHandle ( routine ) ;
268+
269+ // Act
270+ // 最初のStep()呼び出し:
271+ // 1. _routine.MoveNext() -> true, _routine.Current is completedAsyncStatus
272+ // 2. awaited.IsCompleted is true, awaited.IsFaulted is false
273+ // 3. ループが継続し、再度 _routine.MoveNext() -> true, _routine.Current is null
274+ // 4. _routine.Current is not AsyncStatus -> CoroutineStepResult.Continue が返る
275+ var result = handle . Step ( ) ;
276+
277+ // Assert
278+ Assert . That ( result , Is . EqualTo ( CoroutineStepResult . Continue ) ) ;
279+ Assert . That ( handle . Status . IsCompleted , Is . False ) ;
280+ }
281+
282+ // Helper method to create a simple IEnumerator that yields specified values
283+ private static IEnumerator SimpleRoutine ( params Func < object > [ ] steps )
284+ {
285+ foreach ( var step in steps )
286+ yield return step . Invoke ( ) ;
287+ }
288+ }
289+ }
0 commit comments