Skip to content

Commit 04905f4

Browse files
committed
test: restore shared state and clean up workers on failure
- Restore EffectiveCheckService's original executor in finally - Stop and join SolidityNode test workers before restoring shared state - Isolate BlockEventCache and restore it after history workers terminate - Preserve interrupt status while waiting for worker cleanup
1 parent 7efa49c commit 04905f4

3 files changed

Lines changed: 65 additions & 21 deletions

File tree

‎framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java‎

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import static org.mockito.Mockito.verify;
66
import static org.mockito.Mockito.when;
77

8+
import com.google.common.util.concurrent.Uninterruptibles;
9+
import java.util.LinkedHashMap;
10+
import java.util.Map;
11+
import java.util.concurrent.ConcurrentHashMap;
812
import java.util.concurrent.CountDownLatch;
913
import java.util.concurrent.TimeUnit;
1014
import org.junit.After;
@@ -17,6 +21,7 @@
1721
import org.tron.core.ChainBaseManager;
1822
import org.tron.core.capsule.BlockCapsule.BlockId;
1923
import org.tron.core.db.Manager;
24+
import org.tron.core.services.event.BlockEventCache;
2025
import org.tron.core.services.event.BlockEventGet;
2126
import org.tron.core.services.event.BlockEventLoad;
2227
import org.tron.core.services.event.HistoryEventService;
@@ -35,9 +40,16 @@ public class HistoryEventServiceTest {
3540
private final BlockEventGet get = mock(BlockEventGet.class);
3641
private final ChainBaseManager chain = mock(ChainBaseManager.class);
3742
private final DynamicPropertiesStore properties = mock(DynamicPropertiesStore.class);
43+
private final Map<String, Object> savedCacheState = new LinkedHashMap<>();
3844

3945
@Before
4046
public void setUp() {
47+
for (String field : new String[]{"solidNum", "head", "solidId", "blockEventMap", "numMap"}) {
48+
savedCacheState.put(field, ReflectionTestUtils.getField(BlockEventCache.class, field));
49+
}
50+
// init() clears both maps, so use test-owned maps to preserve the original contents.
51+
ReflectionTestUtils.setField(BlockEventCache.class, "blockEventMap", new ConcurrentHashMap<>());
52+
ReflectionTestUtils.setField(BlockEventCache.class, "numMap", new ConcurrentHashMap<>());
4153
Manager manager = mock(Manager.class);
4254
when(manager.getChainBaseManager()).thenReturn(chain);
4355
when(manager.getDynamicPropertiesStore()).thenReturn(properties);
@@ -52,9 +64,20 @@ public void setUp() {
5264

5365
@After
5466
public void tearDown() {
55-
service.close();
56-
Thread worker = (Thread) ReflectionTestUtils.getField(service, "thread");
57-
Assert.assertTrue("History worker did not terminate", worker == null || !worker.isAlive());
67+
try {
68+
service.close();
69+
} finally {
70+
Thread worker = (Thread) ReflectionTestUtils.getField(service, "thread");
71+
if (worker != null) {
72+
worker.interrupt();
73+
Uninterruptibles.joinUninterruptibly(worker, 5, TimeUnit.SECONDS);
74+
Assert.assertFalse("History worker did not terminate", worker.isAlive());
75+
}
76+
// Restore only after the worker can no longer change the shared cache.
77+
savedCacheState.forEach((field, value) ->
78+
ReflectionTestUtils.setField(BlockEventCache.class, field, value));
79+
savedCacheState.clear();
80+
}
5881
}
5982

6083
@Test

‎framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,21 @@ public void testFind() {
7272
ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class);
7373
Mockito.when(executor.submit(Mockito.any(Runnable.class)))
7474
.thenReturn(CompletableFuture.completedFuture(null));
75-
ReflectUtils.setFieldValue(service, "executor", executor);
76-
service.triggerNext();
77-
ArgumentCaptor<Runnable> task = ArgumentCaptor.forClass(Runnable.class);
78-
Mockito.verify(executor).submit(task.capture());
79-
task.getValue().run();
80-
Assert.assertNull(service.getCur());
75+
ScheduledExecutorService originalExecutor = ReflectUtils.getFieldValue(service, "executor");
76+
try {
77+
ReflectUtils.setFieldValue(service, "executor", executor);
78+
service.triggerNext();
79+
ArgumentCaptor<Runnable> task = ArgumentCaptor.forClass(Runnable.class);
80+
Mockito.verify(executor).submit(task.capture());
81+
task.getValue().run();
82+
Assert.assertNull(service.getCur());
8183

82-
ReflectUtils.invokeMethod(service, "resetCount");
83-
InetSocketAddress cur = new InetSocketAddress("192.168.0.1", port);
84-
service.setCur(cur);
85-
service.onDisconnect(cur);
84+
ReflectUtils.invokeMethod(service, "resetCount");
85+
InetSocketAddress cur = new InetSocketAddress("192.168.0.1", port);
86+
service.setCur(cur);
87+
service.onDisconnect(cur);
88+
} finally {
89+
ReflectUtils.setFieldValue(service, "executor", originalExecutor);
90+
}
8691
}
8792
}

‎framework/src/test/java/org/tron/program/SolidityNodeTest.java‎

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.junit.Assert.assertTrue;
88
import static org.mockito.Mockito.mock;
99

10+
import com.google.common.util.concurrent.Uninterruptibles;
1011
import com.google.protobuf.ByteString;
1112
import java.lang.reflect.Field;
1213
import java.lang.reflect.InvocationTargetException;
@@ -551,6 +552,7 @@ public void testGetBlockProcessesOneBlock() throws Exception {
551552
@Test(timeout = 8000)
552553
@SuppressWarnings("unchecked")
553554
public void testGetBlockShutdownPaths() throws Exception {
555+
boolean origFlag = getFlag();
554556
long origID = atomicLong("ID").get();
555557
long origRemote = atomicLong("remoteBlockNum").get();
556558
Field clientField = getField("databaseGrpcClient");
@@ -560,6 +562,7 @@ public void testGetBlockShutdownPaths() throws Exception {
560562

561563
LinkedBlockingDeque<Block> queue =
562564
(LinkedBlockingDeque<Block>) getField("blockQueue").get(solidityNode);
565+
Thread worker = null;
563566
try {
564567
// ── Part 1: interrupt during blockQueue.put() ──────────────────────────
565568
// Fill the queue to capacity so the next put() call blocks.
@@ -581,18 +584,18 @@ public void testGetBlockShutdownPaths() throws Exception {
581584
Method getBlockM = SolidityNode.class.getDeclaredMethod("getBlock");
582585
getBlockM.setAccessible(true);
583586
AtomicReference<Throwable> workerFailure = new AtomicReference<>();
584-
Thread t = new Thread(() -> {
587+
worker = new Thread(() -> {
585588
try {
586589
getBlockM.invoke(solidityNode);
587590
} catch (Exception e) {
588591
workerFailure.set(e);
589592
}
590593
});
591-
t.start();
594+
worker.start();
592595
Thread.sleep(200); // let the thread block inside blockQueue.put()
593-
t.interrupt(); // simulate ExecutorService.shutdownNow()
594-
t.join(4000);
595-
assertFalse("getBlock must exit cleanly when interrupted during put()", t.isAlive());
596+
worker.interrupt(); // simulate ExecutorService.shutdownNow()
597+
worker.join(4000);
598+
assertFalse("getBlock must exit cleanly when interrupted during put()", worker.isAlive());
596599
Assert.assertNull("getBlock worker failed", workerFailure.get());
597600
queue.clear();
598601
setFlag(true);
@@ -612,7 +615,8 @@ public void testGetBlockShutdownPaths() throws Exception {
612615
// Must return without throwing and without infinite retry.
613616
getBlockM.invoke(solidityNode);
614617
} finally {
615-
setFlag(true);
618+
stopWorker(worker);
619+
setFlag(origFlag);
616620
queue.clear();
617621
atomicLong("ID").set(origID);
618622
atomicLong("remoteBlockNum").set(origRemote);
@@ -668,12 +672,12 @@ public void testProcessSolidityBlockProcessesQueuedBlock() throws Exception {
668672
*/
669673
@Test(timeout = 8000)
670674
public void testProcessSolidityBlockHandlesInterrupt() throws Exception {
675+
boolean origFlag = getFlag();
671676
TronNetDelegate mockDelegate = mock(TronNetDelegate.class);
672677
Mockito.when(mockDelegate.isHitDown()).thenReturn(false);
673678

674679
Field delegateField = getField("tronNetDelegate");
675680
Object origDelegate = delegateField.get(solidityNode);
676-
delegateField.set(solidityNode, mockDelegate);
677681

678682
Method m = SolidityNode.class.getDeclaredMethod("processSolidityBlock");
679683
m.setAccessible(true);
@@ -686,20 +690,32 @@ public void testProcessSolidityBlockHandlesInterrupt() throws Exception {
686690
}
687691
});
688692
try {
693+
delegateField.set(solidityNode, mockDelegate);
689694
t.start();
690695
Thread.sleep(150); // let the thread enter blockQueue.poll(1000 ms)
691696
t.interrupt();
692697
t.join(5000);
693698
assertFalse("processSolidityBlock must exit after interrupt", t.isAlive());
694699
Assert.assertNull("processSolidityBlock worker failed", workerFailure.get());
695700
} finally {
696-
setFlag(true);
701+
stopWorker(t);
702+
setFlag(origFlag);
697703
delegateField.set(solidityNode, origDelegate);
698704
}
699705
}
700706

701707
// ── private helpers ──────────────────────────────────────────────────────────
702708

709+
private void stopWorker(Thread worker) throws Exception {
710+
// A timeout may interrupt the test thread before it reaches the normal shutdown path.
711+
setFlag(false);
712+
if (worker != null) {
713+
worker.interrupt();
714+
Uninterruptibles.joinUninterruptibly(worker, 5, TimeUnit.SECONDS);
715+
assertFalse("Test worker must stop before restoring shared state", worker.isAlive());
716+
}
717+
}
718+
703719
private static Field getField(String name) throws Exception {
704720
Field f = SolidityNode.class.getDeclaredField(name);
705721
f.setAccessible(true);

0 commit comments

Comments
 (0)