2525import com .google .genai .types .Content ;
2626import com .google .genai .types .Part ;
2727import io .reactivex .rxjava3 .core .Flowable ;
28+ import io .reactivex .rxjava3 .core .Scheduler ;
2829import io .reactivex .rxjava3 .schedulers .Schedulers ;
30+ import io .reactivex .rxjava3 .schedulers .TestScheduler ;
31+ import io .reactivex .rxjava3 .subscribers .TestSubscriber ;
2932import java .util .List ;
3033import org .junit .Test ;
3134import org .junit .runner .RunWith ;
@@ -36,10 +39,16 @@ public final class ParallelAgentTest {
3639
3740 static class TestingAgent extends BaseAgent {
3841 private final long delayMillis ;
42+ private final Scheduler scheduler ;
3943
4044 private TestingAgent (String name , String description , long delayMillis ) {
45+ this (name , description , delayMillis , Schedulers .computation ());
46+ }
47+
48+ private TestingAgent (String name , String description , long delayMillis , Scheduler scheduler ) {
4149 super (name , description , ImmutableList .of (), null , null );
4250 this .delayMillis = delayMillis ;
51+ this .scheduler = scheduler ;
4352 }
4453
4554 @ Override
@@ -55,7 +64,7 @@ protected Flowable<Event> runAsyncImpl(InvocationContext invocationContext) {
5564 .build ());
5665
5766 if (delayMillis > 0 ) {
58- return event .delay (delayMillis , MILLISECONDS , Schedulers . computation () );
67+ return event .delay (delayMillis , MILLISECONDS , scheduler );
5968 }
6069 return event ;
6170 }
@@ -110,4 +119,79 @@ public void runAsync_noSubAgents_returnsEmptyFlowable() {
110119
111120 assertThat (events ).isEmpty ();
112121 }
122+
123+ static class BlockingAgent extends BaseAgent {
124+ private final long sleepMillis ;
125+
126+ private BlockingAgent (String name , long sleepMillis ) {
127+ super (name , "Blocking Agent" , ImmutableList .of (), null , null );
128+ this .sleepMillis = sleepMillis ;
129+ }
130+
131+ @ Override
132+ protected Flowable <Event > runAsyncImpl (InvocationContext invocationContext ) {
133+ return Flowable .fromCallable (
134+ () -> {
135+ Thread .sleep (sleepMillis );
136+ return Event .builder ()
137+ .author (name ())
138+ .branch (invocationContext .branch ().orElse (null ))
139+ .invocationId (invocationContext .invocationId ())
140+ .content (Content .fromParts (Part .fromText ("Done" )))
141+ .build ();
142+ });
143+ }
144+
145+ @ Override
146+ protected Flowable <Event > runLiveImpl (InvocationContext invocationContext ) {
147+ throw new UnsupportedOperationException ("Not implemented" );
148+ }
149+ }
150+
151+ @ Test
152+ public void runAsync_blockingSubAgents_shouldExecuteInParallel () {
153+ long sleepTime = 1000 ;
154+ BlockingAgent agent1 = new BlockingAgent ("agent1" , sleepTime );
155+ BlockingAgent agent2 = new BlockingAgent ("agent2" , sleepTime );
156+
157+ ParallelAgent parallelAgent =
158+ ParallelAgent .builder ().name ("parallel_agent" ).subAgents (agent1 , agent2 ).build ();
159+
160+ InvocationContext invocationContext = createInvocationContext (parallelAgent );
161+
162+ long startTime = System .currentTimeMillis ();
163+ List <Event > events = parallelAgent .runAsync (invocationContext ).toList ().blockingGet ();
164+ long duration = System .currentTimeMillis () - startTime ;
165+
166+ assertThat (events ).hasSize (2 );
167+ // If parallel, duration should be less than 1.5 * sleepTime (1500ms).
168+ assertThat (duration ).isAtLeast (sleepTime );
169+ assertThat (duration ).isLessThan ((long ) (1.5 * sleepTime ));
170+ }
171+
172+ @ Test
173+ public void runAsync_withTestScheduler_usesVirtualTime () {
174+ TestScheduler testScheduler = new TestScheduler ();
175+ long delayMillis = 1000 ;
176+ TestingAgent agent =
177+ new TestingAgent ("delayed_agent" , "Delayed Agent" , delayMillis , testScheduler );
178+
179+ ParallelAgent parallelAgent =
180+ ParallelAgent .builder ()
181+ .name ("parallel_agent" )
182+ .subAgents (agent )
183+ .scheduler (testScheduler )
184+ .build ();
185+
186+ InvocationContext invocationContext = createInvocationContext (parallelAgent );
187+
188+ TestSubscriber <Event > testSubscriber = parallelAgent .runAsync (invocationContext ).test ();
189+
190+ testScheduler .advanceTimeBy (delayMillis - 100 , MILLISECONDS );
191+ testSubscriber .assertNoValues ();
192+ testSubscriber .assertNotComplete ();
193+ testScheduler .advanceTimeBy (200 , MILLISECONDS );
194+ testSubscriber .assertValueCount (1 );
195+ testSubscriber .assertComplete ();
196+ }
113197}
0 commit comments