From 12c0886f4174dc38fe02ea3d536da2099e12722a Mon Sep 17 00:00:00 2001 From: Heiko Klare Date: Wed, 1 Apr 2026 22:36:07 +0200 Subject: [PATCH 1/2] Fix and reenable ExpressionTests.testSubTypeTiming Replaces the flaky timing-based assertion with a functional validation of the caching behaviour: uses Mockito.mockStatic to verify that Expressions.uncachedIsSubtype is invoked exactly once for a given (Class, type) pair across two consecutive isInstanceOf calls, proving that the second call is served from the cache. Fixes https://github.com/eclipse-platform/eclipse.platform/issues/894 --- .../META-INF/MANIFEST.MF | 3 +- .../expressions/tests/ExpressionTests.java | 58 +++++++++++-------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF b/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF index 7d71ae59d10..3ff18689230 100644 --- a/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF +++ b/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF @@ -13,7 +13,8 @@ Require-Bundle: Import-Package: org.assertj.core.api, org.junit.jupiter.api;version="[5.14.0,6.0.0)", org.junit.jupiter.api.function;version="[5.14.0,6.0.0)", - org.junit.platform.suite.api;version="[1.14.0,2.0.0)" + org.junit.platform.suite.api;version="[1.14.0,2.0.0)", + org.mockito Bundle-RequiredExecutionEnvironment: JavaSE-17 Eclipse-BundleShape: dir Bundle-ActivationPolicy: lazy diff --git a/runtime/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java b/runtime/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java index 331d9abdfa5..c25dfeecdb8 100644 --- a/runtime/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java +++ b/runtime/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java @@ -18,10 +18,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; import java.net.URL; import java.util.AbstractCollection; @@ -33,8 +32,9 @@ import java.util.LinkedList; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.osgi.framework.FrameworkUtil; import org.w3c.dom.Document; @@ -72,7 +72,13 @@ @SuppressWarnings("restriction") public class ExpressionTests { - private static final int TYPE_ITERATIONS = 100000; + /** + * Used exclusively by {@link #testSubTypeTiming()} to provide a cache key type + * unique to this test, avoiding interference with other tests. + */ + private static class CachingTestSet extends HashSet { + private static final long serialVersionUID = 1L; + } public static class CollectionWrapper { public Collection collection; @@ -1014,28 +1020,32 @@ public void testSubType() throws Exception { } @Test - @Disabled("CI test environment too unstable for performance tests") public void testSubTypeTiming() throws Exception { - HashSet o1 = new HashSet<>(); - - System.gc(); - long cachedStart= System.currentTimeMillis(); - for (int i= 0; i < TYPE_ITERATIONS; i++) { - assertTrue(Expressions.isInstanceOf(o1, "java.util.Set")); - assertFalse(Expressions.isInstanceOf(o1, "java.util.List")); + CachingTestSet o = new CachingTestSet(); + + try (MockedStatic expressionsMock = Mockito.mockStatic(Expressions.class, + Mockito.CALLS_REAL_METHODS)) { + // First call (positive): cache miss — isSubtype must traverse the class + // hierarchy + assertThat(Expressions.isInstanceOf(o, "java.util.Set")).isTrue(); + expressionsMock.verify(() -> Expressions.uncachedIsSubtype(CachingTestSet.class, "java.util.Set"), + times(1)); + // Second call (positive): cache hit — uncachedIsSubtype must NOT be invoked + // again + assertThat(Expressions.isInstanceOf(o, "java.util.Set")).isTrue(); + expressionsMock.verify(() -> Expressions.uncachedIsSubtype(CachingTestSet.class, "java.util.Set"), + times(1)); + // First call (negative): cache miss — isSubtype must traverse the class + // hierarchy + assertThat(Expressions.isInstanceOf(o, "java.util.List")).isFalse(); + expressionsMock.verify(() -> Expressions.uncachedIsSubtype(CachingTestSet.class, "java.util.List"), + times(1)); + // Second call (negative): cache hit — uncachedIsSubtype must NOT be invoked + // again + assertThat(Expressions.isInstanceOf(o, "java.util.List")).isFalse(); + expressionsMock.verify(() -> Expressions.uncachedIsSubtype(CachingTestSet.class, "java.util.List"), + times(1)); } - long cachedDelta= System.currentTimeMillis() - cachedStart; - - System.gc(); - long instanceStart= System.currentTimeMillis(); - for (int i= 0; i < TYPE_ITERATIONS; i++) { - assertTrue(Expressions.uncachedIsSubtype(o1.getClass(), "java.util.Set")); - assertFalse(Expressions.uncachedIsSubtype(o1.getClass(), "java.util.List")); - } - long instanceDelta= System.currentTimeMillis() - instanceStart; - - assertThat(cachedDelta).as("assert cachedDelta is less than instanceDelta" + instanceDelta) - .isLessThan(instanceDelta); } } From 880db675ae439f5c4d152c32e1e2a88df2661aef Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Wed, 1 Apr 2026 20:45:34 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.40 stream --- .../org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF b/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF index 3ff18689230..af9222664e9 100644 --- a/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF +++ b/runtime/tests/org.eclipse.core.expressions.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.expressions.tests; singleton:=true -Bundle-Version: 3.7.700.qualifier +Bundle-Version: 3.7.800.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: