From 5d4db4d561bbd0ad69cb499f53c00912220358d6 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 16:23:02 +0800 Subject: [PATCH 01/16] 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 ea477ce5b0ec940d11969aa62a91d6b10d8f6d4d Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 16:33:52 +0800 Subject: [PATCH 02/16] 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 4321e3d1822f52a775efe6b13a8a8acd35775920 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 6 Apr 2026 17:29:08 +0800 Subject: [PATCH 03/16] 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 d92db8189e34c994f4d147c8f81301f7f210a939 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 12:10:29 +0800 Subject: [PATCH 04/16] 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 61aadd4c202ea53ed52316b548a1b652484de310 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 13:57:41 +0800 Subject: [PATCH 05/16] 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 f3dfd3c3163410a29ce5e6335e002dd6486e4e28 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Tue, 7 Apr 2026 14:03:49 +0800 Subject: [PATCH 06/16] 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 8b721fc955c9899105678b48d79afe44c5b72595 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:20:44 +0800 Subject: [PATCH 07/16] 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 85dddb6779e3da3ddd2e658d1754d8fe6110b2e4 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:25:28 +0800 Subject: [PATCH 08/16] 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 ed551eeb07795c3e3a5e667f396fb1fedb142a8d Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:31:35 +0800 Subject: [PATCH 09/16] 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 50998e687bf074f115c2510a971d6c1e6a45818a Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 15:45:47 +0800 Subject: [PATCH 10/16] 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 036abb9b178fab4e94e0d850d1a6e8c5bda114d0 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 16:21:24 +0800 Subject: [PATCH 11/16] 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 c4bb31501ce96d4fa739dd28794c3793b8262c87 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 17:22:34 +0800 Subject: [PATCH 12/16] 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 e9cdd01427e770fd707db99b2e390314e5cd94e5 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Wed, 8 Apr 2026 17:42:23 +0800 Subject: [PATCH 13/16] 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; From b1733119324117332ac61b683a7dcc0efdc2cbe6 Mon Sep 17 00:00:00 2001 From: Asuka Date: Tue, 31 Mar 2026 12:05:06 +0800 Subject: [PATCH 14/16] refactor(tvm): optimize energy cost calculation for vote witness opcode Use BigInteger for memory size computation to improve precision and add unit tests. Co-Authored-By: Claude Opus 4.6 --- .../java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java b/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java index 66de45a0658..75b11f4ab9d 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java @@ -124,8 +124,6 @@ public void testZeroLengthOneArray() { // witness array zero, amount array non-zero Program program = mockProgram(0, 0, 64, 1, 0); long cost = EnergyCost.getVoteWitnessCost3(program); - // witnessArraySize = 0 * 32 + 32 = 32, witnessMemNeeded = 0 + 32 = 32 - // amountArraySize = 1 * 32 + 32 = 64, amountMemNeeded = 64 + 64 = 128 // memWords = 128 / 32 = 4 // memEnergy = 3 * 4 + 4 * 4 / 512 = 12 assertEquals(30012, cost); From 21b16ea7c0a423edd281d909821a478fb717e1bf Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 13 Apr 2026 13:08:40 +0800 Subject: [PATCH 15/16] fix(framework): replace while(true) with while(flag) in SolidityNode getBlockByNum() and getLastSolidityBlockNum() used while(true) which ignores the shutdown flag. If either method is blocked retrying on network errors, shutdown() cannot interrupt the thread, causing ExecutorServiceManager.shutdownAndAwaitTermination to time out. Co-Authored-By: Claude Sonnet 4.6 --- .../java/org/tron/program/SolidityNode.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index de0bdaea1de..db7972bd4af 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -81,15 +81,15 @@ public static void start() { 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(); - } - } + 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 { @@ -161,7 +161,7 @@ private void loopProcessBlock(Block block) { } private Block getBlockByNum(long blockNum) { - while (true) { + while (flag) { try { long time = System.currentTimeMillis(); Block block = databaseGrpcClient.getBlock(blockNum); @@ -182,7 +182,7 @@ private Block getBlockByNum(long blockNum) { } private long getLastSolidityBlockNum() { - while (true) { + while (flag) { try { long time = System.currentTimeMillis(); long blockNum = databaseGrpcClient.getDynamicProperties().getLastSolidityBlockNum(); From 669b1f264480f90ca719070b5fe422bca2f924b8 Mon Sep 17 00:00:00 2001 From: GrapeS Date: Mon, 13 Apr 2026 13:09:02 +0800 Subject: [PATCH 16/16] fix(framework): add null/0 return after while(flag) loops in SolidityNode Compiler requires explicit return values when while(flag) can exit without entering the loop body (e.g. during shutdown). Co-Authored-By: Claude Sonnet 4.6 --- framework/src/main/java/org/tron/program/SolidityNode.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index db7972bd4af..f1c5bab0dce 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -179,6 +179,7 @@ private Block getBlockByNum(long blockNum) { sleep(exceptionSleepTime); } } + return null; } private long getLastSolidityBlockNum() { @@ -195,6 +196,7 @@ private long getLastSolidityBlockNum() { sleep(exceptionSleepTime); } } + return 0; } public void sleep(long time) {