minor
#29412
Flaky TestDynamicComponentService.testIncrementalUpdates: asserts on asynchronous WatchService event after a fixed 10ms sleep
Problem
test.com.top_logic.layout.editor.TestDynamicComponentService#testIncrementalUpdates (run with H2_KB) fails intermittently (build goes UNSTABLE). It passed in Jenkins Build_Git #15016 and failed in #15017 on the identical commit 1c79cfb, so it is a genuine non-deterministic flake, not a regression. The only log line near the failure is the normal shutdown of DynamicComponentService; there is no exception or stack trace, consistent with a plain assertNotNull/`assertNull` failing.
(Surfaced while investigating PR #1485 / #29195, but unrelated to that change.)
Root cause
The test creates, then deletes, a layout definition file, sleeps a fixed 10 ms each time, and then does a single non-blocking poll of the OS file-watch service before asserting. java.nio.file.WatchService delivers events asynchronously with unbounded latency, so 10 ms is not always enough on a loaded CI host.
- com.top_logic/src/test/java/test/com/top_logic/layout/editor/TestDynamicComponentService.java:55 — Thread.sleep(10) then assertHasTemplate(...) at :57.
- ...TestDynamicComponentService.java:62 — Thread.sleep(10) then assertMissingTemplate(...) at :64.
- Read path is fully non-blocking: DynamicComponentService.java:167 calls FileSystemCache.getCache().fetchUpdates().
- com.top_logic.basic/src/main/java/com/top_logic/basic/io/IDEFileSystemCache.java:88-100 (fetchUpdates) → poll() at :168-176 uses the zero-arg _watcher.poll() (:169), which returns null immediately if the OS has not yet enqueued the event.
When the event has not arrived within 10 ms, fetchUpdates() produces no PathUpdate, _allDefinitions is not updated, and the assertion sees stale state → flaky failure.
The test is bound to the real IDEFileSystemCache (not the no-op default) via ServiceTestSetup bringing up FileSystemCache.Module with devel-file-cache-ide.config.xml, so the WatchService is genuinely in play.
Suggested fix (test-only)
Replace each fixed-sleep-then-single-check with a bounded poll-until loop: retry getComponentDefinition(...) until the expected present/absent state is observed or a generous timeout (~10 s) elapses. Because getComponentDefinition re-invokes fetchUpdates() on each call, every retry re-polls the watcher, so the late event is picked up as soon as the OS delivers it; the happy path still returns within a few ms. A genuine regression then fails deterministically at the timeout with a clear message instead of today's silent assert.
Best expressed as a reusable awaitUntil(timeout, BooleanSupplier) helper on BasicTestCase (no such poll-until utility exists there today). Production IDEFileSystemCache.poll() should not be made blocking — that would risk stalling the request thread; the fix belongs in the test.
Related
- #28090 (closed, TL_7.8.1) fixed a different symptom in the same component — the cache logging an error on fast create-then-delete. This ticket is about the test's own synchronization, which remains timing-dependent.
Notes
- Reproduces only intermittently; a plain CI re-run usually passes.