From 29355602674f53f3934a5ab223b6a4bf5b85bd99 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 16:23:02 +0800 Subject: [PATCH 01/13] test(framework): fix test quality issues in framework unit tests - FetchInvDataMsgHandlerTest: add Assert.fail() before catch blocks so tests fail when expected exceptions are not thrown; fix assertEquals argument order (expected, actual) - ConcurrentHashMapTest: restore commented-out Assert.fail() for ItemNotFoundException; replace printStackTrace() with interrupt flag restoration and proper logging; fail main thread on interrupted join - InventoryMsgHandlerTest: add Mockito.verify() to assert isBlockUnsolidified() is called in the unsolidified-block scenario - PeerStatusCheckMockTest: add Mockito.verify() to assert statusCheck() is invoked by the scheduler after init() - MerkleTreeTest: replace Assert.assertFalse(true) with Assert.fail() for clearer failure message Co-Authored-By: Claude Sonnet 4.6 --- .../core/capsule/utils/MerkleTreeTest.java | 2 +- .../core/jsonrpc/ConcurrentHashMapTest.java | 25 +++++++++++-------- .../FetchInvDataMsgHandlerTest.java | 8 ++++-- .../InventoryMsgHandlerTest.java | 1 + .../net/peer/PeerStatusCheckMockTest.java | 7 +++++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java index df84433726e..247a52e648f 100644 --- a/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java +++ b/framework/src/test/java/org/tron/core/capsule/utils/MerkleTreeTest.java @@ -103,7 +103,7 @@ public void test0HashNum() { List hashList = getHash(0); //Empty list. try { MerkleTree.getInstance().createTree(hashList); - Assert.assertFalse(true); + Assert.fail("Expected IndexOutOfBoundsException for empty hash list"); } catch (Exception e) { Assert.assertTrue(e instanceof IndexOutOfBoundsException); } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index abb73671161..1c11a2a35dd 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -52,7 +52,8 @@ public void testHandleBlockHash() { try { Thread.sleep(200); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + Assert.fail("Interrupted during test setup: " + e.getMessage()); } Thread putThread = new Thread(new Runnable() { @@ -69,7 +70,8 @@ public void run() { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + logger.error("putThread interrupted", e); } } } @@ -83,7 +85,8 @@ public void run() { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + logger.error("getThread1 interrupted", e); } logger.info("Thread1 get time {}", t); @@ -98,8 +101,7 @@ public void run() { } } catch (ItemNotFoundException e) { - e.printStackTrace(); - // Assert.fail(e.getMessage()); + Assert.fail("Filter ID should always exist: " + e.getMessage()); } } } @@ -113,7 +115,8 @@ public void run() { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + logger.error("getThread2 interrupted", e); } logger.info("Thread2 get time {}", t); @@ -132,7 +135,7 @@ public void run() { } } catch (ItemNotFoundException e) { - // Assert.fail(e.getMessage()); + Assert.fail("Filter ID should always exist: " + e.getMessage()); } } } @@ -146,7 +149,8 @@ public void run() { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + logger.error("getThread3 interrupted", e); } logger.info("Thread3 get time {}", t); @@ -166,7 +170,7 @@ public void run() { } } catch (ItemNotFoundException e) { - // Assert.fail(e.getMessage()); + Assert.fail("Filter ID should always exist: " + e.getMessage()); } } } @@ -184,7 +188,8 @@ public void run() { getThread2.join(); getThread3.join(); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + Assert.fail("Main thread interrupted while waiting for worker threads: " + e.getMessage()); } logger.info("-----------------------------------------------------------------------"); diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java index 270002fffba..f8677b6bc04 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/FetchInvDataMsgHandlerTest.java @@ -115,16 +115,18 @@ public void testSyncFetchCheck() { Mockito.when(peer.getLastSyncBlockId()) .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 1000L)); fetchInvDataMsgHandler.processMessage(peer, msg); + Assert.fail("Expected exception was not thrown: maxBlockNum: 1000, blockNum: 10000"); } catch (Exception e) { - Assert.assertEquals(e.getMessage(), "maxBlockNum: 1000, blockNum: 10000"); + Assert.assertEquals("maxBlockNum: 1000, blockNum: 10000", e.getMessage()); } try { Mockito.when(peer.getLastSyncBlockId()) .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 20000L)); fetchInvDataMsgHandler.processMessage(peer, msg); + Assert.fail("Expected exception was not thrown: minBlockNum: 16000, blockNum: 10000"); } catch (Exception e) { - Assert.assertEquals(e.getMessage(), "minBlockNum: 16000, blockNum: 10000"); + Assert.assertEquals("minBlockNum: 16000, blockNum: 10000", e.getMessage()); } } @@ -150,11 +152,13 @@ public void testRateLimiter() { try { fetchInvDataMsgHandler.processMessage(peer, msg); + Assert.fail("Expected exception was not thrown: fetch too many blocks, size:101"); } catch (Exception e) { Assert.assertEquals("fetch too many blocks, size:101", e.getMessage()); } try { fetchInvDataMsgHandler.processMessage(peer, msg); + Assert.fail("Expected rate limit exception was not thrown"); } catch (Exception e) { Assert.assertTrue(e.getMessage().endsWith("rate limit")); } diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java index 338b44e6699..1dbf7c7150f 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/InventoryMsgHandlerTest.java @@ -49,6 +49,7 @@ public void testProcessMessage() throws Exception { field.set(handler, tronNetDelegate); handler.processMessage(peer, msg); + Mockito.verify(tronNetDelegate, Mockito.atLeastOnce()).isBlockUnsolidified(); } private Channel getChannel(String host, int port) throws Exception { diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java index 80b1abdc35d..c518e22280b 100644 --- a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java +++ b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java @@ -4,6 +4,7 @@ import static org.mockito.Mockito.spy; import org.junit.After; +import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; @@ -19,8 +20,12 @@ public void testInitException() throws InterruptedException { doThrow(new RuntimeException("test exception")).when(peerStatusCheck).statusCheck(); peerStatusCheck.init(); - // the initialDelay of scheduleWithFixedDelay is 5s + // the initialDelay of scheduleWithFixedDelay is 5s; wait for at least one execution Thread.sleep(5000L); + + // Verify statusCheck() was invoked by the scheduler and the exception was handled gracefully + Mockito.verify(peerStatusCheck, Mockito.atLeastOnce()).statusCheck(); + Assert.assertNotNull(peerStatusCheck); } } \ No newline at end of file From 4350c4828e025a5f3071bb32558a0f08b53386d8 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 16:33:52 +0800 Subject: [PATCH 02/13] test(plugins): fix test quality issues in plugins unit tests - DbLiteTest: remove dead null-dbPath reassignment before init() call; restore thread interrupt flag on InterruptedException in generateSomeTransactions() - DbMoveTest: remove legacy @After destroy() that cleaned a hardcoded path never written by any test; remove dead deleteDir() helper and OUTPUT_DIRECTORY constant; remove now-unused After import - DbTest: narrow INPUT_DIRECTORY and cli fields from public to protected - ArchiveManifestTest, DbArchiveTest: remove copy-pasted writeProperty() and deleteDir() private static methods that were never called; clean up unused imports - DbLiteRocksDbV2Test: rename testToolsWithRocksDB to testToolsWithRocksDbV2 to match the LevelDB v1/v2 naming convention Note: testMvForRocksDB is a pre-existing failure unrelated to these changes. Co-Authored-By: Claude Sonnet 4.6 --- .../java/org/tron/plugins/DbLiteTest.java | 4 +- .../java/org/tron/plugins/DbMoveTest.java | 27 ----------- .../test/java/org/tron/plugins/DbTest.java | 4 +- .../plugins/leveldb/ArchiveManifestTest.java | 46 ------------------- .../tron/plugins/leveldb/DbArchiveTest.java | 46 ------------------- .../plugins/rocksdb/DbLiteRocksDbV2Test.java | 2 +- 6 files changed, 5 insertions(+), 124 deletions(-) diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java index 60db838a80d..07bddc461a0 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java @@ -90,7 +90,6 @@ public void clear() { public void testTools(String dbType, int checkpointVersion) throws InterruptedException, IOException { logger.info("dbType {}, checkpointVersion {}", dbType, checkpointVersion); - dbPath = String.format("%s_%s_%d", dbPath, dbType, System.currentTimeMillis()); init(dbType); final String[] argsForSnapshot = new String[] {"-o", "split", "-t", "snapshot", "--fn-data-path", @@ -166,7 +165,8 @@ private void generateSomeTransactions(int during) { try { Thread.sleep(sleepOnce); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); + return; } if ((runTime += sleepOnce) > during) { return; diff --git a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java index 5b25739f272..6df17c4251c 100644 --- a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java @@ -5,7 +5,6 @@ import java.net.URL; import java.nio.file.Paths; import lombok.extern.slf4j.Slf4j; -import org.junit.After; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -18,8 +17,6 @@ @Slf4j public class DbMoveTest { - private static final String OUTPUT_DIRECTORY = "output-directory-toolkit"; - @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -31,30 +28,6 @@ private void init(DbTool.DbType dbType, String path) throws IOException, RocksDB DbTool.getDB(path, ACCOUNT, dbType).close(); DbTool.getDB(path, DBUtils.MARKET_PAIR_PRICE_TO_ORDER, dbType).close(); DbTool.getDB(path, TRANS, dbType).close(); - - } - - @After - public void destroy() { - deleteDir(new File(OUTPUT_DIRECTORY)); - } - - /** - * delete directory. - */ - private static boolean deleteDir(File dir) { - if (dir.isDirectory()) { - String[] children = dir.list(); - assert children != null; - for (String child : children) { - boolean success = deleteDir(new File(dir, child)); - if (!success) { - logger.warn("can't delete dir:" + dir); - return false; - } - } - } - return dir.delete(); } private static String getConfig(String config) { diff --git a/plugins/src/test/java/org/tron/plugins/DbTest.java b/plugins/src/test/java/org/tron/plugins/DbTest.java index bbcc1a0bbf7..d22addfbae8 100644 --- a/plugins/src/test/java/org/tron/plugins/DbTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbTest.java @@ -18,10 +18,10 @@ public class DbTest { - public String INPUT_DIRECTORY; + protected String INPUT_DIRECTORY; private static final String ACCOUNT = "account"; private static final String MARKET = DBUtils.MARKET_PAIR_PRICE_TO_ORDER; - public CommandLine cli = new CommandLine(new Toolkit()); + protected CommandLine cli = new CommandLine(new Toolkit()); @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); diff --git a/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java index f5880d82e39..ff73eca25cc 100644 --- a/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/ArchiveManifestTest.java @@ -1,16 +1,7 @@ package org.tron.plugins.leveldb; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.Properties; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -82,41 +73,4 @@ public void testEmpty() { Assert.assertEquals(0, ArchiveManifest.run(args)); } - private static void writeProperty(String filename, String key, String value) throws IOException { - File file = new File(filename); - if (!file.exists()) { - file.createNewFile(); - } - - try (FileInputStream fis = new FileInputStream(file); - OutputStream out = new FileOutputStream(file); - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, - StandardCharsets.UTF_8))) { - BufferedReader bf = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8)); - Properties properties = new Properties(); - properties.load(bf); - properties.setProperty(key, value); - properties.store(bw, "Generated by the application. PLEASE DO NOT EDIT! "); - } catch (Exception e) { - logger.warn("{}", e); - } - } - - /** - * delete directory. - */ - private static boolean deleteDir(File dir) { - if (dir.isDirectory()) { - String[] children = dir.list(); - assert children != null; - for (String child : children) { - boolean success = deleteDir(new File(dir, child)); - if (!success) { - logger.warn("can't delete dir:" + dir); - return false; - } - } - } - return dir.delete(); - } } diff --git a/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java b/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java index 69dca01e4f8..8c20c2b55be 100644 --- a/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java +++ b/plugins/src/test/java/org/tron/plugins/leveldb/DbArchiveTest.java @@ -1,16 +1,7 @@ package org.tron.plugins.leveldb; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.util.Properties; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -88,41 +79,4 @@ public void testEmpty() { Assert.assertEquals(0, cli.execute(args)); } - private static void writeProperty(String filename, String key, String value) throws IOException { - File file = new File(filename); - if (!file.exists()) { - file.createNewFile(); - } - - try (FileInputStream fis = new FileInputStream(file); - OutputStream out = new FileOutputStream(file); - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, - StandardCharsets.UTF_8))) { - BufferedReader bf = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8)); - Properties properties = new Properties(); - properties.load(bf); - properties.setProperty(key, value); - properties.store(bw, "Generated by the application. PLEASE DO NOT EDIT! "); - } catch (Exception e) { - logger.warn("{}", e); - } - } - - /** - * delete directory. - */ - private static boolean deleteDir(File dir) { - if (dir.isDirectory()) { - String[] children = dir.list(); - assert children != null; - for (String child : children) { - boolean success = deleteDir(new File(dir, child)); - if (!success) { - logger.warn("can't delete dir:" + dir); - return false; - } - } - } - return dir.delete(); - } } diff --git a/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java index ab1067fefc3..ebc4074ccc0 100644 --- a/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java +++ b/plugins/src/test/java/org/tron/plugins/rocksdb/DbLiteRocksDbV2Test.java @@ -7,7 +7,7 @@ public class DbLiteRocksDbV2Test extends DbLiteTest { @Test - public void testToolsWithRocksDB() throws InterruptedException, IOException { + public void testToolsWithRocksDbV2() throws InterruptedException, IOException { testTools("ROCKSDB", 2); } } From e43aec00e36c5e96eab37185b3a512db06635ae2 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 17:29:08 +0800 Subject: [PATCH 03/13] test(actuator): remove obsolete moreThanFrozenNumber test in FreezeBalanceActuatorTest The method tested a "max frozen number is: X" error that no longer exists in FreezeBalanceActuator. The actuator was updated to check "frozenCount must be 0 or 1" instead, and a second freeze with frozenCount=1 now succeeds rather than throwing. Remove the now-unused ChainConstant import. Co-Authored-By: Claude Sonnet 4.6 --- .../actuator/FreezeBalanceActuatorTest.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java index ddcb9976200..c830cd091e6 100644 --- a/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java @@ -22,7 +22,6 @@ import org.tron.core.capsule.DelegatedResourceAccountIndexCapsule; import org.tron.core.capsule.DelegatedResourceCapsule; import org.tron.core.capsule.TransactionResultCapsule; -import org.tron.core.config.Parameter.ChainConstant; import org.tron.core.config.args.Args; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractValidateException; @@ -621,35 +620,6 @@ public void frozenNumTest() { } } - //@Test - public void moreThanFrozenNumber() { - long frozenBalance = 1_000_000_000L; - long duration = 3; - FreezeBalanceActuator actuator = new FreezeBalanceActuator(); - actuator.setChainBaseManager(dbManager.getChainBaseManager()) - .setAny(getContractForBandwidth(OWNER_ADDRESS, frozenBalance, duration)); - - TransactionResultCapsule ret = new TransactionResultCapsule(); - try { - actuator.validate(); - actuator.execute(ret); - } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); - } - try { - actuator.validate(); - actuator.execute(ret); - fail("cannot run here."); - } catch (ContractValidateException e) { - long maxFrozenNumber = ChainConstant.MAX_FROZEN_NUMBER; - Assert.assertEquals("max frozen number is: " + maxFrozenNumber, e.getMessage()); - - } catch (ContractExeException e) { - Assert.fail(); - } - } - - @Test public void commonErrorCheck() { FreezeBalanceActuator actuator = new FreezeBalanceActuator(); From 485fbc215407eb1367426c568109c6c0f539486a Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 12:10:29 +0800 Subject: [PATCH 04/13] test(framework): fix test cleanup and solidity node shutdown --- .../java/org/tron/program/SolidityNode.java | 32 +++++++++++++++--- .../logsfilter/NativeMessageQueueTest.java | 27 ++++++++++++--- .../core/jsonrpc/SectionBloomStoreTest.java | 18 ++++++++-- .../tron/core/services/ComputeRewardTest.java | 7 ++++ .../ratelimiter/adaptor/AdaptorTest.java | 16 +++++---- .../ratelimiter/adaptor/AdaptorThread.java | 2 +- .../org/tron/program/SolidityNodeTest.java | 33 +++++++++++++++++++ 7 files changed, 117 insertions(+), 18 deletions(-) diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index 3367141e2a5..de0bdaea1de 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -2,7 +2,9 @@ import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL; +import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -11,6 +13,7 @@ import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.client.DatabaseGrpcClient; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.Metrics; import org.tron.core.ChainBaseManager; @@ -39,6 +42,9 @@ public class SolidityNode { private volatile boolean flag = true; + private ExecutorService getBlockEs; + private ExecutorService processBlockEs; + public SolidityNode(Manager dbManager) { this.dbManager = dbManager; this.chainBaseManager = dbManager.getChainBaseManager(); @@ -72,13 +78,25 @@ public static void start() { appT.startup(); SolidityNode node = new SolidityNode(appT.getDbManager()); node.run(); - appT.blockUntilShutdown(); + awaitShutdown(appT, node); } + static void awaitShutdown(Application appT, SolidityNode node) { + try { + appT.blockUntilShutdown(); + } finally { + // SolidityNode is created manually rather than managed by Spring/Application, + // so its executors must be shut down explicitly on exit. + node.shutdown(); + } + } + private void run() { try { - new Thread(this::getBlock).start(); - new Thread(this::processBlock).start(); + getBlockEs = ExecutorServiceManager.newSingleThreadExecutor("solid-get-block"); + processBlockEs = ExecutorServiceManager.newSingleThreadExecutor("solid-process-block"); + getBlockEs.execute(this::getBlock); + processBlockEs.execute(this::processBlock); logger.info("Success to start solid node, ID: {}, remoteBlockNum: {}.", ID.get(), remoteBlockNum); } catch (Exception e) { @@ -88,6 +106,12 @@ private void run() { } } + public void shutdown() { + flag = false; + ExecutorServiceManager.shutdownAndAwaitTermination(getBlockEs, "solid-get-block"); + ExecutorServiceManager.shutdownAndAwaitTermination(processBlockEs, "solid-process-block"); + } + private void getBlock() { long blockNum = ID.incrementAndGet(); while (flag) { @@ -193,4 +217,4 @@ private void resolveCompatibilityIssueIfUsingFullNodeDatabase() { chainBaseManager.getDynamicPropertiesStore().saveLatestSolidifiedBlockNum(headBlockNum); } } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java index d356e43d66c..a71b64b0a6d 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java @@ -1,7 +1,11 @@ package org.tron.common.logsfilter; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.junit.After; import org.junit.Assert; import org.junit.Test; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.logsfilter.nativequeue.NativeMessageQueue; import org.zeromq.SocketType; import org.zeromq.ZContext; @@ -13,6 +17,21 @@ public class NativeMessageQueueTest { public String dataToSend = "################"; public String topic = "testTopic"; + private ExecutorService subscriberExecutor; + + @After + public void tearDown() { + if (subscriberExecutor != null) { + subscriberExecutor.shutdownNow(); + try { + subscriberExecutor.awaitTermination(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + subscriberExecutor = null; + } + } + @Test public void invalidBindPort() { boolean bRet = NativeMessageQueue.getInstance().start(-1111, 0); @@ -39,7 +58,7 @@ public void publishTrigger() { try { Thread.sleep(1000); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); } NativeMessageQueue.getInstance().publishTrigger(dataToSend, topic); @@ -47,14 +66,15 @@ public void publishTrigger() { try { Thread.sleep(1000); } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); } NativeMessageQueue.getInstance().stop(); } public void startSubscribeThread() { - Thread thread = new Thread(() -> { + subscriberExecutor = ExecutorServiceManager.newSingleThreadExecutor("zmq-subscriber"); + subscriberExecutor.execute(() -> { try (ZContext context = new ZContext()) { ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); @@ -70,6 +90,5 @@ public void startSubscribeThread() { // ZMQ.Socket will be automatically closed when ZContext is closed } }); - thread.start(); } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java b/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java index d31f7a4f63d..39bcc30e278 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java @@ -4,12 +4,14 @@ import java.util.BitSet; import java.util.List; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import javax.annotation.Resource; +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.tron.common.BaseTest; import org.tron.common.TestConstants; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.LogInfo; import org.tron.common.utils.ByteArray; @@ -28,10 +30,22 @@ public class SectionBloomStoreTest extends BaseTest { @Resource SectionBloomStore sectionBloomStore; + private ExecutorService sectionExecutor; + static { Args.setParam(new String[] {"--output-directory", dbPath()}, TestConstants.TEST_CONF); } + @Before + public void setUp() { + sectionExecutor = ExecutorServiceManager.newFixedThreadPool("section-bloom-query", 5); + } + + @After + public void tearDown() { + ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, "section-bloom-query"); + } + @Test public void testPutAndGet() { BitSet bitSet = new BitSet(SectionBloomStore.BLOCK_PER_SECTION); @@ -126,7 +140,6 @@ public void testWriteAndQuery() { } long currentMaxBlockNum = 50000; - ExecutorService sectionExecutor = Executors.newFixedThreadPool(5); //query one address try { @@ -236,6 +249,5 @@ public void testWriteAndQuery() { Assert.fail(); } - sectionExecutor.shutdownNow(); } } diff --git a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java index 7617af2c1eb..a1bffd5bf1f 100644 --- a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java +++ b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java @@ -12,7 +12,9 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.Timeout; import org.tron.common.BaseMethodTest; import org.tron.common.error.TronDBException; import org.tron.common.es.ExecutorServiceManager; @@ -33,6 +35,11 @@ public class ComputeRewardTest extends BaseMethodTest { + // setUp() contains a 6-second sleep waiting for async reward calculation; + // 60 s total budget covers setup + test body with headroom for slow CI. + @Rule + public Timeout timeout = Timeout.seconds(60); + private static final byte[] OWNER_ADDRESS = ByteArray.fromHexString( "4105b9e8af8ee371cad87317f442d155b39fbd1bf0"); diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java index 72ac126e394..22d8d50483f 100644 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorTest.java @@ -3,9 +3,12 @@ import com.google.common.cache.Cache; import com.google.common.util.concurrent.RateLimiter; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.utils.ReflectUtils; import org.tron.core.services.ratelimiter.adapter.GlobalPreemptibleAdapter; import org.tron.core.services.ratelimiter.adapter.IPQPSRateLimiterAdapter; @@ -134,15 +137,16 @@ public void testQpsRateLimiterAdapter() { long t0 = System.currentTimeMillis(); CountDownLatch latch = new CountDownLatch(20); - for (int i = 0; i < 20; i++) { - Thread thread = new Thread(new AdaptorThread(latch, strategy)); - thread.start(); - } - + ExecutorService pool = ExecutorServiceManager.newFixedThreadPool("adaptor-test", 20); try { + for (int i = 0; i < 20; i++) { + pool.execute(new AdaptorThread(latch, strategy)); + } latch.await(); } catch (InterruptedException e) { - System.out.println(e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + ExecutorServiceManager.shutdownAndAwaitTermination(pool, "adaptor-test"); } long t1 = System.currentTimeMillis(); Assert.assertTrue(t1 - t0 > 4000); diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java index 4ffe732348e..afcf21e7510 100644 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/adaptor/AdaptorThread.java @@ -19,7 +19,7 @@ public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { - System.out.println(e.getMessage()); + Thread.currentThread().interrupt(); } latch.countDown(); } diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 7d94f813b80..91e3b93fa96 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -2,6 +2,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.doThrow; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; @@ -10,8 +13,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; +import org.mockito.InOrder; import org.tron.common.BaseTest; import org.tron.common.TestConstants; +import org.tron.common.application.Application; import org.tron.common.client.DatabaseGrpcClient; import org.tron.common.utils.PublicMethod; import org.tron.core.config.args.Args; @@ -89,4 +94,32 @@ public void testSolidityNodeHttpApiService() { solidityNodeHttpApiService.stop(); Assert.assertTrue(true); } + + @Test + public void testAwaitShutdownAlwaysStopsNode() { + Application app = mock(Application.class); + SolidityNode node = mock(SolidityNode.class); + + SolidityNode.awaitShutdown(app, node); + + InOrder inOrder = inOrder(app, node); + inOrder.verify(app).blockUntilShutdown(); + inOrder.verify(node).shutdown(); + } + + @Test + public void testAwaitShutdownStopsNodeWhenBlockedCallFails() { + Application app = mock(Application.class); + SolidityNode node = mock(SolidityNode.class); + RuntimeException expected = new RuntimeException("boom"); + doThrow(expected).when(app).blockUntilShutdown(); + + RuntimeException thrown = assertThrows(RuntimeException.class, + () -> SolidityNode.awaitShutdown(app, node)); + assertEquals(expected, thrown); + + InOrder inOrder = inOrder(app, node); + inOrder.verify(app).blockUntilShutdown(); + inOrder.verify(node).shutdown(); + } } From d20b279ea685bcd05725cf0fccdf5b1d910d1ba8 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 13:57:41 +0800 Subject: [PATCH 05/13] test(framework): replace bare Executors with ExecutorServiceManager and fix test quality issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace Executors.newFixedThreadPool / bare new Thread[] with ExecutorServiceManager in LogBlockQueryTest, TransactionRegisterTest, ConcurrentHashMapTest; add proper shutdownAndAwaitTermination - Fix InterruptedException handling: restore interrupt flag instead of swallowing in PropUtilTest, DBIteratorTest, SnapshotImplTest, ProposalControllerTest, BlockStoreTest, PbftTest - Remove dead/unreachable test code in FreezeTest, EventParserTest, EventParserJsonTest, BlockFilterCapsuleTest, MessageTest, RelayServiceTest, DelegationServiceTest - Fix package typo: rename keystroe.CredentialsTest → keystore.CredentialsTest; rewrite test using JUnit Assert and Mockito instead of junit.framework.TestCase - Minor cleanup: unused imports, whitespace, AccountAssetStoreTest assertion style Co-Authored-By: Claude Sonnet 4.6 --- .../logsfilter/EventParserJsonTest.java | 1 - .../common/logsfilter/EventParserTest.java | 1 - .../capsule/BlockFilterCapsuleTest.java | 1 - .../tron/common/runtime/vm/FreezeTest.java | 8 -- .../org/tron/common/utils/PropUtilTest.java | 6 +- .../tron/core/db/AccountAssetStoreTest.java | 2 +- .../java/org/tron/core/db/BlockStoreTest.java | 6 +- .../java/org/tron/core/db/DBIteratorTest.java | 59 ++++++-------- .../org/tron/core/db2/SnapshotImplTest.java | 3 +- .../core/jsonrpc/ConcurrentHashMapTest.java | 71 ++++++++-------- .../tron/core/jsonrpc/LogBlockQueryTest.java | 19 +++-- .../java/org/tron/core/net/MessageTest.java | 2 - .../core/net/services/RelayServiceTest.java | 1 - .../java/org/tron/core/pbft/PbftTest.java | 4 +- .../core/services/DelegationServiceTest.java | 1 - .../core/utils/TransactionRegisterTest.java | 44 ++++++---- .../core/witness/ProposalControllerTest.java | 8 +- .../org/tron/keystore/CredentialsTest.java | 81 +++++++++++++------ .../org/tron/keystroe/CredentialsTest.java | 33 -------- 19 files changed, 167 insertions(+), 184 deletions(-) delete mode 100644 framework/src/test/java/org/tron/keystroe/CredentialsTest.java diff --git a/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java b/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java index 34a8e82c424..9b0f17244a8 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java @@ -65,7 +65,6 @@ public synchronized void testEventParser() { for (int i = 0; i < entryArr.size(); i++) { JSONObject e = entryArr.getJSONObject(i); - System.out.println(e.getString("name")); if (e.getString("name") != null) { if (e.getString("name").equalsIgnoreCase("eventBytesL")) { entry = e; diff --git a/framework/src/test/java/org/tron/common/logsfilter/EventParserTest.java b/framework/src/test/java/org/tron/common/logsfilter/EventParserTest.java index eff644b9cd9..0d2a8ed1496 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/EventParserTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/EventParserTest.java @@ -68,7 +68,6 @@ public synchronized void testEventParser() { ABI.Entry entry = null; for (ABI.Entry e : abi.getEntrysList()) { - System.out.println(e.getName()); if (e.getName().equalsIgnoreCase("eventBytesL")) { entry = e; break; diff --git a/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java b/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java index 5381c6ab2de..b5f7e676eea 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java @@ -22,7 +22,6 @@ public void setUp() { public void testSetAndGetBlockHash() { blockFilterCapsule .setBlockHash("e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f"); - System.out.println(blockFilterCapsule); Assert.assertEquals("e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f", blockFilterCapsule.getBlockHash()); } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/FreezeTest.java b/framework/src/test/java/org/tron/common/runtime/vm/FreezeTest.java index 0d6305f8782..071c07bdc9e 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/FreezeTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/FreezeTest.java @@ -289,17 +289,9 @@ public void testWithCallerEnergyChangedInTx() throws Exception { TVMTestResult result = freezeForOther(userA, contractAddr, userA, frozenBalance, 1); - System.out.println(result.getReceipt().getEnergyUsageTotal()); - System.out.println(accountStore.get(userA)); - System.out.println(accountStore.get(owner)); - clearDelegatedExpireTime(contractAddr, userA); result = unfreezeForOther(userA, contractAddr, userA, 1); - - System.out.println(result.getReceipt().getEnergyUsageTotal()); - System.out.println(accountStore.get(userA)); - System.out.println(accountStore.get(owner)); } @Test diff --git a/framework/src/test/java/org/tron/common/utils/PropUtilTest.java b/framework/src/test/java/org/tron/common/utils/PropUtilTest.java index 2df5bd8effd..330d97bd51c 100644 --- a/framework/src/test/java/org/tron/common/utils/PropUtilTest.java +++ b/framework/src/test/java/org/tron/common/utils/PropUtilTest.java @@ -14,7 +14,7 @@ public void testWriteProperty() { try { file.createNewFile(); } catch (IOException e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } PropUtil.writeProperty(filename, "key", "value"); Assert.assertTrue("value".equals(PropUtil.readProperty(filename, "key"))); @@ -30,11 +30,11 @@ public void testReadProperty() { try { file.createNewFile(); } catch (IOException e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } PropUtil.writeProperty(filename, "key", "value"); Assert.assertTrue("value".equals(PropUtil.readProperty(filename, "key"))); file.delete(); Assert.assertTrue("".equals(PropUtil.readProperty(filename, "key"))); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/db/AccountAssetStoreTest.java b/framework/src/test/java/org/tron/core/db/AccountAssetStoreTest.java index 88734945687..24475f3c7f7 100644 --- a/framework/src/test/java/org/tron/core/db/AccountAssetStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/AccountAssetStoreTest.java @@ -87,7 +87,7 @@ private long createAsset(String tokenName) { try { ownerCapsule.addAssetV2(ByteArray.fromString(String.valueOf(id)), TOTAL_SUPPLY); } catch (Exception e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } accountStore.put(ownerCapsule.getAddress().toByteArray(), ownerCapsule); return id; diff --git a/framework/src/test/java/org/tron/core/db/BlockStoreTest.java b/framework/src/test/java/org/tron/core/db/BlockStoreTest.java index 1868eae4cba..fd408281ab2 100644 --- a/framework/src/test/java/org/tron/core/db/BlockStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/BlockStoreTest.java @@ -46,7 +46,7 @@ public void testPut() { Assert.assertNotNull(blockCapsule1); Assert.assertEquals(number, blockCapsule1.getNum()); } catch (ItemNotFoundException | BadItemException e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } } @@ -63,7 +63,7 @@ public void testGet() { Assert.assertEquals(number, blockCapsule1.getNum()); } catch (ItemNotFoundException | BadItemException e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } } @@ -83,7 +83,7 @@ public void testDelete() { BlockCapsule blockCapsule2 = blockStore.getUnchecked(blockId); Assert.assertNull(blockCapsule2); } catch (ItemNotFoundException | BadItemException e) { - e.printStackTrace(); + Assert.fail(e.getMessage()); } } diff --git a/framework/src/test/java/org/tron/core/db/DBIteratorTest.java b/framework/src/test/java/org/tron/core/db/DBIteratorTest.java index 58923ce50b6..0966d904093 100644 --- a/framework/src/test/java/org/tron/core/db/DBIteratorTest.java +++ b/framework/src/test/java/org/tron/core/db/DBIteratorTest.java @@ -86,43 +86,36 @@ public void testRocksDb() throws RocksDBException, IOException { RocksDB db = RocksDB.open(options, file.toString())) { db.put("1".getBytes(StandardCharsets.UTF_8), "1".getBytes(StandardCharsets.UTF_8)); db.put("2".getBytes(StandardCharsets.UTF_8), "2".getBytes(StandardCharsets.UTF_8)); - RockStoreIterator iterator = new RockStoreIterator(db.newIterator(), new ReadOptions()); - iterator.seekToFirst(); - Assert.assertArrayEquals("1".getBytes(StandardCharsets.UTF_8), iterator.getKey()); - Assert.assertArrayEquals("1".getBytes(StandardCharsets.UTF_8), iterator.next().getValue()); - Assert.assertTrue(iterator.hasNext()); - - Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getValue()); - Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.next().getKey()); - Assert.assertFalse(iterator.hasNext()); - - try { - iterator.seekToLast(); - } catch (Exception e) { - Assert.assertTrue(e instanceof IllegalStateException); + try (RockStoreIterator iterator = + new RockStoreIterator(db.newIterator(), new ReadOptions())) { + iterator.seekToFirst(); + Assert.assertArrayEquals("1".getBytes(StandardCharsets.UTF_8), iterator.getKey()); + Assert.assertArrayEquals("1".getBytes(StandardCharsets.UTF_8), iterator.next().getValue()); + Assert.assertTrue(iterator.hasNext()); + + Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getValue()); + Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.next().getKey()); + Assert.assertFalse(iterator.hasNext()); + + Assert.assertThrows(IllegalStateException.class, iterator::seekToLast); } - iterator = new RockStoreIterator(db.newIterator(), new ReadOptions()); - iterator.seekToLast(); - Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getKey()); - Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getValue()); - iterator.seekToFirst(); - while (iterator.hasNext()) { + try ( + RockStoreIterator iterator = + new RockStoreIterator(db.newIterator(), new ReadOptions())) { + iterator.seekToLast(); + Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getKey()); + Assert.assertArrayEquals("2".getBytes(StandardCharsets.UTF_8), iterator.getValue()); + iterator.seekToFirst(); + while (iterator.hasNext()) { + iterator.next(); + } + Assert.assertFalse(iterator.hasNext()); + Assert.assertThrows(IllegalStateException.class, iterator::getKey); + Assert.assertThrows(IllegalStateException.class, iterator::getValue); + thrown.expect(NoSuchElementException.class); iterator.next(); } - Assert.assertFalse(iterator.hasNext()); - try { - iterator.getKey(); - } catch (Exception e) { - Assert.assertTrue(e instanceof IllegalStateException); - } - try { - iterator.getValue(); - } catch (Exception e) { - Assert.assertTrue(e instanceof IllegalStateException); - } - thrown.expect(NoSuchElementException.class); - iterator.next(); } } diff --git a/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java b/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java index 76e9a18e31a..4948f3d935c 100644 --- a/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java +++ b/framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java @@ -172,9 +172,8 @@ private SnapshotImpl getSnapshotImplIns(Snapshot snapshot) { constructor.setAccessible(true); return (SnapshotImpl) constructor.newInstance(snapshot); } catch (Exception e) { - e.printStackTrace(); + throw new AssertionError(e); } - return null; } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java index 1c11a2a35dd..07eb6d7da1f 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -7,9 +7,13 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.logsfilter.capsule.BlockFilterCapsule; import org.tron.common.utils.ByteArray; import org.tron.core.exception.ItemNotFoundException; @@ -18,6 +22,7 @@ @Slf4j public class ConcurrentHashMapTest { + private static final String EXECUTOR_NAME = "jsonrpc-concurrent-map-test"; private static int randomInt(int minInt, int maxInt) { return (int) round(random(true) * (maxInt - minInt) + minInt, true); @@ -56,9 +61,10 @@ public void testHandleBlockHash() { Assert.fail("Interrupted during test setup: " + e.getMessage()); } - Thread putThread = new Thread(new Runnable() { - public void run() { + ExecutorService executor = ExecutorServiceManager.newFixedThreadPool(EXECUTOR_NAME, 4); + try { + Future putTask = executor.submit(() -> { for (int i = 1; i <= times; i++) { logger.info("put time {}, from {} to {}", i, (1 + (i - 1) * eachCount), i * eachCount); @@ -71,22 +77,19 @@ public void run() { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - logger.error("putThread interrupted", e); + throw new AssertionError("putThread interrupted", e); } } - } - - }); + }); - Thread getThread1 = new Thread(new Runnable() { - public void run() { + Future getTask1 = executor.submit(() -> { for (int t = 1; t <= times * 2; t++) { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - logger.error("getThread1 interrupted", e); + throw new AssertionError("getThread1 interrupted", e); } logger.info("Thread1 get time {}", t); @@ -105,18 +108,16 @@ public void run() { } } } - } - }); + }); - Thread getThread2 = new Thread(new Runnable() { - public void run() { + Future getTask2 = executor.submit(() -> { for (int t = 1; t <= times * 2; t++) { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - logger.error("getThread2 interrupted", e); + throw new AssertionError("getThread2 interrupted", e); } logger.info("Thread2 get time {}", t); @@ -139,18 +140,16 @@ public void run() { } } } - } - }); + }); - Thread getThread3 = new Thread(new Runnable() { - public void run() { + Future getTask3 = executor.submit(() -> { for (int t = 1; t <= times * 2; t++) { try { Thread.sleep(randomInt(50, 100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - logger.error("getThread3 interrupted", e); + throw new AssertionError("getThread3 interrupted", e); } logger.info("Thread3 get time {}", t); @@ -164,8 +163,7 @@ public void run() { try { resultMap3.get(String.valueOf(k)).add(str.toString()); } catch (Exception e) { - logger.error("resultMap3 get {} exception {}", k, e.getMessage()); - e.printStackTrace(); + throw new AssertionError("resultMap3 get " + k + " exception", e); } } @@ -174,22 +172,21 @@ public void run() { } } } + }); + + for (Future future : new Future[] {putTask, getTask1, getTask2, getTask3}) { + try { + future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + Assert.fail("Main thread interrupted while waiting for worker threads: " + + e.getMessage()); + } catch (ExecutionException e) { + Assert.fail("Worker thread failed: " + e.getCause()); + } } - }); - - putThread.start(); - getThread1.start(); - getThread2.start(); - getThread3.start(); - - try { - putThread.join(); - getThread1.join(); - getThread2.join(); - getThread3.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Assert.fail("Main thread interrupted while waiting for worker threads: " + e.getMessage()); + } finally { + ExecutorServiceManager.shutdownAndAwaitTermination(executor, EXECUTOR_NAME); } logger.info("-----------------------------------------------------------------------"); @@ -210,4 +207,4 @@ public void run() { } } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java index d80d10694a8..9f7f32d7201 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java @@ -3,13 +3,13 @@ import java.lang.reflect.Method; import java.util.BitSet; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import javax.annotation.Resource; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.es.ExecutorServiceManager; import org.tron.common.TestConstants; import org.tron.core.config.args.Args; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; @@ -21,6 +21,7 @@ public class LogBlockQueryTest extends BaseTest { @Resource SectionBloomStore sectionBloomStore; + private static final String EXECUTOR_NAME = "log-block-query-test"; private ExecutorService sectionExecutor; private Method partialMatchMethod; private static final long CURRENT_MAX_BLOCK_NUM = 50000L; @@ -31,10 +32,10 @@ public class LogBlockQueryTest extends BaseTest { @Before public void setup() throws Exception { - sectionExecutor = Executors.newFixedThreadPool(5); - + sectionExecutor = ExecutorServiceManager.newFixedThreadPool(EXECUTOR_NAME, 5); + // Get private method through reflection - partialMatchMethod = LogBlockQuery.class.getDeclaredMethod("partialMatch", + partialMatchMethod = LogBlockQuery.class.getDeclaredMethod("partialMatch", int[][].class, int.class); partialMatchMethod.setAccessible(true); @@ -52,9 +53,7 @@ public void setup() throws Exception { @After public void tearDown() { - if (sectionExecutor != null && !sectionExecutor.isShutdown()) { - sectionExecutor.shutdown(); - } + ExecutorServiceManager.shutdownAndAwaitTermination(sectionExecutor, EXECUTOR_NAME); } @Test @@ -63,8 +62,8 @@ public void testPartialMatch() throws Exception { LogFilterWrapper logFilterWrapper = new LogFilterWrapper( new FilterRequest("0x0", "0x1", null, null, null), CURRENT_MAX_BLOCK_NUM, null, false); - - LogBlockQuery logBlockQuery = new LogBlockQuery(logFilterWrapper, sectionBloomStore, + + LogBlockQuery logBlockQuery = new LogBlockQuery(logFilterWrapper, sectionBloomStore, CURRENT_MAX_BLOCK_NUM, sectionExecutor); int section = 0; @@ -105,4 +104,4 @@ public void testPartialMatch() throws Exception { Assert.assertNotNull(result); Assert.assertTrue(result.isEmpty()); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/net/MessageTest.java b/framework/src/test/java/org/tron/core/net/MessageTest.java index 5b81d18a599..a2264545dc0 100644 --- a/framework/src/test/java/org/tron/core/net/MessageTest.java +++ b/framework/src/test/java/org/tron/core/net/MessageTest.java @@ -26,7 +26,6 @@ public void test1() throws Exception { disconnectMessage = new DisconnectMessage(MessageTypes.P2P_DISCONNECT.asByte(), disconnectMessageTest.toByteArray()); } catch (Exception e) { - System.out.println(e.getMessage()); Assert.assertTrue(e instanceof P2pException); } } @@ -39,7 +38,6 @@ public void test2() throws Exception { disconnectMessageTest.toByteArray()); } long endTime = System.currentTimeMillis(); - System.out.println("spend time : " + (endTime - startTime)); } @Test diff --git a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java index 6f34288939f..8585244b941 100644 --- a/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/RelayServiceTest.java @@ -89,7 +89,6 @@ private void initWitness() { // Base58: TNboetpFgv9SqMoHvaVt626NLXETnbdW1K byte[] key = Hex.decode("418A8D690BF36806C36A7DAE3AF796643C1AA9CC01");//exist already WitnessCapsule witnessCapsule = chainBaseManager.getWitnessStore().get(key); - System.out.println(witnessCapsule.getInstance()); witnessCapsule.setVoteCount(1000); chainBaseManager.getWitnessStore().put(key, witnessCapsule); List list = new ArrayList<>(); diff --git a/framework/src/test/java/org/tron/core/pbft/PbftTest.java b/framework/src/test/java/org/tron/core/pbft/PbftTest.java index 33a46516988..10965240c8e 100644 --- a/framework/src/test/java/org/tron/core/pbft/PbftTest.java +++ b/framework/src/test/java/org/tron/core/pbft/PbftTest.java @@ -30,7 +30,7 @@ public void testPbftSrMessage() { PbftMessage pbftSrMessage = PbftMessage .prePrepareSRLMsg(blockCapsule, srList, 1, miner); PbftMessage.fullNodePrePrepareSRLMsg(blockCapsule, srList, 1); - System.out.println(pbftSrMessage); + org.junit.Assert.assertNotNull(pbftSrMessage); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java b/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java index fc60c2afa03..a16a71c4e59 100644 --- a/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/DelegationServiceTest.java @@ -78,7 +78,6 @@ private void testWithdraw() { mortgageService.withdrawReward(sr1); accountCapsule = dbManager.getAccountStore().get(sr1); allowance = accountCapsule.getAllowance() - allowance; - System.out.println("withdrawReward:" + allowance); Assert.assertEquals(reward, allowance); } diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java index ea622213f45..c57f7f42e2f 100644 --- a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -9,8 +9,12 @@ import java.util.Collections; import java.util.HashSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -18,6 +22,7 @@ import org.mockito.MockedConstruction; import org.mockito.junit.MockitoJUnitRunner; import org.reflections.Reflections; +import org.tron.common.es.ExecutorServiceManager; import org.tron.core.actuator.AbstractActuator; import org.tron.core.actuator.TransferActuator; import org.tron.core.config.args.Args; @@ -49,25 +54,32 @@ public void testAlreadyRegisteredSkipRegistration() { @Test public void testConcurrentAccessThreadSafe() throws InterruptedException { final int threadCount = 5; - Thread[] threads = new Thread[threadCount]; final AtomicBoolean testPassed = new AtomicBoolean(true); + ExecutorService executor = ExecutorServiceManager + .newFixedThreadPool("transaction-register-test", threadCount); + Future[] futures = new Future[threadCount]; + + try { + for (int i = 0; i < threadCount; i++) { + futures[i] = executor.submit(() -> { + try { + TransactionRegister.registerActuator(); + } catch (Throwable e) { + testPassed.set(false); + throw e; + } + }); + } - for (int i = 0; i < threadCount; i++) { - threads[i] = new Thread(() -> { + for (Future future : futures) { try { - TransactionRegister.registerActuator(); - } catch (Throwable e) { - testPassed.set(false); + future.get(); + } catch (ExecutionException e) { + Assert.fail("Concurrent registration should not throw: " + e.getCause()); } - }); - } - - for (Thread thread : threads) { - thread.start(); - } - - for (Thread thread : threads) { - thread.join(); + } + } finally { + ExecutorServiceManager.shutdownAndAwaitTermination(executor, "transaction-register-test"); } assertTrue("All threads should complete without exceptions", testPassed.get()); @@ -134,4 +146,4 @@ public void testThrowsTronError() { assertTrue(error.getMessage().contains("TransferActuator")); } } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/witness/ProposalControllerTest.java b/framework/src/test/java/org/tron/core/witness/ProposalControllerTest.java index 59f4e899d9f..94e50b410a0 100644 --- a/framework/src/test/java/org/tron/core/witness/ProposalControllerTest.java +++ b/framework/src/test/java/org/tron/core/witness/ProposalControllerTest.java @@ -80,7 +80,7 @@ public void testProcessProposal() { try { proposalCapsule = dbManager.getProposalStore().get(key); } catch (Exception ex) { - System.out.println(ex.getMessage()); + Assert.fail(ex.getMessage()); } Assert.assertEquals(State.DISAPPROVED, proposalCapsule.getState()); @@ -95,7 +95,7 @@ public void testProcessProposal() { try { proposalCapsule = dbManager.getProposalStore().get(key); } catch (Exception ex) { - System.out.println(ex.getMessage()); + Assert.fail(ex.getMessage()); } Assert.assertEquals(State.DISAPPROVED, proposalCapsule.getState()); @@ -117,7 +117,7 @@ public void testProcessProposal() { try { proposalCapsule = dbManager.getProposalStore().get(key); } catch (Exception ex) { - System.out.println(ex.getMessage()); + Assert.fail(ex.getMessage()); } Assert.assertEquals(State.APPROVED, proposalCapsule.getState()); } @@ -166,7 +166,7 @@ public void testProcessProposals() { try { proposalCapsule3 = dbManager.getProposalStore().get(proposalCapsule3.createDbKey()); } catch (Exception ex) { - System.out.println(ex.getMessage()); + Assert.fail(ex.getMessage()); } Assert.assertEquals(State.DISAPPROVED, proposalCapsule3.getState()); diff --git a/framework/src/test/java/org/tron/keystore/CredentialsTest.java b/framework/src/test/java/org/tron/keystore/CredentialsTest.java index 3fe2ce02b63..97cb5d54139 100644 --- a/framework/src/test/java/org/tron/keystore/CredentialsTest.java +++ b/framework/src/test/java/org/tron/keystore/CredentialsTest.java @@ -1,25 +1,34 @@ package org.tron.keystore; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import junit.framework.TestCase; -import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Test; -import org.springframework.util.Assert; -import org.tron.common.crypto.SignUtils; +import org.mockito.Mockito; +import org.tron.common.crypto.SignInterface; import org.tron.common.crypto.sm2.SM2; import org.tron.common.utils.ByteUtil; +import org.tron.common.utils.StringUtil; -@Slf4j -public class CredentialsTest extends TestCase { +public class CredentialsTest { + + private static final byte[] ADDRESS_1 = ByteUtil.hexToBytes( + "410102030405060708090a0b0c0d0e0f1011121314"); + private static final byte[] ADDRESS_2 = ByteUtil.hexToBytes( + "411415161718191a1b1c1d1e1f2021222324252627"); + + private SignInterface mockSignInterface(byte[] address) { + SignInterface signInterface = Mockito.mock(SignInterface.class); + Mockito.when(signInterface.getAddress()).thenReturn(address); + return signInterface; + } @Test - public void testCreate() throws NoSuchAlgorithmException { - Credentials credentials = Credentials.create(SignUtils.getGeneratedRandomSign( - SecureRandom.getInstance("NativePRNG"),true)); - Assert.hasText(credentials.getAddress(),"Credentials address create failed!"); - Assert.notNull(credentials.getSignInterface(), - "Credentials cryptoEngine create failed"); + public void testCreate() { + SignInterface signInterface = mockSignInterface(ADDRESS_1); + Credentials credentials = Credentials.create(signInterface); + Assert.assertEquals("Credentials address create failed!", + StringUtil.encode58Check(ADDRESS_1), credentials.getAddress()); + Assert.assertSame("Credentials cryptoEngine create failed", signInterface, + credentials.getSignInterface()); } @Test @@ -28,21 +37,43 @@ public void testCreateFromSM2() { Credentials.create(SM2.fromNodeId(ByteUtil.hexToBytes("fffffffffff" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffff"))); + Assert.fail("Expected IllegalArgumentException"); } catch (Exception e) { - Assert.isInstanceOf(IllegalArgumentException.class, e); + Assert.assertTrue(e instanceof IllegalArgumentException); } } @Test - public void testEquals() throws NoSuchAlgorithmException { - Credentials credentials1 = Credentials.create(SignUtils.getGeneratedRandomSign( - SecureRandom.getInstance("NativePRNG"),true)); - Credentials credentials2 = Credentials.create(SignUtils.getGeneratedRandomSign( - SecureRandom.getInstance("NativePRNG"),true)); - Assert.isTrue(!credentials1.equals(credentials2), - "Credentials instance should be not equal!"); - Assert.isTrue(!(credentials1.hashCode() == credentials2.hashCode()), - "Credentials instance hashcode should be not equal!"); + public void testEquals() { + Credentials credentials1 = Credentials.create(mockSignInterface(ADDRESS_1)); + Credentials credentials2 = Credentials.create(mockSignInterface(ADDRESS_2)); + + Assert.assertNotEquals("Credentials address fixtures should differ", + credentials1.getAddress(), credentials2.getAddress()); + Assert.assertNotEquals("Credentials instance should be not equal!", + credentials1, credentials2); + } + + @Test + public void testEqualsWithAddressAndCryptoEngine() { + Object aObject = new Object(); + SignInterface signInterface = mockSignInterface(ADDRESS_1); + SignInterface signInterface2 = mockSignInterface(ADDRESS_1); + SignInterface signInterface3 = mockSignInterface(ADDRESS_2); + + Credentials credential = Credentials.create(signInterface); + Credentials sameCredential = Credentials.create(signInterface); + Credentials sameAddressDifferentEngineCredential = Credentials.create(signInterface2); + Credentials differentCredential = Credentials.create(signInterface3); + + Assert.assertFalse(aObject.equals(credential)); + Assert.assertFalse(credential.equals(aObject)); + Assert.assertFalse(credential.equals(null)); + Assert.assertEquals(credential, sameCredential); + Assert.assertEquals("Equal credentials must have the same hashCode", + credential.hashCode(), sameCredential.hashCode()); + Assert.assertNotEquals(credential, sameAddressDifferentEngineCredential); + Assert.assertFalse(credential.equals(differentCredential)); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java b/framework/src/test/java/org/tron/keystroe/CredentialsTest.java deleted file mode 100644 index 2642129e00a..00000000000 --- a/framework/src/test/java/org/tron/keystroe/CredentialsTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.tron.keystroe; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; -import org.tron.common.crypto.SignInterface; -import org.tron.keystore.Credentials; - -public class CredentialsTest { - - @Test - public void test_equality() { - Object aObject = new Object(); - SignInterface si = Mockito.mock(SignInterface.class); - SignInterface si2 = Mockito.mock(SignInterface.class); - SignInterface si3 = Mockito.mock(SignInterface.class); - byte[] address = "TQhZ7W1RudxFdzJMw6FvMnujPxrS6sFfmj".getBytes(); - byte[] address2 = "TNCmcTdyrYKMtmE1KU2itzeCX76jGm5Not".getBytes(); - Mockito.when(si.getAddress()).thenReturn(address); - Mockito.when(si2.getAddress()).thenReturn(address); - Mockito.when(si3.getAddress()).thenReturn(address2); - Credentials aCredential = Credentials.create(si); - Assert.assertFalse(aObject.equals(aCredential)); - Assert.assertFalse(aCredential.equals(aObject)); - Assert.assertFalse(aCredential.equals(null)); - Credentials anotherCredential = Credentials.create(si); - Assert.assertTrue(aCredential.equals(anotherCredential)); - Credentials aCredential2 = Credentials.create(si2); - Assert.assertTrue(aCredential.equals(anotherCredential)); - Credentials aCredential3 = Credentials.create(si3); - Assert.assertFalse(aCredential.equals(aCredential3)); - } -} From 9a3339196562fdfa560913b22662e468f81e0b97 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 14:03:49 +0800 Subject: [PATCH 06/13] test(framework): unify app context fixtures in service tests --- .gitignore | 1 + .../common/ClassLevelAppContextFixture.java | 62 +++++++++++++++++++ .../messagehandler/MessageHandlerTest.java | 8 ++- .../core/services/RpcApiServicesTest.java | 33 +++------- .../org/tron/core/services/WalletApiTest.java | 24 ++----- .../LiteFnQueryGrpcInterceptorTest.java | 25 +++----- .../filter/RpcApiAccessInterceptorTest.java | 24 +++---- 7 files changed, 96 insertions(+), 81 deletions(-) create mode 100644 framework/src/test/java/org/tron/common/ClassLevelAppContextFixture.java diff --git a/.gitignore b/.gitignore index 3917bb44679..6587cb5b287 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ Wallet /framework/propPath .cache +/.dev/ diff --git a/framework/src/test/java/org/tron/common/ClassLevelAppContextFixture.java b/framework/src/test/java/org/tron/common/ClassLevelAppContextFixture.java new file mode 100644 index 00000000000..1d26f895b64 --- /dev/null +++ b/framework/src/test/java/org/tron/common/ClassLevelAppContextFixture.java @@ -0,0 +1,62 @@ +package org.tron.common; + +import io.grpc.ManagedChannel; +import java.util.concurrent.TimeUnit; +import org.tron.common.application.ApplicationFactory; +import org.tron.common.application.TronApplicationContext; +import org.tron.core.config.DefaultConfig; + +/** + * Shared class-level fixture for tests that manually manage a TronApplicationContext. + */ +public class ClassLevelAppContextFixture { + + private TronApplicationContext context; + + public TronApplicationContext createContext() { + context = new TronApplicationContext(DefaultConfig.class); + return context; + } + + public TronApplicationContext createAndStart() { + createContext(); + startApp(); + return context; + } + + public void startApp() { + ApplicationFactory.create(context).startup(); + } + + public TronApplicationContext getContext() { + return context; + } + + public void close() { + if (context != null) { + context.close(); + context = null; + } + } + + public static void shutdownChannel(ManagedChannel channel) { + if (channel == null) { + return; + } + try { + channel.shutdown(); + if (!channel.awaitTermination(5, TimeUnit.SECONDS)) { + channel.shutdownNow(); + } + } catch (InterruptedException e) { + channel.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + public static void shutdownChannels(ManagedChannel... channels) { + for (ManagedChannel channel : channels) { + shutdownChannel(channel); + } + } +} diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java index b1fb197a2e9..be843674632 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/MessageHandlerTest.java @@ -13,13 +13,13 @@ import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.consensus.pbft.message.PbftMessage; import org.tron.core.capsule.BlockCapsule; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.net.P2pEventHandlerImpl; import org.tron.core.net.TronNetService; @@ -33,6 +33,8 @@ public class MessageHandlerTest { private static TronApplicationContext context; + private static final ClassLevelAppContextFixture APP_FIXTURE = + new ClassLevelAppContextFixture(); private PeerConnection peer; private static P2pEventHandlerImpl p2pEventHandler; private static ApplicationContext ctx; @@ -45,7 +47,7 @@ public class MessageHandlerTest { public static void init() throws Exception { Args.setParam(new String[] {"--output-directory", temporaryFolder.newFolder().toString(), "--debug"}, TestConstants.TEST_CONF); - context = new TronApplicationContext(DefaultConfig.class); + context = APP_FIXTURE.createContext(); p2pEventHandler = context.getBean(P2pEventHandlerImpl.class); ctx = (ApplicationContext) ReflectUtils.getFieldObject(p2pEventHandler, "ctx"); @@ -57,7 +59,7 @@ public static void init() throws Exception { @AfterClass public static void destroy() { Args.clearParam(); - context.destroy(); + APP_FIXTURE.close(); } @After diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index f40ec48e035..a4cb0dedde7 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -23,6 +23,7 @@ import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; import org.junit.runners.MethodSorters; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.api.DatabaseGrpc; import org.tron.api.DatabaseGrpc.DatabaseBlockingStub; import org.tron.api.GrpcAPI.BlockLimit; @@ -52,8 +53,6 @@ import org.tron.api.WalletSolidityGrpc; import org.tron.api.WalletSolidityGrpc.WalletSolidityBlockingStub; import org.tron.common.TestConstants; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.es.ExecutorServiceManager; import org.tron.common.utils.ByteArray; @@ -65,7 +64,6 @@ import org.tron.core.capsule.AccountCapsule; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.TransactionCapsule; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.db.Manager; import org.tron.protos.Protocol; @@ -118,8 +116,9 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class RpcApiServicesTest { - private static Application appTest; private static TronApplicationContext context; + private static final ClassLevelAppContextFixture APP_FIXTURE = + new ClassLevelAppContextFixture(); private static ManagedChannel channelFull = null; private static ManagedChannel channelPBFT = null; private static ManagedChannel channelSolidity = null; @@ -188,7 +187,7 @@ public static void init() throws IOException { .executor(executorService) .intercept(new TimeoutInterceptor(5000)) .build(); - context = new TronApplicationContext(DefaultConfig.class); + context = APP_FIXTURE.createContext(); databaseBlockingStubFull = DatabaseGrpc.newBlockingStub(channelFull); databaseBlockingStubSolidity = DatabaseGrpc.newBlockingStub(channelSolidity); databaseBlockingStubPBFT = DatabaseGrpc.newBlockingStub(channelPBFT); @@ -204,15 +203,12 @@ public static void init() throws IOException { manager.getAccountStore().put(ownerCapsule.createDbKey(), ownerCapsule); manager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); manager.getDynamicPropertiesStore().saveAllowShieldedTRC20Transaction(1); - appTest = ApplicationFactory.create(context); - appTest.startup(); + APP_FIXTURE.startApp(); } @AfterClass public static void destroy() { - shutdownChannel(channelFull); - shutdownChannel(channelPBFT); - shutdownChannel(channelSolidity); + ClassLevelAppContextFixture.shutdownChannels(channelFull, channelPBFT, channelSolidity); if (executorService != null) { ExecutorServiceManager.shutdownAndAwaitTermination( @@ -220,25 +216,10 @@ public static void destroy() { executorService = null; } - context.close(); + APP_FIXTURE.close(); Args.clearParam(); } - private static void shutdownChannel(ManagedChannel channel) { - if (channel == null) { - return; - } - try { - channel.shutdown(); - if (!channel.awaitTermination(5, TimeUnit.SECONDS)) { - channel.shutdownNow(); - } - } catch (InterruptedException e) { - channel.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - @Test public void testGetBlockByNum() { NumberMessage message = NumberMessage.newBuilder().setNum(0).build(); diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index b7a26d6dc73..555c763e566 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -12,15 +12,13 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.api.GrpcAPI.EmptyMessage; import org.tron.api.WalletGrpc; import org.tron.common.TestConstants; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.TimeoutInterceptor; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @@ -34,7 +32,8 @@ public class WalletApiTest { public Timeout timeout = new Timeout(30, TimeUnit.SECONDS); private static TronApplicationContext context; - private static Application appT; + private static final ClassLevelAppContextFixture APP_FIXTURE = + new ClassLevelAppContextFixture(); @BeforeClass @@ -43,9 +42,7 @@ public static void init() throws IOException { "--p2p-disable", "true"}, TestConstants.TEST_CONF); Args.getInstance().setRpcPort(PublicMethod.chooseRandomPort()); Args.getInstance().setRpcEnable(true); - context = new TronApplicationContext(DefaultConfig.class); - appT = ApplicationFactory.create(context); - appT.startup(); + context = APP_FIXTURE.createAndStart(); } @Test @@ -61,22 +58,13 @@ public void listNodesTest() { Assert.assertTrue(walletStub.listNodes(EmptyMessage.getDefaultInstance()) .getNodesList().isEmpty()); } finally { - // Properly shutdown the gRPC channel to prevent resource leaks - channel.shutdown(); - try { - if (!channel.awaitTermination(5, java.util.concurrent.TimeUnit.SECONDS)) { - channel.shutdownNow(); - } - } catch (InterruptedException e) { - channel.shutdownNow(); - Thread.currentThread().interrupt(); - } + ClassLevelAppContextFixture.shutdownChannel(channel); } } @AfterClass public static void destroy() { - context.destroy(); + APP_FIXTURE.close(); Args.clearParam(); } diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java index 42ed21312c3..5feaf0e5223 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryGrpcInterceptorTest.java @@ -18,21 +18,21 @@ import org.tron.api.GrpcAPI; import org.tron.api.WalletGrpc; import org.tron.api.WalletSolidityGrpc; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.ChainBaseManager; import org.tron.core.Constant; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; @Slf4j public class LiteFnQueryGrpcInterceptorTest { private static TronApplicationContext context; + private static final ClassLevelAppContextFixture APP_FIXTURE = + new ClassLevelAppContextFixture(); private static ManagedChannel channelFull = null; private static ManagedChannel channelSolidity = null; private static ManagedChannel channelpBFT = null; @@ -84,30 +84,21 @@ public static void init() throws IOException { .usePlaintext() .intercept(new TimeoutInterceptor(5000)) .build(); - context = new TronApplicationContext(DefaultConfig.class); + context = APP_FIXTURE.createContext(); blockingStubFull = WalletGrpc.newBlockingStub(channelFull); blockingStubSolidity = WalletSolidityGrpc.newBlockingStub(channelSolidity); blockingStubpBFT = WalletSolidityGrpc.newBlockingStub(channelpBFT); chainBaseManager = context.getBean(ChainBaseManager.class); - Application appTest = ApplicationFactory.create(context); - appTest.startup(); + APP_FIXTURE.startApp(); } /** * destroy the context. */ @AfterClass - public static void destroy() throws InterruptedException { - if (channelFull != null) { - channelFull.shutdownNow(); - } - if (channelSolidity != null) { - channelSolidity.shutdownNow(); - } - if (channelpBFT != null) { - channelpBFT.shutdownNow(); - } - context.close(); + public static void destroy() { + ClassLevelAppContextFixture.shutdownChannels(channelFull, channelSolidity, channelpBFT); + APP_FIXTURE.close(); Args.clearParam(); } diff --git a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java index 817693dc630..07821d10343 100644 --- a/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/RpcApiAccessInterceptorTest.java @@ -31,14 +31,12 @@ import org.tron.api.GrpcAPI.TransactionIdList; import org.tron.api.WalletGrpc; import org.tron.api.WalletSolidityGrpc; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; -import org.tron.common.application.Application; -import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; import org.tron.common.utils.TimeoutInterceptor; import org.tron.core.Constant; -import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.services.RpcApiService; import org.tron.protos.Protocol.Transaction; @@ -47,6 +45,8 @@ public class RpcApiAccessInterceptorTest { private static TronApplicationContext context; + private static final ClassLevelAppContextFixture APP_FIXTURE = + new ClassLevelAppContextFixture(); private static ManagedChannel channelFull = null; private static ManagedChannel channelPBFT = null; private static ManagedChannel channelSolidity = null; @@ -93,14 +93,13 @@ public static void init() throws IOException { .intercept(new TimeoutInterceptor(5000)) .build(); - context = new TronApplicationContext(DefaultConfig.class); + context = APP_FIXTURE.createContext(); blockingStubFull = WalletGrpc.newBlockingStub(channelFull); blockingStubSolidity = WalletSolidityGrpc.newBlockingStub(channelSolidity); blockingStubPBFT = WalletSolidityGrpc.newBlockingStub(channelPBFT); - Application appTest = ApplicationFactory.create(context); - appTest.startup(); + APP_FIXTURE.startApp(); } /** @@ -108,16 +107,8 @@ public static void init() throws IOException { */ @AfterClass public static void destroy() { - if (channelFull != null) { - channelFull.shutdownNow(); - } - if (channelPBFT != null) { - channelPBFT.shutdownNow(); - } - if (channelSolidity != null) { - channelSolidity.shutdownNow(); - } - context.close(); + ClassLevelAppContextFixture.shutdownChannels(channelFull, channelPBFT, channelSolidity); + APP_FIXTURE.close(); Args.clearParam(); } @@ -307,4 +298,3 @@ public void testGetMemoFee() { } } - From c99df5e9d75439e5f6145e70cbdff89ab545f33c Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:20:44 +0800 Subject: [PATCH 07/13] style(test): fix import order to satisfy checkstyle Co-Authored-By: Claude Sonnet 4.6 --- .../src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java | 2 +- .../test/java/org/tron/core/services/RpcApiServicesTest.java | 2 +- .../src/test/java/org/tron/core/services/WalletApiTest.java | 2 +- .../test/java/org/tron/core/utils/TransactionRegisterTest.java | 2 +- framework/src/test/java/org/tron/program/SolidityNodeTest.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java index 9f7f32d7201..94269e86fec 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogBlockQueryTest.java @@ -9,8 +9,8 @@ import org.junit.Before; import org.junit.Test; import org.tron.common.BaseTest; -import org.tron.common.es.ExecutorServiceManager; import org.tron.common.TestConstants; +import org.tron.common.es.ExecutorServiceManager; import org.tron.core.config.args.Args; import org.tron.core.services.jsonrpc.TronJsonRpc.FilterRequest; import org.tron.core.services.jsonrpc.filters.LogBlockQuery; diff --git a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java index a4cb0dedde7..94b05cae98f 100644 --- a/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java +++ b/framework/src/test/java/org/tron/core/services/RpcApiServicesTest.java @@ -23,7 +23,6 @@ import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; import org.junit.runners.MethodSorters; -import org.tron.common.ClassLevelAppContextFixture; import org.tron.api.DatabaseGrpc; import org.tron.api.DatabaseGrpc.DatabaseBlockingStub; import org.tron.api.GrpcAPI.BlockLimit; @@ -52,6 +51,7 @@ import org.tron.api.WalletGrpc.WalletBlockingStub; import org.tron.api.WalletSolidityGrpc; import org.tron.api.WalletSolidityGrpc.WalletSolidityBlockingStub; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; import org.tron.common.application.TronApplicationContext; import org.tron.common.es.ExecutorServiceManager; diff --git a/framework/src/test/java/org/tron/core/services/WalletApiTest.java b/framework/src/test/java/org/tron/core/services/WalletApiTest.java index 555c763e566..4a55556afb1 100644 --- a/framework/src/test/java/org/tron/core/services/WalletApiTest.java +++ b/framework/src/test/java/org/tron/core/services/WalletApiTest.java @@ -12,9 +12,9 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.rules.Timeout; -import org.tron.common.ClassLevelAppContextFixture; import org.tron.api.GrpcAPI.EmptyMessage; import org.tron.api.WalletGrpc; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; import org.tron.common.application.TronApplicationContext; import org.tron.common.utils.PublicMethod; diff --git a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java index c57f7f42e2f..5c365eb3ef0 100644 --- a/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java +++ b/framework/src/test/java/org/tron/core/utils/TransactionRegisterTest.java @@ -14,8 +14,8 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 91e3b93fa96..6dcfb33a8b8 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -3,8 +3,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; From 8d22ffb1323756f1bcf67375f5d6fe69c2c38818 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:25:28 +0800 Subject: [PATCH 08/13] chore(config): remove /.dev/ from .gitignore Move to local .git/info/exclude instead. Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6587cb5b287..3917bb44679 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,3 @@ Wallet /framework/propPath .cache -/.dev/ From b17f73d213baf9ee15f2719f55c0586fed602576 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:31:35 +0800 Subject: [PATCH 09/13] style(test): fix import order in SolidityNodeTest Co-Authored-By: Claude Sonnet 4.6 --- framework/src/test/java/org/tron/program/SolidityNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 6dcfb33a8b8..2313e594566 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -2,8 +2,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import java.util.concurrent.TimeUnit; From 441c71609a21f3e16aff1158297e6c295ba98690 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:45:47 +0800 Subject: [PATCH 10/13] test(framework): use assertSame for exception identity check in SolidityNodeTest Co-Authored-By: Claude Sonnet 4.6 --- framework/src/test/java/org/tron/program/SolidityNodeTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index 2313e594566..782f934d686 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -1,6 +1,7 @@ package org.tron.program; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; @@ -116,7 +117,7 @@ public void testAwaitShutdownStopsNodeWhenBlockedCallFails() { RuntimeException thrown = assertThrows(RuntimeException.class, () -> SolidityNode.awaitShutdown(app, node)); - assertEquals(expected, thrown); + assertSame(expected, thrown); InOrder inOrder = inOrder(app, node); inOrder.verify(app).blockUntilShutdown(); From a8d650f097a64b0269fdc579583da56b116efa85 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 16:21:24 +0800 Subject: [PATCH 11/13] fix(net): guard relay nodes in peer connection --- .../main/java/org/tron/core/net/peer/PeerConnection.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index 253502bc3a1..ebd4a3827ad 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -57,8 +57,6 @@ @Scope("prototype") public class PeerConnection { - private static List relayNodes = Args.getInstance().getFastForwardNodes(); - @Getter private PeerStatistics peerStatistics = new PeerStatistics(); @@ -166,7 +164,9 @@ public class PeerConnection { public void setChannel(Channel channel) { this.channel = channel; - if (relayNodes.stream().anyMatch(n -> n.getAddress().equals(channel.getInetAddress()))) { + List relayNodes = Args.getInstance().getFastForwardNodes(); + if (relayNodes != null + && relayNodes.stream().anyMatch(n -> n.getAddress().equals(channel.getInetAddress()))) { this.isRelayPeer = true; } this.nodeStatistics = TronStatsManager.getNodeStatistics(channel.getInetAddress()); From f0dfdea831d238a6ab02f756cee32b286e975e6d Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 17:22:34 +0800 Subject: [PATCH 12/13] test(framework,plugins): fix flaky tests in bandwidth, stats and db-move - BandWidthRuntimeTest: deploy contract before setDebug(true) to avoid CPU timeout triggered by debug mode during contract deployment - TronStatsManagerTest: capture P2pStats snapshot before invoking work() and compare against snapshot instead of hardcoded 0, preventing failures when another test starts the real P2pService with non-zero stats - DbMoveTest: add @After cleanup of output-directory-toolkit so LevelDB and RocksDB tests don't share the same relative destination path - PeerStatusCheckMockTest: replace 5-second sleep with mock executor; verify scheduleWithFixedDelay args and run the captured Runnable directly Co-Authored-By: Claude Sonnet 4.6 --- .../runtime/vm/BandWidthRuntimeTest.java | 7 ++++- .../net/peer/PeerStatusCheckMockTest.java | 27 ++++++++++++++----- .../net/services/TronStatsManagerTest.java | 16 ++++++++--- .../java/org/tron/plugins/DbMoveTest.java | 26 ++++++++++++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/framework/src/test/java/org/tron/common/runtime/vm/BandWidthRuntimeTest.java b/framework/src/test/java/org/tron/common/runtime/vm/BandWidthRuntimeTest.java index 40a4003f625..6b8b138c3f3 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/BandWidthRuntimeTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/BandWidthRuntimeTest.java @@ -24,6 +24,7 @@ import org.junit.BeforeClass; import org.junit.Test; import org.tron.common.BaseTest; +import org.tron.common.parameter.CommonParameter; import org.tron.common.runtime.RuntimeImpl; import org.tron.common.runtime.TvmTestUtils; import org.tron.common.utils.Commons; @@ -153,8 +154,10 @@ public void testSuccess() { @Test public void testSuccessNoBandd() { + boolean originalDebug = CommonParameter.getInstance().isDebug(); try { byte[] contractAddress = createContract(); + CommonParameter.getInstance().setDebug(true); TriggerSmartContract triggerContract = TvmTestUtils.createTriggerContract(contractAddress, "setCoin(uint256)", "50", false, 0, Commons.decodeFromBase58Check(TriggerOwnerTwoAddress)); @@ -185,6 +188,8 @@ public void testSuccessNoBandd() { balance); } catch (TronException e) { Assert.assertNotNull(e); + } finally { + CommonParameter.getInstance().setDebug(originalDebug); } } @@ -254,4 +259,4 @@ public void testMaxContractResultSize() { } Assert.assertEquals(2, maxSize); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java index c518e22280b..fc0718e1d2d 100644 --- a/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java +++ b/framework/src/test/java/org/tron/core/net/peer/PeerStatusCheckMockTest.java @@ -1,12 +1,18 @@ package org.tron.core.net.peer; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; +import org.tron.common.utils.ReflectUtils; public class PeerStatusCheckMockTest { @After @@ -15,17 +21,26 @@ public void clearMocks() { } @Test - public void testInitException() throws InterruptedException { + public void testInitException() { PeerStatusCheck peerStatusCheck = spy(new PeerStatusCheck()); + ScheduledExecutorService executor = mock(ScheduledExecutorService.class); + ReflectUtils.setFieldValue(peerStatusCheck, "peerStatusCheckExecutor", executor); doThrow(new RuntimeException("test exception")).when(peerStatusCheck).statusCheck(); + peerStatusCheck.init(); - // the initialDelay of scheduleWithFixedDelay is 5s; wait for at least one execution - Thread.sleep(5000L); + Mockito.verify(executor).scheduleWithFixedDelay(any(Runnable.class), eq(5L), eq(2L), + eq(TimeUnit.SECONDS)); + Runnable scheduledTask = Mockito.mockingDetails(executor).getInvocations().stream() + .filter(invocation -> invocation.getMethod().getName().equals("scheduleWithFixedDelay")) + .map(invocation -> (Runnable) invocation.getArgument(0)) + .findFirst() + .orElseThrow(() -> new AssertionError("scheduled task was not registered")); + + scheduledTask.run(); - // Verify statusCheck() was invoked by the scheduler and the exception was handled gracefully - Mockito.verify(peerStatusCheck, Mockito.atLeastOnce()).statusCheck(); + Mockito.verify(peerStatusCheck).statusCheck(); Assert.assertNotNull(peerStatusCheck); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/net/services/TronStatsManagerTest.java b/framework/src/test/java/org/tron/core/net/services/TronStatsManagerTest.java index a940a14d392..7ad56c464bb 100644 --- a/framework/src/test/java/org/tron/core/net/services/TronStatsManagerTest.java +++ b/framework/src/test/java/org/tron/core/net/services/TronStatsManagerTest.java @@ -7,8 +7,10 @@ import org.junit.Assert; import org.junit.Test; +import org.tron.core.net.TronNetService; import org.tron.core.net.service.statistics.NodeStatistics; import org.tron.core.net.service.statistics.TronStatsManager; +import org.tron.p2p.stats.P2pStats; import org.tron.protos.Protocol; public class TronStatsManagerTest { @@ -50,14 +52,20 @@ public void testWork() throws Exception { Assert.assertEquals(field3.get(manager), 1L); Assert.assertEquals(field4.get(manager), 1L); + P2pStats statsSnapshot = TronNetService.getP2pService().getP2pStats(); + long expectedTcpIn = statsSnapshot.getTcpInSize(); + long expectedTcpOut = statsSnapshot.getTcpOutSize(); + long expectedUdpIn = statsSnapshot.getUdpInSize(); + long expectedUdpOut = statsSnapshot.getUdpOutSize(); + Method method = manager.getClass().getDeclaredMethod("work"); method.setAccessible(true); method.invoke(manager); - Assert.assertEquals(field1.get(manager), 0L); - Assert.assertEquals(field2.get(manager), 0L); - Assert.assertEquals(field3.get(manager), 0L); - Assert.assertEquals(field4.get(manager), 0L); + Assert.assertEquals(expectedTcpIn, (long) field1.get(manager)); + Assert.assertEquals(expectedTcpOut, (long) field2.get(manager)); + Assert.assertEquals(expectedUdpIn, (long) field3.get(manager)); + Assert.assertEquals(expectedUdpOut, (long) field4.get(manager)); } } diff --git a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java index 6df17c4251c..ec4f0d545b0 100644 --- a/plugins/src/test/java/org/tron/plugins/DbMoveTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbMoveTest.java @@ -5,6 +5,7 @@ import java.net.URL; import java.nio.file.Paths; import lombok.extern.slf4j.Slf4j; +import org.junit.After; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -17,6 +18,8 @@ @Slf4j public class DbMoveTest { + private static final String OUTPUT_DIRECTORY = "output-directory-toolkit"; + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -30,6 +33,29 @@ private void init(DbTool.DbType dbType, String path) throws IOException, RocksDB DbTool.getDB(path, TRANS, dbType).close(); } + @After + public void destroy() { + deleteDir(new File(OUTPUT_DIRECTORY)); + } + + /** + * delete directory. + */ + private static boolean deleteDir(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + assert children != null; + for (String child : children) { + boolean success = deleteDir(new File(dir, child)); + if (!success) { + logger.warn("can't delete dir:" + dir); + return false; + } + } + } + return dir.delete(); + } + private static String getConfig(String config) { URL path = DbMoveTest.class.getClassLoader().getResource(config); return path == null ? null : path.getPath(); From 9d99acaef420af5aecbea9c74341b8bb69eeda4d Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 17:42:23 +0800 Subject: [PATCH 13/13] refactor(framework): promote relayNodes to instance field in PeerConnection Extract relayNodes from a local variable inside setChannel() to a lazy- initialized instance field. setChannel() now only fetches from Args when the field is still null, so tests can pre-inject a custom list via reflection before calling setChannel(). This restores testability of PeerManagerTest without touching the test itself. Co-Authored-By: Claude Sonnet 4.6 --- .../main/java/org/tron/core/net/peer/PeerConnection.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java index ebd4a3827ad..ef2c7d5983a 100644 --- a/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java +++ b/framework/src/main/java/org/tron/core/net/peer/PeerConnection.java @@ -161,10 +161,14 @@ public class PeerConnection { private volatile boolean needSyncFromUs = true; @Getter private P2pRateLimiter p2pRateLimiter = new P2pRateLimiter(); + @Getter + private List relayNodes; public void setChannel(Channel channel) { this.channel = channel; - List relayNodes = Args.getInstance().getFastForwardNodes(); + if (this.relayNodes == null) { + this.relayNodes = Args.getInstance().getFastForwardNodes(); + } if (relayNodes != null && relayNodes.stream().anyMatch(n -> n.getAddress().equals(channel.getInetAddress()))) { this.isRelayPeer = true;