Skip to content

Flaky test on Windows: EmbeddedOpenDJTest#testOpenDJ — EmbeddedOpenDJ.close() silently leaves its temporary directory behind #797

Description

@vharseko

EmbeddedOpenDJTest#testOpenDJ failed on windows-latest / Java 26 while every other job of the same run — including windows-latest / Java 11 — was green:

[ERROR] EmbeddedOpenDJTest.testOpenDJ:91 expected [false] but found [true]
java.lang.AssertionError: expected [false] but found [true]
	at org.testng.Assert.assertFalse(Assert.java:75)
	at org.openidentityplatform.opendj.embedded.EmbeddedOpenDJTest.testOpenDJ(EmbeddedOpenDJTest.java:91)

Run: https://github.com/OpenIdentityPlatform/OpenDJ/actions/runs/30554186279/job/90910350398 (observed on PR #793, whose diff does not touch opendj-embedded; the same job passed on the previous commit of that PR and on PRs #790, #791, #795).

What actually fails

Everything the test exercises works — the server starts, the LDIF import, the export, the bind and the search all succeed, and the shutdown completes cleanly:

15:05:16.685 [main] INFO  o.o.s.backends.pluggable.BackendImpl - The backend userRoot is now taken offline
15:05:16.692 [main] INFO  o.opends.server.core.DirectoryServer - The Directory Server is now stopped

The only failing step is the assertion added in #763, which checks that the per-instance temporary directory is gone after close():

https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/test/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJTest.java#L86-L92

//stop OpenDJ
embeddedOpenDJ.close();
assertFalse(embeddedOpenDJ.isRunning());

//the per-instance temporary directory is deleted on close
assertFalse(serverRoot.exists());
assertFalse(serverRoot.getParentFile().exists());

Cause

EmbeddedOpenDJ.close() deletes the instance directory with a single best-effort call, immediately after server.stop() returns:

https://github.com/OpenIdentityPlatform/OpenDJ/blob/master/opendj-embedded/src/main/java/org/openidentityplatform/opendj/embedded/EmbeddedOpenDJ.java#L166-L178

@Override
public void close()  {
    if (server.isRunning()) {
        ...
        server.stop(...);
    }
    // close() is also registered as a shutdown hook, so deletion must stay idempotent
    FileUtils.deleteQuietly(instanceDirectory);
}

On Windows a file cannot be deleted while any handle to it is still open, and server.stop() returning does not guarantee that every handle — backend files, the JSON audit event handler, server.out, the extracted opendj.zip — has already been released; some are closed by background threads shortly afterwards. When that race is lost, deleteQuietly fails, swallows the exception, and the directory survives. POSIX platforms are unaffected because unlinking an open file is allowed there, which is why only the Windows jobs can ever see this.

So the assertion is not wrong — it reports a real, Windows-only defect that is normally invisible:

  • every embedded instance can leak its whole temporary directory (an extracted server root, backend data included) into %TEMP% on Windows;
  • close() gives the caller no indication that the cleanup failed, since deleteQuietly discards the error.

Suggested fix

In EmbeddedOpenDJ.close(), retry the deletion for a short bounded period instead of attempting it once, so that handles released just after shutdown are still picked up; log a warning if the directory is still there when the retries are exhausted, and register the leftovers for deletion on JVM exit as a last resort. Keeping the operation idempotent matters — close() is also registered as a shutdown hook.

With that in place the existing assertions in EmbeddedOpenDJTest become deterministic on Windows and can stay as they are.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CIWindowsbugembeddedEmbedded directory server (EmbeddedDirectoryServer)testsTest suites: fixing, enabling, un-disabling

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions