Skip to content

Commit f419b8b

Browse files
authored
improve: allow flaky tests run with specify parameters (#4543)
Fix #3249 ### Motivation This PR addresses the issue where tests annotated with @FlakyTest were not being executed by JUnit unless they were also annotated with @test. To resolve this, we have updated our Maven Surefire plugin configuration to exclude tests tagged with flaky during regular CI runs, allowing these tests to be run separately. This adjustment ensures that all tests, including those marked as flaky, are executed as intended unless explicitly excluded.(In CI still don't run it) Moreover, I've planned the addition of a daily CI job and non-required CI to specifically run tests tagged as flaky, ensuring continuous monitoring and quicker identification of intermittent issues without affecting the main test pipeline. This setup also enhances the ability to run tests directly from the IDE, making it more convenient for developers to execute and debug individual tests as needed.
1 parent 3732f4b commit f419b8b

File tree

9 files changed

+124
-49
lines changed

9 files changed

+124
-49
lines changed

bookkeeper-common/src/test/java/org/apache/bookkeeper/common/testing/annotations/FlakyTest.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,44 @@
1919
package org.apache.bookkeeper.common.testing.annotations;
2020

2121
import java.lang.annotation.Documented;
22+
import java.lang.annotation.ElementType;
2223
import java.lang.annotation.Retention;
2324
import java.lang.annotation.RetentionPolicy;
25+
import java.lang.annotation.Target;
26+
import org.junit.jupiter.api.Tag;
27+
import org.junit.jupiter.api.Test;
2428

2529
/**
26-
* Intended for marking a test case as flaky.
30+
* Annotation to mark a test as flaky.
31+
*
32+
* <p>Flaky tests are tests that produce inconsistent results,
33+
* occasionally failing or passing without changes to the code under test.
34+
* This could be due to external factors such as timing issues, resource contention,
35+
* dependency on non-deterministic data, or integration with external systems.
36+
*
37+
* <p>Tests marked with this annotation are excluded from execution
38+
* in CI pipelines and Maven commands by default, to ensure a reliable and
39+
* deterministic build process. However, they can still be executed manually
40+
* or in specific environments for debugging and resolution purposes.
41+
*
42+
* <p>Usage:
43+
* <pre>
44+
* {@code
45+
* @FlakyTest
46+
* public void testSomething() {
47+
* // Test logic here
48+
* }
49+
* }
50+
* </pre>
51+
*
52+
* <p>It is recommended to investigate and address the root causes of flaky tests
53+
* rather than relying on this annotation long-term.
2754
*/
2855
@Documented
29-
@Retention(RetentionPolicy.SOURCE)
56+
@Target({ ElementType.TYPE, ElementType.METHOD })
57+
@Retention(RetentionPolicy.RUNTIME)
58+
@Tag("flaky")
59+
@Test
3060
public @interface FlakyTest {
3161

3262
/**

bookkeeper-server/src/test/java/org/apache/bookkeeper/bookie/BookieStorageThresholdTest.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ public void setUp() throws Exception {
8686

8787
LedgerHandle[] prepareData(int numEntryLogs) throws Exception {
8888
// since an entry log file can hold at most 100 entries
89-
// first ledger write 2 entries, which is less than low water mark
89+
// first ledger write 2 entries, which is less than low watermark
9090
int num1 = 2;
91-
// third ledger write more than high water mark entries
91+
// third ledger write more than high watermark entries
9292
int num3 = (int) (NUM_ENTRIES * 0.7f);
93-
// second ledger write remaining entries, which is higher than low water
94-
// mark and less than high water mark
93+
// second ledger write remaining entries, which is higher than low watermark
94+
// and less than high watermark
9595
int num2 = NUM_ENTRIES - num3 - num1;
9696

9797
LedgerHandle[] lhs = new LedgerHandle[3];
@@ -148,8 +148,8 @@ public void testStorageThresholdCompaction() throws Exception {
148148
File ledgerDir2 = tmpDirs.createNew("ledger", "test2");
149149
File journalDir = tmpDirs.createNew("journal", "test");
150150
String[] ledgerDirNames = new String[]{
151-
ledgerDir1.getPath(),
152-
ledgerDir2.getPath()
151+
ledgerDir1.getPath(),
152+
ledgerDir2.getPath()
153153
};
154154
conf.setLedgerDirNames(ledgerDirNames);
155155
conf.setJournalDirName(journalDir.getPath());
@@ -224,7 +224,7 @@ public void diskFull(File disk) {
224224
// there are no writableLedgerDirs
225225
for (File ledgerDir : bookie.getLedgerDirsManager().getAllLedgerDirs()) {
226226
assertFalse("Found entry log file ([0,1,2].log. They should have been compacted" + ledgerDir,
227-
TestUtils.hasLogFiles(ledgerDir.getParentFile(), true, 0, 1, 2));
227+
TestUtils.hasLogFiles(ledgerDir.getParentFile(), true, 0, 1, 2));
228228
}
229229

230230
try {

bookkeeper-server/src/test/java/org/apache/bookkeeper/client/BookKeeperDiskSpaceWeightedLedgerPlacementTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public void testDiskSpaceWeightedBookieSelection() throws Exception {
179179

180180
/**
181181
* Test to show that weight based selection honors the disk weight of bookies and also adapts
182-
* when the bookies's weight changes.
182+
* when the bookies' weight changes.
183183
*/
184184
@FlakyTest("https://github.com/apache/bookkeeper/issues/503")
185185
public void testDiskSpaceWeightedBookieSelectionWithChangingWeights() throws Exception {

bookkeeper-server/src/test/java/org/apache/bookkeeper/replication/BookieAutoRecoveryTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ public void testNoSuchLedgerExists() throws Exception {
375375
}
376376

377377
/**
378-
* Test that if a empty ledger loses the bookie not in the quorum for entry 0, it will
378+
* Test that if an empty ledger loses the bookie not in the quorum for entry 0, it will
379379
* still be openable when it loses enough bookies to lose a whole quorum.
380380
*/
381381
@Test

pom.xml

+3
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,9 @@
918918
<groupId>org.apache.maven.plugins</groupId>
919919
<artifactId>maven-surefire-plugin</artifactId>
920920
<version>${maven-surefire-plugin.version}</version>
921+
<configuration>
922+
<excludedGroups>flaky</excludedGroups>
923+
</configuration>
921924
</plugin>
922925
<plugin>
923926
<groupId>org.apache.maven.plugins</groupId>

stream/distributedlog/core/src/test/java/org/apache/distributedlog/TestDistributedLogBase.java

+25
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
import org.junit.Before;
6262
import org.junit.BeforeClass;
6363
import org.junit.Rule;
64+
import org.junit.jupiter.api.AfterAll;
65+
import org.junit.jupiter.api.AfterEach;
66+
import org.junit.jupiter.api.BeforeAll;
67+
import org.junit.jupiter.api.BeforeEach;
68+
import org.junit.jupiter.api.TestInfo;
69+
import org.junit.rules.TestName;
6470
import org.junit.rules.Timeout;
6571
import org.slf4j.Logger;
6672
import org.slf4j.LoggerFactory;
@@ -72,6 +78,9 @@
7278
public class TestDistributedLogBase {
7379
static final Logger LOG = LoggerFactory.getLogger(TestDistributedLogBase.class);
7480

81+
@Rule
82+
public final TestName runtime = new TestName();
83+
7584
@Rule
7685
public Timeout globalTimeout = Timeout.seconds(120);
7786

@@ -105,7 +114,20 @@ public class TestDistributedLogBase {
105114
protected static int zkPort;
106115
protected static final List<File> TMP_DIRS = new ArrayList<File>();
107116

117+
protected String testName;
118+
119+
@Before
120+
public void setTestNameJunit4() {
121+
testName = runtime.getMethodName();
122+
}
123+
124+
@BeforeEach
125+
void setTestNameJunit5(TestInfo testInfo) {
126+
testName = testInfo.getDisplayName();
127+
}
128+
108129
@BeforeClass
130+
@BeforeAll
109131
public static void setupCluster() throws Exception {
110132
setupCluster(numBookies);
111133
}
@@ -134,6 +156,7 @@ public void uncaughtException(Thread t, Throwable e) {
134156
}
135157

136158
@AfterClass
159+
@AfterAll
137160
public static void teardownCluster() throws Exception {
138161
bkutil.teardown();
139162
zks.stop();
@@ -143,6 +166,7 @@ public static void teardownCluster() throws Exception {
143166
}
144167

145168
@Before
169+
@BeforeEach
146170
public void setup() throws Exception {
147171
try {
148172
zkc = LocalDLMEmulator.connectZooKeeper("127.0.0.1", zkPort);
@@ -153,6 +177,7 @@ public void setup() throws Exception {
153177
}
154178

155179
@After
180+
@AfterEach
156181
public void teardown() throws Exception {
157182
if (null != zkc) {
158183
zkc.close();

stream/distributedlog/core/src/test/java/org/apache/distributedlog/bk/TestLedgerAllocator.java

+42-32
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818
package org.apache.distributedlog.bk;
1919

2020
import static java.nio.charset.StandardCharsets.UTF_8;
21-
import static org.junit.Assert.assertEquals;
22-
import static org.junit.Assert.assertTrue;
23-
import static org.junit.Assert.fail;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import static org.junit.jupiter.api.Assertions.fail;
2424

2525
import java.net.URI;
2626
import java.util.Enumeration;
2727
import java.util.HashSet;
2828
import java.util.Map;
2929
import java.util.Set;
3030
import java.util.concurrent.CompletableFuture;
31+
import java.util.concurrent.TimeUnit;
3132
import org.apache.bookkeeper.client.BKException;
3233
import org.apache.bookkeeper.client.BookKeeper;
3334
import org.apache.bookkeeper.client.LedgerEntry;
@@ -53,17 +54,18 @@
5354
import org.apache.zookeeper.Op;
5455
import org.apache.zookeeper.ZooDefs;
5556
import org.apache.zookeeper.data.Stat;
56-
import org.junit.After;
57-
import org.junit.Before;
58-
import org.junit.Rule;
59-
import org.junit.Test;
60-
import org.junit.rules.TestName;
57+
import org.junit.jupiter.api.AfterAll;
58+
import org.junit.jupiter.api.BeforeAll;
59+
import org.junit.jupiter.api.Test;
60+
import org.junit.jupiter.api.TestInstance;
61+
import org.junit.jupiter.api.Timeout;
6162
import org.slf4j.Logger;
6263
import org.slf4j.LoggerFactory;
6364

6465
/**
6566
* TestLedgerAllocator.
6667
*/
68+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
6769
public class TestLedgerAllocator extends TestDistributedLogBase {
6870

6971
private static final Logger logger = LoggerFactory.getLogger(TestLedgerAllocator.class);
@@ -81,9 +83,6 @@ public void onAbort(Throwable t) {
8183
}
8284
};
8385

84-
@Rule
85-
public TestName runtime = new TestName();
86-
8786
private ZooKeeperClient zkc;
8887
private BookKeeperClient bkc;
8988
private DistributedLogConfiguration dlConf = new DistributedLogConfiguration();
@@ -92,7 +91,7 @@ private URI createURI(String path) {
9291
return URI.create("distributedlog://" + zkServers + path);
9392
}
9493

95-
@Before
94+
@BeforeAll
9695
public void setup() throws Exception {
9796
zkc = TestZooKeeperClientBuilder.newBuilder()
9897
.uri(createURI("/"))
@@ -102,7 +101,7 @@ public void setup() throws Exception {
102101
.dlConfig(dlConf).ledgersPath(ledgersPath).zkc(zkc).build();
103102
}
104103

105-
@After
104+
@AfterAll
106105
public void teardown() throws Exception {
107106
bkc.close();
108107
zkc.close();
@@ -164,7 +163,8 @@ public void testAllocation() throws Exception {
164163
Utils.close(allocator);
165164
}
166165

167-
@Test(timeout = 60000)
166+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
167+
@Test
168168
public void testBadVersionOnTwoAllocators() throws Exception {
169169
String allocationPath = "/allocation-bad-version";
170170
zkc.get().create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
@@ -173,9 +173,11 @@ public void testBadVersionOnTwoAllocators() throws Exception {
173173
Versioned<byte[]> allocationData = new Versioned<byte[]>(data, new LongVersion(stat.getVersion()));
174174

175175
SimpleLedgerAllocator allocator1 =
176-
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
176+
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf),
177+
zkc, bkc);
177178
SimpleLedgerAllocator allocator2 =
178-
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
179+
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf),
180+
zkc, bkc);
179181
allocator1.allocate();
180182
// wait until allocated
181183
ZKTransaction txn1 = newTxn();
@@ -206,7 +208,8 @@ public void testBadVersionOnTwoAllocators() throws Exception {
206208
assertEquals(1, i);
207209
}
208210

209-
@Test(timeout = 60000)
211+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
212+
@Test
210213
public void testAllocatorWithoutEnoughBookies() throws Exception {
211214
String allocationPath = "/allocator-without-enough-bookies";
212215

@@ -230,17 +233,18 @@ public void testAllocatorWithoutEnoughBookies() throws Exception {
230233
assertEquals(0, data.length);
231234
}
232235

233-
@Test(timeout = 60000)
234-
public void testSuccessAllocatorShouldDeleteUnusedledger() throws Exception {
236+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
237+
@Test
238+
public void testSuccessAllocatorShouldDeleteUnusedLedger() throws Exception {
235239
String allocationPath = "/allocation-delete-unused-ledger";
236240
zkc.get().create(allocationPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
237241
Stat stat = new Stat();
238242
byte[] data = zkc.get().getData(allocationPath, false, stat);
239243

240244
Versioned<byte[]> allocationData = new Versioned<byte[]>(data, new LongVersion(stat.getVersion()));
241245

242-
SimpleLedgerAllocator allocator1 =
243-
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
246+
SimpleLedgerAllocator allocator1 = new SimpleLedgerAllocator(allocationPath, allocationData,
247+
newQuorumConfigProvider(dlConf), zkc, bkc);
244248
allocator1.allocate();
245249
// wait until allocated
246250
ZKTransaction txn1 = newTxn();
@@ -250,8 +254,8 @@ public void testSuccessAllocatorShouldDeleteUnusedledger() throws Exception {
250254
stat = new Stat();
251255
data = zkc.get().getData(allocationPath, false, stat);
252256
allocationData = new Versioned<byte[]>(data, new LongVersion(stat.getVersion()));
253-
SimpleLedgerAllocator allocator2 =
254-
new SimpleLedgerAllocator(allocationPath, allocationData, newQuorumConfigProvider(dlConf), zkc, bkc);
257+
SimpleLedgerAllocator allocator2 = new SimpleLedgerAllocator(allocationPath, allocationData,
258+
newQuorumConfigProvider(dlConf), zkc, bkc);
255259
allocator2.allocate();
256260
// wait until allocated
257261
ZKTransaction txn2 = newTxn();
@@ -296,7 +300,8 @@ public void testSuccessAllocatorShouldDeleteUnusedledger() throws Exception {
296300
assertEquals(1, i);
297301
}
298302

299-
@Test(timeout = 60000)
303+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
304+
@Test
300305
public void testCloseAllocatorDuringObtaining() throws Exception {
301306
String allocationPath = "/allocation2";
302307
SimpleLedgerAllocator allocator = createAllocator(allocationPath);
@@ -329,7 +334,8 @@ public void testCloseAllocatorAfterConfirm() throws Exception {
329334
dlConf.getBKDigestPW().getBytes(UTF_8));
330335
}
331336

332-
@Test(timeout = 60000)
337+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
338+
@Test
333339
public void testCloseAllocatorAfterAbort() throws Exception {
334340
String allocationPath = "/allocation3";
335341
SimpleLedgerAllocator allocator = createAllocator(allocationPath);
@@ -352,9 +358,10 @@ public void testCloseAllocatorAfterAbort() throws Exception {
352358
dlConf.getBKDigestPW().getBytes(UTF_8));
353359
}
354360

355-
@Test(timeout = 60000)
361+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
362+
@Test
356363
public void testConcurrentAllocation() throws Exception {
357-
String allocationPath = "/" + runtime.getMethodName();
364+
String allocationPath = "/" + testName;
358365
SimpleLedgerAllocator allocator = createAllocator(allocationPath);
359366
allocator.allocate();
360367
ZKTransaction txn1 = newTxn();
@@ -365,15 +372,17 @@ public void testConcurrentAllocation() throws Exception {
365372
assertTrue(obtainFuture2.isCompletedExceptionally());
366373
try {
367374
Utils.ioResult(obtainFuture2);
368-
fail("Should fail the concurrent obtain since there is already a transaction obtaining the ledger handle");
375+
fail("Should fail the concurrent obtain since there is "
376+
+ "already a transaction obtaining the ledger handle");
369377
} catch (SimpleLedgerAllocator.ConcurrentObtainException cbe) {
370378
// expected
371379
}
372380
}
373381

374-
@Test(timeout = 60000)
382+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
383+
@Test
375384
public void testObtainMultipleLedgers() throws Exception {
376-
String allocationPath = "/" + runtime.getMethodName();
385+
String allocationPath = "/" + testName;
377386
SimpleLedgerAllocator allocator = createAllocator(allocationPath);
378387
int numLedgers = 10;
379388
Set<LedgerHandle> allocatedLedgers = new HashSet<LedgerHandle>();
@@ -387,9 +396,10 @@ public void testObtainMultipleLedgers() throws Exception {
387396
assertEquals(numLedgers, allocatedLedgers.size());
388397
}
389398

390-
@Test(timeout = 60000)
399+
@Timeout(value = 60, unit = TimeUnit.SECONDS)
400+
@Test
391401
public void testAllocationWithMetadata() throws Exception {
392-
String allocationPath = "/" + runtime.getMethodName();
402+
String allocationPath = "/" + testName;
393403

394404
String application = "testApplicationMetadata";
395405
String component = "testComponentMetadata";

0 commit comments

Comments
 (0)