@@ -271,9 +271,47 @@ public void processElement(ProcessContext context, @StateId("count") ValueState<
271271 private final String watchPathStr ;
272272 }
273273
274+ private static class AfterNumberOfNewOutputs
275+ implements Watch .Growth .TerminationCondition <String , Integer > {
276+ private final int numOutputs ;
277+
278+ public AfterNumberOfNewOutputs (int numOutputs ) {
279+ this .numOutputs = numOutputs ;
280+ }
281+
282+ @ Override
283+ public org .apache .beam .sdk .coders .Coder <Integer > getStateCoder () {
284+ return VarIntCoder .of ();
285+ }
286+
287+ @ Override
288+ public Integer forNewInput (org .joda .time .Instant now , String input ) {
289+ return 0 ;
290+ }
291+
292+ @ Override
293+ public Integer onSeenNewOutput (org .joda .time .Instant now , Integer state ) {
294+ return (state == null ? 0 : state ) + 1 ;
295+ }
296+
297+ @ Override
298+ public boolean canStopPolling (org .joda .time .Instant now , Integer state ) {
299+ return (state == null ? 0 : state ) >= numOutputs ;
300+ }
301+
302+ @ Override
303+ public String toString (Integer state ) {
304+ return "AfterNumberOfNewOutputs(" + state + "/" + numOutputs + ")" ;
305+ }
306+ }
307+
274308 @ Test
275309 @ Category ({NeedsRunner .class , UsesUnboundedSplittableParDo .class })
276310 public void testMatchWatchForNewFiles () throws IOException , InterruptedException {
311+ final int numFiles = 3 ;
312+ final int numFirstFiles = 3 ;
313+ final int numUpdatedFiles = 6 ; // first x3, second x2, third x1
314+
277315 // Write some files to a "source" directory.
278316 final Path sourcePath = tmpFolder .getRoot ().toPath ().resolve ("source" );
279317 sourcePath .toFile ().mkdir ();
@@ -291,23 +329,29 @@ public void testMatchWatchForNewFiles() throws IOException, InterruptedException
291329 .filepattern (watchPath .resolve ("*" ).toString ())
292330 .continuously (
293331 Duration .millis (100 ),
294- Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (1 ))));
332+ Watch .Growth .eitherOf (
333+ new AfterNumberOfNewOutputs (numFiles ),
334+ Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (10 )))));
295335 PCollection <MatchResult .Metadata > matchAllMetadata =
296336 p .apply ("create for matchAll new files" , Create .of (watchPath .resolve ("*" ).toString ()))
297337 .apply (
298338 "match filename through matchAll" ,
299339 FileIO .matchAll ()
300340 .continuously (
301341 Duration .millis (100 ),
302- Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (1 ))));
342+ Watch .Growth .eitherOf (
343+ new AfterNumberOfNewOutputs (numFiles ),
344+ Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (10 )))));
303345 PCollection <MatchResult .Metadata > matchUpdatedMetadata =
304346 p .apply (
305347 "match updated" ,
306348 FileIO .match ()
307349 .filepattern (watchPath .resolve ("first" ).toString ())
308350 .continuously (
309351 Duration .millis (100 ),
310- Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (1 )),
352+ Watch .Growth .eitherOf (
353+ new AfterNumberOfNewOutputs (numFirstFiles ),
354+ Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (10 ))),
311355 true ));
312356 PCollection <MatchResult .Metadata > matchAllUpdatedMetadata =
313357 p .apply ("create for matchAll updated files" , Create .of (watchPath .resolve ("*" ).toString ()))
@@ -316,7 +360,9 @@ public void testMatchWatchForNewFiles() throws IOException, InterruptedException
316360 FileIO .matchAll ()
317361 .continuously (
318362 Duration .millis (100 ),
319- Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (1 )),
363+ Watch .Growth .eitherOf (
364+ new AfterNumberOfNewOutputs (numUpdatedFiles ),
365+ Watch .Growth .afterTimeSinceNewOutput (Duration .standardSeconds (10 ))),
320366 true ));
321367
322368 // write one file at the beginning. This will trigger the first output for matchAll
0 commit comments