Skip to content

Commit 3909341

Browse files
authored
Stabilize unit tests (Local, Static initialization) (#5731)
2 parents ec0c34e + c2dd823 commit 3909341

14 files changed

Lines changed: 251 additions & 60 deletions

File tree

core/query/src/test/java/org/eclipse/rdf4j/query/explanation/GenericPlanNodeTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,28 @@
1616
import static org.junit.jupiter.api.Assertions.assertNull;
1717
import static org.junit.jupiter.api.Assertions.assertTrue;
1818

19+
import java.util.Locale;
20+
21+
import org.junit.jupiter.api.AfterEach;
22+
import org.junit.jupiter.api.BeforeEach;
1923
import org.junit.jupiter.api.Test;
2024

2125
class GenericPlanNodeTest {
2226

27+
private Locale defaultLocale;
28+
29+
@BeforeEach
30+
void setUp() {
31+
defaultLocale = Locale.getDefault();
32+
// set EN locale explicitly to avoid different number formatting across environment
33+
Locale.setDefault(Locale.ENGLISH);
34+
}
35+
36+
@AfterEach
37+
void tearDown() {
38+
Locale.setDefault(defaultLocale);
39+
}
40+
2341
@Test
2442
void toStringIncludesPopulatedTelemetryFields() {
2543
GenericPlanNode node = new GenericPlanNode("Join");

core/queryalgebra/evaluation/src/test/java/org/eclipse/rdf4j/query/algebra/evaluation/impl/QueryCostEstimatesTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,35 @@
1212

1313
import static org.assertj.core.api.Assertions.assertThat;
1414

15+
import java.util.Locale;
16+
1517
import org.eclipse.rdf4j.common.exception.RDF4JException;
1618
import org.eclipse.rdf4j.query.algebra.evaluation.optimizer.QueryJoinOptimizer;
1719
import org.eclipse.rdf4j.query.parser.ParsedQuery;
1820
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
21+
import org.junit.jupiter.api.AfterEach;
22+
import org.junit.jupiter.api.BeforeEach;
1923
import org.junit.jupiter.api.Test;
2024

2125
/**
2226
* Tests that cost estimates are printed as part of the plan
2327
*/
2428
public class QueryCostEstimatesTest {
2529

30+
private Locale defaultLocale;
31+
32+
@BeforeEach
33+
void setUp() {
34+
defaultLocale = Locale.getDefault();
35+
// set EN locale explicitly to avoid different number formatting across environment
36+
Locale.setDefault(Locale.ENGLISH);
37+
}
38+
39+
@AfterEach
40+
void tearDown() {
41+
Locale.setDefault(defaultLocale);
42+
}
43+
2644
@Test
2745
public void testBindingSetAssignmentOptimization() throws RDF4JException {
2846
String query = "prefix ex: <ex:>" + "select ?s ?p ?o ?x where {" + " ex:s1 ex:pred ?v. "

core/queryalgebra/model/src/main/java/org/eclipse/rdf4j/query/algebra/Var.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import org.eclipse.rdf4j.model.Value;
1717

18+
import com.google.common.annotations.VisibleForTesting;
19+
1820
/**
1921
* A variable that can contain a Value.
2022
*
@@ -312,10 +314,33 @@ public boolean isConstant() {
312314
return constant;
313315
}
314316

317+
/**
318+
* Installs a custom {@link Provider} to be used by {@link Var#of} and {@link Var#clone}. Intended for test use
319+
* only. Always call {@link #resetProvider()} in a corresponding teardown.
320+
*/
321+
@VisibleForTesting
322+
/* package */ static void setProvider(Provider provider) {
323+
Holder.PROVIDER = provider;
324+
}
325+
326+
/**
327+
* Resets the {@link Provider} to the one resolved from the system property and {@link ServiceLoader}. Intended for
328+
* test use only to undo a prior {@link #setProvider} call.
329+
*/
330+
@VisibleForTesting
331+
/* package */ static void resetProvider() {
332+
Holder.reset();
333+
}
334+
315335
private static final class Holder {
316336
private static final Provider DEFAULT = Var::new;
317337

318-
static final Provider PROVIDER = initProvider();
338+
// Not final so that Var.setProvider() can replace it during tests
339+
static Provider PROVIDER = initProvider();
340+
341+
private static void reset() {
342+
PROVIDER = initProvider();
343+
}
319344

320345
private static Provider initProvider() {
321346
// 1) Explicit override via system property (FQCN of Var.Provider)

core/queryalgebra/model/src/test/java/org/eclipse/rdf4j/query/algebra/VarProviderCloneHookTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
2424
import org.junit.jupiter.api.Test;
2525

26+
@WithVarProvider(KindAwareVarProvider.class)
2627
class VarProviderCloneHookTest {
2728

2829
private final ValueFactory vf = SimpleValueFactory.getInstance();
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.query.algebra;
12+
13+
import java.util.Optional;
14+
15+
import org.junit.jupiter.api.extension.AfterEachCallback;
16+
import org.junit.jupiter.api.extension.BeforeEachCallback;
17+
import org.junit.jupiter.api.extension.ExtensionContext;
18+
19+
/**
20+
* JUnit 5 extension that installs a custom {@link Var.Provider} for the duration of each test and resets it afterwards.
21+
*
22+
* <p>
23+
* Activated via {@link WithVarProvider} on the test class or method. The method-level annotation takes precedence over
24+
* the class-level annotation. The provider is always reset after each test regardless of outcome.
25+
*
26+
* @see WithVarProvider
27+
*/
28+
class VarProviderExtension implements BeforeEachCallback, AfterEachCallback {
29+
30+
@Override
31+
public void beforeEach(ExtensionContext context) throws Exception {
32+
Optional<WithVarProvider> annotation = findAnnotation(context);
33+
if (annotation.isPresent()) {
34+
Var.Provider provider = annotation.get().value().getDeclaredConstructor().newInstance();
35+
Var.setProvider(provider);
36+
}
37+
}
38+
39+
@Override
40+
public void afterEach(ExtensionContext context) {
41+
Var.resetProvider();
42+
}
43+
44+
/**
45+
* Returns the {@link WithVarProvider} annotation, preferring the method level over the class level.
46+
*/
47+
private Optional<WithVarProvider> findAnnotation(ExtensionContext context) {
48+
return context.getTestMethod()
49+
.map(m -> m.getAnnotation(WithVarProvider.class))
50+
.or(() -> context.getTestClass().map(c -> c.getAnnotation(WithVarProvider.class)));
51+
}
52+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
package org.eclipse.rdf4j.query.algebra;
12+
13+
import java.lang.annotation.ElementType;
14+
import java.lang.annotation.Retention;
15+
import java.lang.annotation.RetentionPolicy;
16+
import java.lang.annotation.Target;
17+
18+
import org.junit.jupiter.api.extension.ExtendWith;
19+
20+
/**
21+
* Installs a custom {@link Var.Provider} for the duration of the annotated test class or test method.
22+
*
23+
* <p>
24+
* The specified provider is instantiated via its no-argument constructor, installed before each test, and removed after
25+
* each test via {@link Var#resetProvider()}. When placed on both a class and a method, the method-level annotation
26+
* takes precedence.
27+
*
28+
* <p>
29+
* Usage:
30+
*
31+
* <pre>
32+
* &#64;WithVarProvider(MyVarProvider.class)
33+
* class MyTest {
34+
*
35+
* &#64;Test
36+
* void test() {
37+
* Var v = Var.of("x"); // produced by MyVarProvider
38+
* }
39+
* }
40+
* </pre>
41+
*/
42+
@Target({ ElementType.TYPE, ElementType.METHOD })
43+
@Retention(RetentionPolicy.RUNTIME)
44+
@ExtendWith(VarProviderExtension.class)
45+
public @interface WithVarProvider {
46+
47+
/**
48+
* The {@link Var.Provider} implementation to install. Must have a public no-argument constructor.
49+
*/
50+
Class<? extends Var.Provider> value();
51+
}

core/queryalgebra/model/src/test/java/org/eclipse/rdf4j/query/algebra/helpers/QueryModelTreeToGenericPlanNodeTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.eclipse.rdf4j.query.algebra.ExtensionElem;
2323
import org.eclipse.rdf4j.query.algebra.Filter;
2424
import org.eclipse.rdf4j.query.algebra.Join;
25+
import org.eclipse.rdf4j.query.algebra.KindAwareVarProvider;
2526
import org.eclipse.rdf4j.query.algebra.Projection;
2627
import org.eclipse.rdf4j.query.algebra.ProjectionElem;
2728
import org.eclipse.rdf4j.query.algebra.ProjectionElemList;
@@ -30,6 +31,7 @@
3031
import org.eclipse.rdf4j.query.algebra.StatementPattern;
3132
import org.eclipse.rdf4j.query.algebra.TupleExpr;
3233
import org.eclipse.rdf4j.query.algebra.Var;
34+
import org.eclipse.rdf4j.query.algebra.WithVarProvider;
3335
import org.eclipse.rdf4j.query.explanation.Explanation;
3436
import org.eclipse.rdf4j.query.explanation.GenericPlanNode;
3537
import org.eclipse.rdf4j.query.explanation.TelemetryMetricNames;
@@ -189,6 +191,7 @@ public void doesNotAnnotateConnectedJoinAsCartesianJoin() {
189191
}
190192

191193
@Test
194+
@WithVarProvider(KindAwareVarProvider.class)
192195
public void skipsCartesianAnnotationForUnsupportedVarSubclass() {
193196
Join join = new Join(
194197
new StatementPattern(Var.of("s"), Var.of("p"), Var.of("o")),

core/queryalgebra/model/src/test/resources/META-INF/services/org.eclipse.rdf4j.query.algebra.Var$Provider

Lines changed: 0 additions & 1 deletion
This file was deleted.

core/sail/api/src/test/java/org/eclipse/rdf4j/common/concurrent/locks/AbstractReadWriteLockManagerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ void deadlockTestReadWrite() throws InterruptedException {
346346
assertThat(memoryAppender.countEventsForLogger(className)).isEqualTo(1);
347347
memoryAppender.assertContains("is possibly deadlocked waiting on \"_READ\" with id ", Level.WARN);
348348
memoryAppender.assertContains(
349-
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.lambda$getStartedDaemonThread", Level.WARN);
349+
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.acquireTwoLocks(", Level.WARN);
350350

351351
}
352352

@@ -370,7 +370,7 @@ void deadlockTestWriteRead() throws InterruptedException {
370370
assertThat(memoryAppender.countEventsForLogger(className)).isEqualTo(1);
371371
memoryAppender.assertContains("is possibly deadlocked waiting on \"_WRITE\" with id ", Level.WARN);
372372
memoryAppender.assertContains(
373-
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.lambda$getStartedDaemonThread", Level.WARN);
373+
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.acquireTwoLocks(", Level.WARN);
374374

375375
}
376376

@@ -394,7 +394,7 @@ void deadlockTestWriteWrite() throws InterruptedException {
394394
assertThat(memoryAppender.countEventsForLogger(className)).isEqualTo(1);
395395
memoryAppender.assertContains("is possibly deadlocked waiting on \"_WRITE\" with id ", Level.WARN);
396396
memoryAppender.assertContains(
397-
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.lambda$getStartedDaemonThread", Level.WARN);
397+
"at org.eclipse.rdf4j.common.concurrent.locks.TestHelper.acquireTwoLocks(", Level.WARN);
398398

399399
}
400400

core/sail/api/src/test/java/org/eclipse/rdf4j/common/concurrent/locks/ExclusiveLockManagerTest.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,7 @@ void deadlockTest() throws InterruptedException {
9191

9292
Thread thread = null;
9393
try {
94-
thread = new Thread(() -> {
95-
Lock lock1 = null;
96-
Lock lock2 = null;
97-
try {
98-
lock1 = lockManagerTracking.getExclusiveLock();
99-
lock2 = lockManagerTracking.getExclusiveLock();
100-
} catch (InterruptedException ignored) {
101-
102-
} finally {
103-
if (lock1 != null) {
104-
lock1.release();
105-
}
106-
if (lock2 != null) {
107-
lock2.release();
108-
}
109-
}
110-
});
111-
94+
thread = new Thread(() -> acquireTwoLocks(lockManagerTracking));
11295
thread.setDaemon(true);
11396
thread.start();
11497

@@ -125,7 +108,7 @@ void deadlockTest() throws InterruptedException {
125108
assertThat(memoryAppender.countEventsForLogger(ExclusiveLockManager.class.getName())).isEqualTo(1);
126109
memoryAppender.assertContains("is possibly deadlocked waiting on \"ExclusiveLockManager\" with id", Level.WARN);
127110
memoryAppender.assertContains(
128-
"at org.eclipse.rdf4j.common.concurrent.locks.ExclusiveLockManagerTest.lambda$deadlockTest$0(ExclusiveLockManagerTest.",
111+
"at org.eclipse.rdf4j.common.concurrent.locks.ExclusiveLockManagerTest.acquireTwoLocks(",
129112
Level.WARN);
130113
}
131114

@@ -167,6 +150,23 @@ void stalledTest() throws InterruptedException {
167150

168151
}
169152

153+
private void acquireTwoLocks(ExclusiveLockManager lockManager) {
154+
Lock lock1 = null;
155+
Lock lock2 = null;
156+
try {
157+
lock1 = lockManager.getExclusiveLock();
158+
lock2 = lockManager.getExclusiveLock();
159+
} catch (InterruptedException ignored) {
160+
} finally {
161+
if (lock1 != null) {
162+
lock1.release();
163+
}
164+
if (lock2 != null) {
165+
lock2.release();
166+
}
167+
}
168+
}
169+
170170
private void lock(ExclusiveLockManager lockManager) throws InterruptedException {
171171
Lock lock = lockManager.getExclusiveLock();
172172
assertTrue(lock.isActive());

0 commit comments

Comments
 (0)