3939import java .util .UUID ;
4040import java .util .concurrent .atomic .AtomicBoolean ;
4141import java .util .concurrent .atomic .AtomicInteger ;
42+ import java .util .stream .Collectors ;
43+ import java .util .stream .Stream ;
4244
4345import com .asarkar .grpc .test .GrpcCleanupExtension ;
4446import com .asarkar .grpc .test .Resources ;
5557import org .apache .commons .io .FileUtils ;
5658import org .jetbrains .annotations .NotNull ;
5759import org .junit .jupiter .api .AfterAll ;
60+ import org .junit .jupiter .api .AfterEach ;
5861import org .junit .jupiter .api .Assertions ;
5962import org .junit .jupiter .api .BeforeAll ;
6063import org .junit .jupiter .api .Test ;
@@ -141,6 +144,47 @@ static void clean() {
141144 }
142145 }
143146
147+ private final List <TikaGrpcServerImpl > services = new ArrayList <>();
148+
149+ /**
150+ * Every service built here is closed by {@link #closeServices()}. Only the manager that
151+ * started a pipes-server child can delete that child's temp dir, so a service the test
152+ * drops on the floor -- including on an assertion failure -- orphans it.
153+ */
154+ private TikaGrpcServerImpl newService (Path config ) throws Exception {
155+ TikaGrpcServerImpl service = new TikaGrpcServerImpl (config .toAbsolutePath ().toString ());
156+ services .add (service );
157+ return service ;
158+ }
159+
160+ @ AfterEach
161+ void closeServices () throws Exception {
162+ for (TikaGrpcServerImpl service : services ) {
163+ //both are idempotent, so a test that already shut down in-line is fine
164+ service .shutdown ();
165+ service .postShutdown ();
166+ }
167+ services .clear ();
168+ assertNoOrphanedServerTempDirs ();
169+ }
170+
171+ /**
172+ * Fails the test that leaked rather than leaving it for whoever notices the temp dir later.
173+ * Reliable because surefire gives each module its own java.io.tmpdir and runs these
174+ * classes one at a time.
175+ */
176+ private static void assertNoOrphanedServerTempDirs () throws Exception {
177+ Path tmp = Paths .get (System .getProperty ("java.io.tmpdir" ));
178+ try (Stream <Path > paths = Files .list (tmp )) {
179+ List <String > orphans = paths
180+ .map (p -> p .getFileName ().toString ())
181+ .filter (n -> n .startsWith ("pipes-server-" ))
182+ .sorted ()
183+ .collect (Collectors .toList ());
184+ assertTrue (orphans .isEmpty (), "orphaned pipes-server temp dirs: " + orphans );
185+ }
186+ }
187+
144188 static final int NUM_FETCHERS_TO_CREATE = 10 ;
145189
146190 @ Test
@@ -150,7 +194,7 @@ public void testFetcherCrud(Resources resources) throws Exception {
150194 Server server = InProcessServerBuilder
151195 .forName (serverName )
152196 .directExecutor ()
153- .addService (new TikaGrpcServerImpl (tikaConfigUnlocked . toAbsolutePath (). toString () ))
197+ .addService (newService (tikaConfigUnlocked ))
154198 .build ()
155199 .start ();
156200 resources .register (server , Duration .ofSeconds (10 ));
@@ -451,13 +495,14 @@ public void testSavePipesIteratorValidatesType(Resources resources) throws Excep
451495 assertNotNull (reply .getMessage ());
452496 }
453497
454- private static TikaGrpc .TikaBlockingStub startServer (Resources resources , Path config )
498+ //non-static: newService tracks the service on the per-test instance so it gets closed
499+ private TikaGrpc .TikaBlockingStub startServer (Resources resources , Path config )
455500 throws Exception {
456501 String serverName = InProcessServerBuilder .generateName ();
457502 Server server = InProcessServerBuilder
458503 .forName (serverName )
459504 .directExecutor ()
460- .addService (new TikaGrpcServerImpl (config . toAbsolutePath (). toString () ))
505+ .addService (newService (config ))
461506 .build ()
462507 .start ();
463508 resources .register (server , Duration .ofSeconds (10 ));
@@ -481,7 +526,7 @@ public void testServerSideStreamingSendsTerminalSignal(Resources resources) thro
481526 Server server = InProcessServerBuilder
482527 .forName (serverName )
483528 .directExecutor ()
484- .addService (new TikaGrpcServerImpl (tikaConfigUnlocked . toAbsolutePath (). toString () ))
529+ .addService (newService (tikaConfigUnlocked ))
485530 .build ()
486531 .start ();
487532 resources .register (server , Duration .ofSeconds (10 ));
@@ -544,7 +589,7 @@ public void onCompleted() {
544589 public void testBiStream (Resources resources ) throws Exception {
545590 String serverName = InProcessServerBuilder .generateName ();
546591
547- TikaGrpcServerImpl tikaGrpcServerImpl = new TikaGrpcServerImpl (tikaConfigUnlocked . toAbsolutePath (). toString () );
592+ TikaGrpcServerImpl tikaGrpcServerImpl = newService (tikaConfigUnlocked );
548593 Server server = InProcessServerBuilder
549594 .forName (serverName )
550595 .directExecutor ()
0 commit comments