2121import java .util .Arrays ;
2222import java .util .Collections ;
2323import java .util .List ;
24+ import java .util .concurrent .ConcurrentHashMap ;
2425
2526public abstract class KarateUtils {
2627
@@ -36,20 +37,24 @@ private KarateUtils() {}
3637 LibraryCapability .TIA ,
3738 LibraryCapability .DISABLED );
3839
40+ private static final ConcurrentHashMap <URI , String > CLASSPATH_NAME_CACHE =
41+ new ConcurrentHashMap <>();
42+
3943 public static Feature getFeature (FeatureRuntime featureRuntime ) {
4044 return featureRuntime .getFeature ();
4145 }
4246
4347 /**
44- * Karate v2's {@code Feature.getNameForReport()} composes the identifier from {@code
45- * Resource.getRelativePath()}, which v2 computes by relativizing the resolved {@code file://}
46- * URL against the JVM's working directory — build-relative locally, absolute on CI runners.
47- * Karate v1 sidestepped this by tracking the classpath root via ClassGraph, so its identifier
48- * ({@code "[org/example/test_succeed] test succeed"}) is stable across environments.
48+ * Produces the karate-1.0-compatible suite identifier {@code "[<classpath-relative-path>]
49+ * <name>"} (e.g. {@code "[org/example/test_succeed] test succeed"}).
50+ *
51+ * <p>We can't use v2's {@code Feature.getNameForReport()} directly: it derives the path from
52+ * {@code Resource.getRelativePath()}, which v2 relativizes against the JVM working directory and
53+ * falls back to the absolute path when the file is outside it.
4954 *
50- * <p>We recover the same classpath-relative form here by asking the classloader to resolve
51- * progressively longer suffixes of the feature file 's path — the first suffix whose {@code
52- * ClassLoader.getResource(...)} returns the same URL is the classpath-relative name .
55+ * <p>We recover that form by asking the classloader to resolve progressively longer suffixes of
56+ * the feature's path; the shortest suffix that resolves back to the same file is the
57+ * classpath-relative name. Falls back to the bare filename if nothing resolves .
5358 */
5459 public static String getFeatureNameForReport (Feature feature ) {
5560 if (feature == null ) {
@@ -68,6 +73,10 @@ private static String resolveClasspathRelativeName(Feature feature) {
6873 if (uri == null ) {
6974 return feature .getName () != null ? feature .getName () : "" ;
7075 }
76+ return CLASSPATH_NAME_CACHE .computeIfAbsent (uri , KarateUtils ::computeClasspathRelativeName );
77+ }
78+
79+ private static String computeClasspathRelativeName (URI uri ) {
7180 Path path ;
7281 try {
7382 path = Paths .get (uri );
0 commit comments