This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 8d6769e36b7a CAMEL-23912: Fix flaky ThemeTest by using Theme's own 
classloader for CSS
8d6769e36b7a is described below

commit 8d6769e36b7ad92e188b989409a6744cf5bdfd58
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue Jul 7 07:09:43 2026 +0200

    CAMEL-23912: Fix flaky ThemeTest by using Theme's own classloader for CSS
    
    ThemeTest had a 34% flaky rate under parallel test execution because
    StyleEngine.loadStylesheet() used StyleEngine.class.getClassLoader() which
    can diverge from the project's classloader. Load CSS via
    Theme.class.getClassLoader() and addStylesheet() instead, synchronize the
    accent() and zebra() accessors, and use ConcurrentHashMap for the style
    cache.
    
    Closes #24475
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../camel/dsl/jbang/core/commands/tui/Theme.java   | 40 +++++++++++++++++++---
 1 file changed, 35 insertions(+), 5 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
index 1d549acaa65b..6b62c45fa96a 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Theme.java
@@ -16,11 +16,14 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import dev.tamboui.css.Styleable;
 import dev.tamboui.css.engine.StyleEngine;
@@ -70,7 +73,7 @@ final class Theme {
     private static final Style FALLBACK_MCP_DOWN = 
Style.EMPTY.fg(Color.LIGHT_RED);
     private static final Color FALLBACK_ZEBRA = Color.rgb(0x1C, 0x1C, 0x1C);
 
-    private static final Map<String, Style> CACHE = new HashMap<>();
+    private static final Map<String, Style> CACHE = new ConcurrentHashMap<>();
 
     private static boolean initialized;
     private static boolean fallbackLogged;
@@ -81,7 +84,7 @@ final class Theme {
     private Theme() {
     }
 
-    static Color accent() {
+    static synchronized Color accent() {
         StyleEngine e = engine();
         if (e == null) {
             return ACCENT;
@@ -98,7 +101,7 @@ final class Theme {
      * Subtle alternating-row background for zebra striping. Theme-aware (dark 
gray on dark, light gray on light) so
      * stripes stay readable on both terminals. Applied at the row level so it 
never overrides the selection highlight.
      */
-    static Color zebra() {
+    static synchronized Color zebra() {
         StyleEngine e = engine();
         if (e == null) {
             return FALLBACK_ZEBRA;
@@ -243,7 +246,12 @@ final class Theme {
         }
         try {
             StyleEngine e = StyleEngine.create();
-            e.loadStylesheet(mode, "tui/themes/" + mode + ".tcss");
+            // Read CSS content via Theme's own classloader instead of 
delegating
+            // to StyleEngine.loadStylesheet() which uses 
StyleEngine.class.getClassLoader().
+            // Under parallel test execution the two classloaders can diverge, 
causing
+            // intermittent "resource not found" failures in CI.
+            String cssContent = loadCssResource("tui/themes/" + mode + 
".tcss");
+            e.addStylesheet(mode, cssContent);
             e.setActiveStylesheet(mode);
             engine = e;
         } catch (Exception ex) {
@@ -253,6 +261,28 @@ final class Theme {
         return engine;
     }
 
+    /**
+     * Reads a CSS resource from the classpath using this class's own 
classloader, which is guaranteed to have access to
+     * project resources regardless of classloader hierarchy.
+     */
+    private static String loadCssResource(String path) throws IOException {
+        InputStream is = 
Theme.class.getClassLoader().getResourceAsStream(path);
+        if (is == null) {
+            // Fallback to the thread context classloader in case the class's 
own classloader
+            // doesn't have visibility (e.g., in OSGi or custom classloader 
setups).
+            ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+            if (tccl != null) {
+                is = tccl.getResourceAsStream(path);
+            }
+        }
+        if (is == null) {
+            throw new IOException("Theme CSS resource not found on classpath: 
" + path);
+        }
+        try (InputStream in = is) {
+            return new String(in.readAllBytes(), StandardCharsets.UTF_8);
+        }
+    }
+
     private static String loadPersistedMode() {
         String[] holder = { DARK };
         try {

Reply via email to