diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index f35538c0961..9ba1cdf55fc 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -52,7 +52,11 @@ jobs: restore-keys: macos26-${{ matrix.arch }}-gradle- - name: Build - run: ./gradlew clean build --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew clean build --no-daemon --console=plain 2>&1 | tee ci-logs/build.log - name: Toolkit jar smoke test run: | @@ -63,6 +67,17 @@ jobs: java -jar "$JAR" db archive -h java -jar "$JAR" keystore --help + - name: Upload test diagnostics + if: failure() + uses: actions/upload-artifact@v6 + with: + name: tron-test-logs-${{ github.job }}-jdk${{ matrix.java }}-${{ matrix.arch }} + path: | + **/logs/tron-test.log + ci-logs/*.log + if-no-files-found: warn + retention-days: 7 + build-ubuntu: name: Build ubuntu24 (JDK 17 / aarch64) if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'ubuntu' }} @@ -91,7 +106,11 @@ jobs: restore-keys: ubuntu24-aarch64-gradle- - name: Build - run: ./gradlew clean build --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew clean build --no-daemon --console=plain 2>&1 | tee ci-logs/build.log - name: Toolkit jar smoke test run: | @@ -102,6 +121,17 @@ jobs: java -jar "$JAR" db archive -h java -jar "$JAR" keystore --help + - name: Upload test diagnostics + if: failure() + uses: actions/upload-artifact@v6 + with: + name: tron-test-logs-${{ github.job }} + path: | + **/logs/tron-test.log + ci-logs/*.log + if-no-files-found: warn + retention-days: 7 + docker-build-rockylinux: name: Build rockylinux (JDK 8 / x86_64) if: ${{ github.event_name == 'pull_request' || inputs.job == 'all' || inputs.job == 'rockylinux' }} @@ -113,15 +143,27 @@ jobs: env: GRADLE_USER_HOME: /github/home/.gradle - LANG: en_US.UTF-8 - LC_ALL: en_US.UTF-8 + LANG: C.utf8 + LC_ALL: C.utf8 steps: - name: Install dependencies (Rocky 8 + JDK8) run: | set -euxo pipefail - dnf -y install java-1.8.0-openjdk-devel git wget unzip which jq bc curl glibc-langpack-en - dnf -y groupinstall "Development Tools" + # Rocky 8 already provides CA certificates, JNI runtime libraries, tar and gzip. + # Its built-in C.utf8 locale provides UTF-8 without an extra language pack. + # git-core provides checkout commands; zstd supports actions/cache compression. + # Download more packages concurrently when mirror requests are slow. + dnf -y --setopt=install_weak_deps=False --setopt=max_parallel_downloads=10 install \ + java-1.8.0-openjdk-devel git-core zstd + # Set JAVA_HOME so the Gradle wrapper does not need which. + javac_path=$(command -v javac) + javac_real=$(readlink -f "$javac_path") + jdk_bin=$(dirname "$javac_real") + jdk_home=$(dirname "$jdk_bin") + test -x "$jdk_home/bin/java" + test -x "$jdk_home/bin/javac" + printf 'JAVA_HOME=%s\n' "$jdk_home" >> "$GITHUB_ENV" - name: Checkout code uses: actions/checkout@v5 @@ -143,7 +185,11 @@ jobs: run: ./gradlew --stop || true - name: Build - run: ./gradlew clean build --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew clean build --no-daemon --console=plain 2>&1 | tee ci-logs/build.log - name: Toolkit jar smoke test run: | @@ -155,7 +201,22 @@ jobs: java -jar "$JAR" keystore --help - name: Test with RocksDB engine - run: ./gradlew :framework:testWithRocksDb --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew :framework:testWithRocksDb --no-daemon --console=plain 2>&1 | tee ci-logs/rocksdb-test.log + + - name: Upload test diagnostics + if: failure() + uses: actions/upload-artifact@v6 + with: + name: tron-test-logs-${{ github.job }} + path: | + **/logs/tron-test.log + ci-logs/*.log + if-no-files-found: warn + retention-days: 7 docker-build-debian11: name: Build debian11 (JDK 8 / x86_64) @@ -197,7 +258,11 @@ jobs: debian11-x86_64-gradle- - name: Build - run: ./gradlew clean build --no-daemon --no-build-cache + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew clean build --no-daemon --no-build-cache --console=plain 2>&1 | tee ci-logs/build.log - name: Toolkit jar smoke test run: | @@ -209,10 +274,18 @@ jobs: java -jar "$JAR" keystore --help - name: Test with RocksDB engine - run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache --console=plain 2>&1 | tee ci-logs/rocksdb-test.log - name: Generate module coverage reports - run: ./gradlew jacocoTestReport --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew jacocoTestReport --no-daemon --console=plain 2>&1 | tee ci-logs/coverage.log - name: Upload PR coverage reports uses: actions/upload-artifact@v6 @@ -222,6 +295,17 @@ jobs: **/build/reports/jacoco/test/jacocoTestReport.xml if-no-files-found: error + - name: Upload test diagnostics + if: failure() + uses: actions/upload-artifact@v6 + with: + name: tron-test-logs-${{ github.job }} + path: | + **/logs/tron-test.log + ci-logs/*.log + if-no-files-found: warn + retention-days: 7 + coverage-base: name: Coverage Base (JDK 8 / x86_64) if: ${{ github.event_name == 'pull_request' }} @@ -260,19 +344,33 @@ jobs: coverage-base-x86_64-gradle- - name: Build (base) + id: base_build # Test failures on the base branch are tolerated: merge-order races can # leave the base with a pre-existing failing test that is unrelated to # this PR. The only output we need from this job is the jacoco XML for # coverage diffing, so we must not let a stale test failure block it. continue-on-error: true - run: ./gradlew clean build --no-daemon --no-build-cache + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew clean build --no-daemon --no-build-cache --console=plain 2>&1 | tee ci-logs/build.log - name: Test with RocksDB engine (base) + id: base_rocksdb_test continue-on-error: true - run: ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew :framework:testWithRocksDb --no-daemon --no-build-cache --console=plain 2>&1 | tee ci-logs/rocksdb-test.log - name: Generate module coverage reports (base) - run: ./gradlew jacocoTestReport --no-daemon + shell: bash + run: | + set -euo pipefail + mkdir -p ci-logs + ./gradlew jacocoTestReport --no-daemon --console=plain 2>&1 | tee ci-logs/coverage.log - name: Upload base coverage reports uses: actions/upload-artifact@v6 @@ -282,6 +380,18 @@ jobs: **/build/reports/jacoco/test/jacocoTestReport.xml if-no-files-found: warn + - name: Upload test diagnostics + # Preserve logs for test failures tolerated by continue-on-error above. + if: ${{ failure() || steps.base_build.outcome == 'failure' || steps.base_rocksdb_test.outcome == 'failure' }} + uses: actions/upload-artifact@v6 + with: + name: tron-test-logs-${{ github.job }} + path: | + **/logs/tron-test.log + ci-logs/*.log + if-no-files-found: warn + retention-days: 7 + coverage-gate: name: Coverage Gate needs: [docker-build-debian11, coverage-base] diff --git a/.github/workflows/pr-cancel.yml b/.github/workflows/pr-cancel.yml index 3213026d3f9..a4d46153d47 100644 --- a/.github/workflows/pr-cancel.yml +++ b/.github/workflows/pr-cancel.yml @@ -21,7 +21,6 @@ jobs: 'pr-build.yml', 'codeql.yml', 'integration-test-single-node.yml', - 'integration-test-multinode.yml', ]; const headSha = context.payload.pull_request.head.sha; const prNumber = context.payload.pull_request.number; diff --git a/framework/build.gradle b/framework/build.gradle index 8255fc30d18..5fbbbbc8916 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -1,5 +1,5 @@ plugins { - id "org.gradle.test-retry" version "1.5.9" + // id "org.gradle.test-retry" version "1.5.9" id "org.sonarqube" version "2.6" id "com.gorylenko.gradle-git-properties" version "2.4.1" } @@ -110,10 +110,10 @@ run { } def configureTestTask = { Task t -> - t.retry { - maxRetries = 5 - maxFailures = 20 - } + // t.retry { + // maxRetries = 5 + // maxFailures = 20 + // } t.testLogging { exceptionFormat = 'full' } diff --git a/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java b/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java index 67739ac50d2..08846eacc2c 100644 --- a/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java +++ b/framework/src/main/java/org/tron/common/backup/socket/BackupServer.java @@ -27,7 +27,7 @@ public class BackupServer implements AutoCloseable { private BackupManager backupManager; - private Channel channel; + private volatile Channel channel; private volatile boolean shutdown = false; @@ -75,6 +75,12 @@ public void initChannel(NioDatagramChannel ch) channel = b.bind(port).sync().channel(); + // close() may have run while bind was still in progress. + if (shutdown) { + channel.close().sync(); + break; + } + logger.info("Backup server started, bind port {}", port); channel.closeFuture().sync(); diff --git a/framework/src/test/java/com/google/common/util/concurrent/FakeTimeRateLimiter.java b/framework/src/test/java/com/google/common/util/concurrent/FakeTimeRateLimiter.java new file mode 100644 index 00000000000..137d6aa21b8 --- /dev/null +++ b/framework/src/test/java/com/google/common/util/concurrent/FakeTimeRateLimiter.java @@ -0,0 +1,35 @@ +package com.google.common.util.concurrent; + +import org.tron.common.math.StrictMathWrapper; + +/** Test-only clock for exercising real Guava permit accounting without wall-clock sleeps. */ +public final class FakeTimeRateLimiter { + + private FakeTimeRateLimiter() { + } + + public static RateLimiter create(double permitsPerSecond) { + return RateLimiter.create(permitsPerSecond, new Stopwatch()); + } + + public static RateLimiter createWithStoredPermit(double permitsPerSecond) { + Stopwatch clock = new Stopwatch(); + RateLimiter limiter = RateLimiter.create(permitsPerSecond, clock); + clock.sleepMicrosUninterruptibly((long) StrictMathWrapper.ceil(1_000_000 / permitsPerSecond)); + return limiter; + } + + private static final class Stopwatch extends RateLimiter.SleepingStopwatch { + private long micros; + + @Override + protected long readMicros() { + return micros; + } + + @Override + protected void sleepMicrosUninterruptibly(long sleepMicros) { + micros += sleepMicros; + } + } +} diff --git a/framework/src/test/java/org/tron/common/BaseMethodTest.java b/framework/src/test/java/org/tron/common/BaseMethodTest.java index 9ee1dfa3b36..6fc02d3badc 100644 --- a/framework/src/test/java/org/tron/common/BaseMethodTest.java +++ b/framework/src/test/java/org/tron/common/BaseMethodTest.java @@ -39,6 +39,9 @@ @Slf4j public abstract class BaseMethodTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -77,11 +80,17 @@ protected void afterInit() { @After public final void destroyContext() { - beforeDestroy(); - if (context != null) { - context.close(); // triggers appT.shutdown() via TronApplicationContext + try { + beforeDestroy(); + } finally { + try { + if (context != null) { + context.close(); // triggers appT.shutdown() via TronApplicationContext + } + } finally { + Args.clearParam(); + } } - Args.clearParam(); } protected void beforeDestroy() { diff --git a/framework/src/test/java/org/tron/common/BaseMethodTestLifecycleTest.java b/framework/src/test/java/org/tron/common/BaseMethodTestLifecycleTest.java new file mode 100644 index 00000000000..9cf2398b4a1 --- /dev/null +++ b/framework/src/test/java/org/tron/common/BaseMethodTestLifecycleTest.java @@ -0,0 +1,31 @@ +package org.tron.common; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.tron.common.application.TronApplicationContext; +import org.tron.core.config.args.Args; + +public class BaseMethodTestLifecycleTest { + + @Test + public void closesContextWhenSubclassCleanupFails() { + BaseMethodTest fixture = new BaseMethodTest() { + @Override + protected void beforeDestroy() { + throw new IllegalStateException("intentional cleanup failure"); + } + }; + fixture.context = Mockito.mock(TronApplicationContext.class); + Args.setParam(new String[0], TestConstants.TEST_CONF); + try { + IllegalStateException failure = Assert.assertThrows(IllegalStateException.class, + fixture::destroyContext); + Assert.assertEquals("intentional cleanup failure", failure.getMessage()); + Mockito.verify(fixture.context).close(); + Assert.assertEquals(0, Args.getInstance().getHttpMaxMessageSize()); + } finally { + Args.clearParam(); + } + } +} diff --git a/framework/src/test/java/org/tron/common/BaseTest.java b/framework/src/test/java/org/tron/common/BaseTest.java index 6d075a2d6aa..23967be542e 100644 --- a/framework/src/test/java/org/tron/common/BaseTest.java +++ b/framework/src/test/java/org/tron/common/BaseTest.java @@ -8,6 +8,7 @@ import org.junit.AfterClass; import org.junit.Assert; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.springframework.test.annotation.DirtiesContext; @@ -61,6 +62,9 @@ @DirtiesContext public abstract class BaseTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + @ClassRule public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -84,15 +88,20 @@ public static String dbPath() { try { return temporaryFolder.newFolder().toString(); } catch (IOException e) { - Assert.fail("create temp folder failed"); + throw new AssertionError("create temp folder failed", e); } - return null; } @AfterClass public static void destroy() { - appT1.shutdown(); - Args.clearParam(); + try { + if (appT1 != null) { + appT1.shutdown(); + } + } finally { + appT1 = null; + Args.clearParam(); + } } public void closePeer() { diff --git a/framework/src/test/java/org/tron/common/VMConfigRule.java b/framework/src/test/java/org/tron/common/VMConfigRule.java new file mode 100644 index 00000000000..6159b525e38 --- /dev/null +++ b/framework/src/test/java/org/tron/common/VMConfigRule.java @@ -0,0 +1,40 @@ +package org.tron.common; + +import java.lang.reflect.Field; +import org.junit.rules.ExternalResource; +import org.tron.common.parameter.CommonParameter; +import org.tron.core.vm.config.ConfigLoader; +import org.tron.core.vm.config.VMConfig; + +/** Restores VM flags after each test, including failed setup and assertion paths. */ +public class VMConfigRule extends ExternalResource { + + private VMConfig.Snapshot savedSnapshot; + private boolean savedLoaderDisabled; + private boolean savedHardFork; + private boolean savedTrace; + + @Override + protected void before() throws Exception { + Field global = VMConfig.class.getDeclaredField("globalSnapshot"); + global.setAccessible(true); + VMConfig.Snapshot current = (VMConfig.Snapshot) global.get(null); + savedSnapshot = new VMConfig.Snapshot(); + // init* methods mutate the snapshot in place, so saving only its reference is insufficient. + for (Field flag : VMConfig.Snapshot.class.getFields()) { + flag.set(savedSnapshot, flag.get(current)); + } + savedLoaderDisabled = ConfigLoader.disable; + savedHardFork = CommonParameter.ENERGY_LIMIT_HARD_FORK; + savedTrace = VMConfig.vmTrace(); + VMConfig.clearLocalSnapshot(); + } + + @Override + protected void after() { + VMConfig.setGlobalSnapshot(savedSnapshot); + ConfigLoader.disable = savedLoaderDisabled; + VMConfig.initVmHardFork(savedHardFork); + VMConfig.setVmTrace(savedTrace); + } +} diff --git a/framework/src/test/java/org/tron/common/VMConfigRuleTest.java b/framework/src/test/java/org/tron/common/VMConfigRuleTest.java new file mode 100644 index 00000000000..2ae0c391618 --- /dev/null +++ b/framework/src/test/java/org/tron/common/VMConfigRuleTest.java @@ -0,0 +1,90 @@ +package org.tron.common; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.Statement; +import org.tron.core.vm.config.ConfigLoader; +import org.tron.core.vm.config.VMConfig; + +public class VMConfigRuleTest { + + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + + @Before + public void seedConfig() { + VMConfig.Snapshot snapshot = new VMConfig.Snapshot(); + snapshot.allowTvmOsaka = true; + VMConfig.setGlobalSnapshot(snapshot); + ConfigLoader.disable = false; + } + + @Test + public void restoresConfigAfterAssertionFailure() { + Statement failingTest = new Statement() { + @Override + public void evaluate() { + polluteConfig(); + throw new AssertionError("intentional failure"); + } + }; + AssertionError failure = Assert.assertThrows(AssertionError.class, + () -> new VMConfigRule().apply(failingTest, Description.EMPTY).evaluate()); + Assert.assertEquals("intentional failure", failure.getMessage()); + assertConfigRestored(); + } + + @Test + public void restoresConfigAfterSetupFailure() throws Exception { + // Explicitly run the fixture while normal test discovery honors its class-level @Ignore. + Result result = new JUnitCore().run(new BlockJUnit4ClassRunner(FailingSetup.class)); + Assert.assertEquals(1, result.getRunCount()); + Assert.assertEquals(1, result.getFailureCount()); + Assert.assertEquals("intentional setup failure", result.getFailures().get(0).getMessage()); + assertConfigRestored(); + } + + @Test + public void skipsFixtureDuringNormalDiscovery() { + Result result = JUnitCore.runClasses(FailingSetup.class); + Assert.assertEquals(0, result.getRunCount()); + Assert.assertEquals(0, result.getFailureCount()); + Assert.assertEquals(1, result.getIgnoreCount()); + assertConfigRestored(); + } + + private static void polluteConfig() { + VMConfig.initAllowTvmOsaka(0); + ConfigLoader.disable = true; + VMConfig.setLocalSnapshot(new VMConfig.Snapshot()); + } + + private static void assertConfigRestored() { + Assert.assertTrue("Global snapshot or thread-local view leaked", VMConfig.allowTvmOsaka()); + Assert.assertFalse("Config loader switch leaked", ConfigLoader.disable); + } + + @Ignore("Failure fixture executed explicitly by VMConfigRuleTest") + public static class FailingSetup { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + + @Before + public void setUp() { + polluteConfig(); + throw new IllegalStateException("intentional setup failure"); + } + + @Test + public void body() { + Assert.fail("Body must not run after failed setup"); + } + } +} diff --git a/framework/src/test/java/org/tron/common/backup/BackupManagerTest.java b/framework/src/test/java/org/tron/common/backup/BackupManagerTest.java index 5ff02fc8cb5..20ee12128cb 100644 --- a/framework/src/test/java/org/tron/common/backup/BackupManagerTest.java +++ b/framework/src/test/java/org/tron/common/backup/BackupManagerTest.java @@ -9,8 +9,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import org.junit.After; import org.junit.Assert; @@ -18,10 +18,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.tron.common.TestConstants; import org.tron.common.backup.BackupManager.BackupStatusEnum; import org.tron.common.backup.message.KeepAliveMessage; import org.tron.common.backup.socket.BackupServer; +import org.tron.common.backup.socket.MessageHandler; import org.tron.common.backup.socket.UdpEvent; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.PublicMethod; @@ -48,8 +51,16 @@ public void setUp() throws Exception { @After public void tearDown() { - InetUtil.dnsLookup = savedLookup; - Args.clearParam(); + try { + if (backupServer != null) { + backupServer.close(); + } else if (manager != null) { + manager.stop(); + } + } finally { + InetUtil.dnsLookup = savedLookup; + Args.clearParam(); + } } @Test @@ -133,21 +144,27 @@ public void testSendKeepAliveMessage() throws Exception { field.set(manager, "127.0.0.1"); Assert.assertEquals(manager.getStatus(), BackupManager.BackupStatusEnum.MASTER); - backupServer.initServer(); + ScheduledExecutorService scheduler = Mockito.mock(ScheduledExecutorService.class); + Mockito.when(scheduler.awaitTermination(Mockito.anyLong(), Mockito.any())) + .thenReturn(true); + Field schedulerField = manager.getClass().getDeclaredField("executorService"); + schedulerField.setAccessible(true); + ((ScheduledExecutorService) schedulerField.get(manager)).shutdownNow(); + schedulerField.set(manager, scheduler); + MessageHandler handler = Mockito.mock(MessageHandler.class); + manager.setMessageHandler(handler); manager.init(); - Thread.sleep(parameter.getKeepAliveInterval() + 1000);//test send KeepAliveMessage - - field = manager.getClass().getDeclaredField("executorService"); - field.setAccessible(true); - ScheduledExecutorService executorService = (ScheduledExecutorService) field.get(manager); - executorService.shutdown(); - - Field field2 = backupServer.getClass().getDeclaredField("executor"); - field2.setAccessible(true); - ExecutorService executorService2 = (ExecutorService) field2.get(backupServer); - executorService2.shutdown(); + ArgumentCaptor heartbeat = ArgumentCaptor.forClass(Runnable.class); + Mockito.verify(scheduler).scheduleWithFixedDelay(heartbeat.capture(), Mockito.eq(1000L), + Mockito.eq((long) parameter.getKeepAliveInterval()), Mockito.eq(TimeUnit.MILLISECONDS)); + heartbeat.getValue().run(); + ArgumentCaptor sent = ArgumentCaptor.forClass(UdpEvent.class); + Mockito.verify(handler).accept(sent.capture()); + Assert.assertEquals("127.0.0.2", sent.getValue().getAddress().getHostString()); + Assert.assertEquals(parameter.getBackupPort(), sent.getValue().getAddress().getPort()); + Assert.assertFalse(((KeepAliveMessage) sent.getValue().getMessage()).getFlag()); Assert.assertEquals(BackupManager.BackupStatusEnum.INIT, manager.getStatus()); } diff --git a/framework/src/test/java/org/tron/common/backup/BackupServerLifecycleTest.java b/framework/src/test/java/org/tron/common/backup/BackupServerLifecycleTest.java new file mode 100644 index 00000000000..4ccd0183236 --- /dev/null +++ b/framework/src/test/java/org/tron/common/backup/BackupServerLifecycleTest.java @@ -0,0 +1,105 @@ +package org.tron.common.backup; + +import static org.mockito.AdditionalAnswers.delegatesTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +import io.netty.channel.Channel; +import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.test.util.ReflectionTestUtils; +import org.tron.common.TestConstants; +import org.tron.common.backup.socket.BackupServer; +import org.tron.common.backup.socket.MessageHandler; +import org.tron.common.utils.PublicMethod; +import org.tron.core.config.args.Args; + +public class BackupServerLifecycleTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Before + public void setUp() throws Exception { + Args.setParam(new String[]{"-d", temporaryFolder.newFolder().toString()}, + TestConstants.TEST_CONF); + Args.getInstance().setBackupPort(PublicMethod.chooseRandomPort()); + Args.getInstance().setBackupMembers(Collections.singletonList("127.0.0.2")); + } + + @After + public void tearDown() { + Args.clearParam(); + } + + @Test(timeout = 30_000) + public void testCloseDuringBind() throws Exception { + BackupManager manager = mock(BackupManager.class); + CountDownLatch bindStarted = new CountDownLatch(1); + CountDownLatch allowBind = new CountDownLatch(1); + CountDownLatch closeWaiting = new CountDownLatch(1); + AtomicReference pendingChannel = new AtomicReference<>(); + doAnswer(invocation -> { + MessageHandler handler = invocation.getArgument(0); + pendingChannel.set((Channel) ReflectionTestUtils.getField(handler, "channel")); + bindStarted.countDown(); + if (!allowBind.await(10, TimeUnit.SECONDS)) { + throw new IllegalStateException("Timed out waiting to finish bind"); + } + return null; + }).when(manager).setMessageHandler(any(MessageHandler.class)); + + BackupServer server = new BackupServer(manager); + ExecutorService closer = Executors.newSingleThreadExecutor(); + ExecutorService worker = null; + try { + server.initServer(); + worker = (ExecutorService) ReflectionTestUtils.getField(server, "executor"); + Assert.assertTrue("Bind did not start", bindStarted.await(10, TimeUnit.SECONDS)); + ExecutorService delegate = worker; + ExecutorService observedWorker = mock(ExecutorService.class, delegatesTo(delegate)); + doAnswer(invocation -> { + delegate.shutdown(); + closeWaiting.countDown(); + return null; + }).when(observedWorker).shutdown(); + ReflectionTestUtils.setField(server, "executor", observedWorker); + + Future close = closer.submit(server::close); + // Ensure close() has checked the still-null channel before completing bind. + Assert.assertTrue("Close did not reach executor shutdown", + closeWaiting.await(5, TimeUnit.SECONDS)); + allowBind.countDown(); + close.get(5, TimeUnit.SECONDS); + Assert.assertFalse("The late-bound channel must be closed", pendingChannel.get().isOpen()); + Assert.assertTrue("The server worker must terminate", worker.isTerminated()); + } finally { + // Also release resources when running this regression against the broken implementation. + allowBind.countDown(); + Channel channel = pendingChannel.get(); + if (channel != null) { + channel.close().awaitUninterruptibly(5, TimeUnit.SECONDS); + } + if (worker != null) { + worker.shutdown(); + if (!worker.awaitTermination(5, TimeUnit.SECONDS)) { + worker.shutdownNow(); + } + } + closer.shutdownNow(); + closer.awaitTermination(5, TimeUnit.SECONDS); + } + } +} diff --git a/framework/src/test/java/org/tron/common/config/args/ArgsTest.java b/framework/src/test/java/org/tron/common/config/args/ArgsTest.java index 6081021c74f..a72b8ea6df3 100644 --- a/framework/src/test/java/org/tron/common/config/args/ArgsTest.java +++ b/framework/src/test/java/org/tron/common/config/args/ArgsTest.java @@ -61,7 +61,7 @@ public void testHelpMessage() { method.setAccessible(true); method.invoke(Args.class); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.printHelp(jCommander); } diff --git a/framework/src/test/java/org/tron/common/jetty/JettyServerTest.java b/framework/src/test/java/org/tron/common/jetty/JettyServerTest.java index fbb2721f502..b2f5bb4c694 100644 --- a/framework/src/test/java/org/tron/common/jetty/JettyServerTest.java +++ b/framework/src/test/java/org/tron/common/jetty/JettyServerTest.java @@ -2,10 +2,10 @@ import java.net.URI; import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.DefaultServlet; @@ -15,7 +15,6 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import org.tron.common.utils.PublicMethod; @Slf4j public class JettyServerTest { @@ -26,7 +25,7 @@ public class JettyServerTest { public static void startJetty() throws Exception { server = new Server(); ServerConnector connector = new ServerConnector(server); - connector.setPort(PublicMethod.chooseRandomPort()); + connector.setPort(0); server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(); @@ -53,12 +52,13 @@ public static void stopJetty() { @Test public void testGet() throws Exception { - HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(serverUri.resolve("/")); request.setHeader("Content-Length", "+450"); - HttpResponse mockResponse = client.execute(request); - Assert.assertTrue(mockResponse.getStatusLine().toString().contains( - "400 Invalid Content-Length Value")); + try (CloseableHttpClient client = HttpClients.createDefault(); + CloseableHttpResponse response = client.execute(request)) { + Assert.assertTrue(response.getStatusLine().toString().contains( + "400 Invalid Content-Length Value")); + } } } diff --git a/framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java b/framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java index 958af4f7b7b..9a7ddbe6fd3 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/EventLoaderTest.java @@ -15,6 +15,7 @@ import org.pf4j.PluginWrapper; import org.tron.common.logsfilter.trigger.BlockLogTrigger; import org.tron.common.logsfilter.trigger.TransactionLogTrigger; +import org.tron.common.utils.PublicMethod; public class EventLoaderTest { @@ -22,7 +23,7 @@ public class EventLoaderTest { public void launchNativeQueue() { EventPluginConfig config = new EventPluginConfig(); config.setSendQueueLength(1000); - config.setBindPort(5555); + config.setBindPort(PublicMethod.chooseRandomPort()); config.setUseNativeQueue(true); config.setPluginPath("pluginPath"); config.setServerAddress("serverAddress"); @@ -48,9 +49,12 @@ public void launchNativeQueue() { config.setTriggerConfigList(triggerConfigList); - assertTrue(EventPluginLoader.getInstance().start(config)); - - EventPluginLoader.getInstance().stopPlugin(); + EventPluginLoader loader = new EventPluginLoader(); + try { + assertTrue(loader.start(config)); + } finally { + loader.stopPlugin(); + } } @Test 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 5219654977b..397ee4b4e52 100644 --- a/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java +++ b/framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java @@ -1,87 +1,84 @@ package org.tron.common.logsfilter; -import java.util.concurrent.ExecutorService; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +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.mockito.MockedConstruction; import org.tron.common.logsfilter.nativequeue.NativeMessageQueue; +import org.tron.common.utils.PublicMethod; import org.zeromq.SocketType; import org.zeromq.ZContext; import org.zeromq.ZMQ; public class NativeMessageQueueTest { - public int bindPort = 5555; - public String dataToSend = "################"; - public String topic = "testTopic"; - - private ExecutorService subscriberExecutor; - private final String zmqSubscriber = "zmq-subscriber"; - @After public void tearDown() { - ExecutorServiceManager.shutdownAndAwaitTermination(subscriberExecutor, zmqSubscriber); - subscriberExecutor = null; + NativeMessageQueue.getInstance().stop(); } @Test public void invalidBindPort() { - boolean bRet = NativeMessageQueue.getInstance().start(-1111, 0); - Assert.assertEquals(true, bRet); - NativeMessageQueue.getInstance().stop(); + assertDefaultConfiguration(-1111, 0, 0); } @Test public void invalidSendLength() { - boolean bRet = NativeMessageQueue.getInstance().start(0, -2222); - Assert.assertEquals(true, bRet); - NativeMessageQueue.getInstance().stop(); + assertDefaultConfiguration(0, -2222, 1000); } - @Test - public void publishTrigger() { - - int sendLength = 0; - boolean bRet = NativeMessageQueue.getInstance().start(bindPort, sendLength); - Assert.assertEquals(true, bRet); - - startSubscribeThread(); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - NativeMessageQueue.getInstance().publishTrigger(dataToSend, topic); - - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + private void assertDefaultConfiguration(int port, int queueLength, int expectedQueueLength) { + ZMQ.Socket publisher = mock(ZMQ.Socket.class); + when(publisher.bind("tcp://*:5555")).thenReturn(true); + // Check fallback values without reserving the shared default port in a test JVM. + try (MockedConstruction contexts = mockConstruction(ZContext.class, + (context, construction) -> when(context.createSocket(SocketType.PUB)) + .thenReturn(publisher))) { + try { + Assert.assertTrue(NativeMessageQueue.getInstance().start(port, queueLength)); + verify(publisher).bind("tcp://*:5555"); + verify(contexts.constructed().get(0)).setSndHWM(expectedQueueLength); + } finally { + NativeMessageQueue.getInstance().stop(); + } + verify(publisher).close(); + verify(contexts.constructed().get(0)).close(); } - - NativeMessageQueue.getInstance().stop(); } - public void startSubscribeThread() { - subscriberExecutor = ExecutorServiceManager.newSingleThreadExecutor(zmqSubscriber); - subscriberExecutor.execute(() -> { - try (ZContext context = new ZContext()) { - ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); - - Assert.assertTrue(subscriber.connect(String.format("tcp://localhost:%d", bindPort))); - Assert.assertTrue(subscriber.subscribe(topic)); - - while (!Thread.currentThread().isInterrupted()) { - byte[] message = subscriber.recv(); - String triggerMsg = new String(message); - - Assert.assertTrue(triggerMsg.contains(dataToSend) || triggerMsg.contains(topic)); + @Test(timeout = 15_000) + public void publishTrigger() { + int bindPort = PublicMethod.chooseRandomPort(); + String dataToSend = "################"; + String topic = "testTopic"; + Assert.assertTrue(NativeMessageQueue.getInstance().start(bindPort, 0)); + + try (ZContext context = new ZContext()) { + ZMQ.Socket subscriber = context.createSocket(SocketType.SUB); + subscriber.setReceiveTimeOut(100); + Assert.assertTrue(subscriber.subscribe(topic)); + Assert.assertTrue(subscriber.connect(String.format("tcp://127.0.0.1:%d", bindPort))); + + // PUB/SUB subscription setup is asynchronous. Bound the wait and assert on this thread. + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); + while (System.nanoTime() < deadline) { + NativeMessageQueue.getInstance().publishTrigger(dataToSend, topic); + String receivedTopic = subscriber.recvStr(); + if (receivedTopic != null) { + Assert.assertEquals(topic, receivedTopic); + Assert.assertTrue(subscriber.hasReceiveMore()); + Assert.assertEquals(dataToSend, subscriber.recvStr()); + Assert.assertFalse(subscriber.hasReceiveMore()); + return; } - // ZMQ.Socket will be automatically closed when ZContext is closed } - }); + Assert.fail("Timed out waiting for the published trigger"); + } } } diff --git a/framework/src/test/java/org/tron/common/prometheus/SRMetricsTest.java b/framework/src/test/java/org/tron/common/prometheus/SRMetricsTest.java index 4c2e9292d29..547fc60c151 100644 --- a/framework/src/test/java/org/tron/common/prometheus/SRMetricsTest.java +++ b/framework/src/test/java/org/tron/common/prometheus/SRMetricsTest.java @@ -38,6 +38,8 @@ public class SRMetricsTest extends BaseTest { Args.setParam(new String[]{"-d", dbPath()}, TestConstants.TEST_CONF); Args.getInstance().setNodeListenPort(20000 + PORT.incrementAndGet()); Args.getInstance().setMetricsPrometheusEnable(true); + // Only the registry is asserted; let the OS allocate a port for each test JVM. + Args.getInstance().setMetricsPrometheusPort(0); Metrics.init(); } diff --git a/framework/src/test/java/org/tron/common/runtime/RuntimeImplTest.java b/framework/src/test/java/org/tron/common/runtime/RuntimeImplTest.java index 7fcdfae2753..5bd7537d233 100644 --- a/framework/src/test/java/org/tron/common/runtime/RuntimeImplTest.java +++ b/framework/src/test/java/org/tron/common/runtime/RuntimeImplTest.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.encoders.Hex; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -22,6 +23,7 @@ import org.tron.core.exception.ReceiptCheckErrException; import org.tron.core.exception.VMIllegalException; import org.tron.core.store.StoreFactory; +import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.repository.Repository; import org.tron.core.vm.repository.RepositoryImpl; import org.tron.protos.Protocol.AccountType; @@ -62,6 +64,12 @@ public void init() { repository.commit(); } + @After + public void clearConstantCallConfig() { + // Keep the snapshot for this test's energy assertions, then release it for the next test. + VMConfig.clearLocalSnapshot(); + } + // // solidity src code // pragma solidity ^0.4.2; // diff --git a/framework/src/test/java/org/tron/common/runtime/VMActuatorMockTest.java b/framework/src/test/java/org/tron/common/runtime/VMActuatorMockTest.java index 013ba606e9a..4f59549af45 100644 --- a/framework/src/test/java/org/tron/common/runtime/VMActuatorMockTest.java +++ b/framework/src/test/java/org/tron/common/runtime/VMActuatorMockTest.java @@ -8,9 +8,11 @@ import java.util.Collections; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; +import org.tron.common.VMConfigRule; import org.tron.common.runtime.vm.DataWord; import org.tron.common.runtime.vm.LogInfo; import org.tron.core.actuator.VMActuator; @@ -24,6 +26,9 @@ public class VMActuatorMockTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + @BeforeClass public static void init() { // Warm up the registry before VM execution timing starts. diff --git a/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java b/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java index 8849e114c94..b3f8d283b21 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/BatchValidateSignContractTest.java @@ -7,7 +7,9 @@ import org.apache.commons.lang3.tuple.Pair; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import org.tron.common.VMConfigRule; import org.tron.common.crypto.ECKey; import org.tron.common.crypto.Hash; import org.tron.common.utils.ByteUtil; @@ -22,6 +24,9 @@ @Slf4j public class BatchValidateSignContractTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + private static final String METHOD_SIGN = "batchvalidatesign(bytes32,bytes[],address[])"; private static final byte[] smellData; private static final byte[] longData; diff --git a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java index 5a58407f887..84d9af348a2 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/Create2Test.java @@ -216,7 +216,7 @@ private void testJsonRpc(byte[] actualContract, long loop) { tronJsonRpc.getStorageAt(ByteArray.toHexString(actualContract), "0", "latest"); Assert.assertEquals(loop, ByteArray.jsonHexToLong(res)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java index d5a50ea4f9d..a220a2c5d20 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/PrecompiledContractsTest.java @@ -283,7 +283,7 @@ public void proposalTest() { Assert.assertEquals(State.CANCELED, proposalCapsule.getState()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java b/framework/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java index 0cbdd43c3a1..b51d4653198 100644 --- a/framework/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java +++ b/framework/src/test/java/org/tron/common/runtime/vm/TransferToAccountTest.java @@ -31,6 +31,7 @@ import org.tron.core.exception.VMIllegalException; import org.tron.core.store.StoreFactory; import org.tron.core.vm.EnergyCost; +import org.tron.core.vm.config.VMConfig; import org.tron.core.vm.repository.RepositoryImpl; import org.tron.protos.Protocol.AccountType; import org.tron.protos.Protocol.Transaction; @@ -257,13 +258,27 @@ public void TransferTokenTest() VMActuator vmActuator = new VMActuator(true); - vmActuator.validate(context); - vmActuator.execute(context); + try { + vmActuator.validate(context); + vmActuator.execute(context); + } finally { + // Match Wallet's constant-call lifecycle, including validation/execution failures. + VMConfig.clearLocalSnapshot(); + } ProgramResult result = context.getProgramResult(); Assert.assertNull(result.getRuntimeError()); + // Later tests on this worker must observe global updates, not this call's snapshot. + boolean londonEnabled = VMConfig.allowTvmLondon(); + try { + VMConfig.initAllowTvmLondon(londonEnabled ? 0 : 1); + Assert.assertEquals(!londonEnabled, VMConfig.allowTvmLondon()); + } finally { + VMConfig.initAllowTvmLondon(londonEnabled ? 1 : 0); + } + } private byte[] deployTransferContract(long id) diff --git a/framework/src/test/java/org/tron/common/utils/FileUtilTest.java b/framework/src/test/java/org/tron/common/utils/FileUtilTest.java index c22e83760a1..62c4f563b31 100644 --- a/framework/src/test/java/org/tron/common/utils/FileUtilTest.java +++ b/framework/src/test/java/org/tron/common/utils/FileUtilTest.java @@ -8,27 +8,24 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.List; -import org.junit.After; -import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; public class FileUtilTest { + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + private Path tempDir; @Before public void setUp() throws IOException { - tempDir = Files.createTempDirectory("testDir"); + tempDir = temporaryFolder.newFolder("testDir").toPath(); Files.createFile(tempDir.resolve("file1.txt")); Files.createFile(tempDir.resolve("file2.txt")); @@ -37,19 +34,6 @@ public void setUp() throws IOException { Files.createFile(subDir.resolve("file3.txt")); } - @After - public void tearDown() throws IOException { - Files.walk(tempDir) - .sorted(Comparator.reverseOrder()) - .forEach(path -> { - try { - Files.delete(path); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } - @Test public void testRecursiveList() throws IOException { List files = FileUtil.recursiveList(tempDir.toString()); @@ -63,11 +47,10 @@ public void testRecursiveList() throws IOException { @Test public void testReadData_NormalFile() throws IOException { - Path tempFile = Files.createTempFile("testfile", ".txt"); + Path tempFile = Files.createFile(tempDir.resolve("testfile.txt")); try (FileWriter writer = new FileWriter(tempFile.toFile())) { writer.write("Hello, World!"); } - tempFile.toFile().deleteOnExit(); char[] buffer = new char[1024]; int len = readData(tempFile.toString(), buffer); @@ -86,43 +69,35 @@ public void testReadData_IOException() { @Test - public void testCreateFileIfNotExists() { - String existFile = "existsfile.txt"; + public void testCreateFileIfNotExists() throws IOException { + String existFile = tempDir.resolve("existsfile.txt").toString(); File file1 = new File(existFile); - try { - file1.createNewFile(); - } catch (IOException e) { - System.out.println("ignore this exception."); - } + assertTrue(file1.createNewFile()); assertTrue(file1.exists()); assertTrue(FileUtil.createDirIfNotExists(existFile)); assertTrue(file1.exists()); - String notExistFile = "notexistsfile.txt"; + String notExistFile = tempDir.resolve("notexistsfile.txt").toString(); File file2 = new File(notExistFile); assertTrue(!file2.exists()); assertTrue(FileUtil.createDirIfNotExists(notExistFile)); assertTrue(file2.exists()); - file1.delete(); - file2.delete(); } @Test public void testCreateDirIfNotExists() { - String existDir = "existsdir"; + String existDir = tempDir.resolve("existsdir").toString(); File fileDir1 = new File(existDir); fileDir1.mkdir(); assertTrue(fileDir1.exists()); assertTrue(FileUtil.createDirIfNotExists(existDir)); assertTrue(fileDir1.exists()); - String notExistDir = "notexistsdir"; + String notExistDir = tempDir.resolve("notexistsdir").toString(); File fileDir2 = new File(notExistDir); assertTrue(!fileDir2.exists()); assertTrue(FileUtil.createDirIfNotExists(notExistDir)); assertTrue(fileDir2.exists()); - fileDir1.delete(); - fileDir2.delete(); } diff --git a/framework/src/test/java/org/tron/common/utils/Sha256HashTest.java b/framework/src/test/java/org/tron/common/utils/Sha256HashTest.java index 0df72cc125d..f9aa787d1c2 100644 --- a/framework/src/test/java/org/tron/common/utils/Sha256HashTest.java +++ b/framework/src/test/java/org/tron/common/utils/Sha256HashTest.java @@ -5,15 +5,17 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import ch.qos.logback.core.util.FileUtil; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; +import java.util.ArrayList; import java.util.Arrays; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.IntStream; -import org.apache.commons.io.FileUtils; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; import org.tron.common.parameter.CommonParameter; @@ -55,33 +57,36 @@ public void testHash() throws IOException { } @Test - public void testMultiThreadingHash() { + public void testMultiThreadingHash() throws Exception { byte[] input = ByteArray.fromHexString("A0E11973395042BA3C0B52B4CDF4E15EA77818F275"); byte[] hash = ByteArray .fromHexString("CD5D4A7E8BE869C00E17F8F7712F41DBE2DDBD4D8EC36A7280CD578863717084"); - AtomicLong countFailed = new AtomicLong(0); - AtomicLong countAll = new AtomicLong(0); - IntStream.range(0, 7).parallel().forEach(index -> { - Thread thread = - new Thread(() -> { - for (int i = 0; i < 10000; i++) { - byte[] hash0 = Sha256Hash.hash(CommonParameter.getInstance() - .isECKeyCryptoEngine(), input); - countAll.incrementAndGet(); - if (!Arrays.equals(hash, hash0)) { - countFailed.incrementAndGet(); - Assert.fail(); - } + int workerCount = 7; + CountDownLatch ready = new CountDownLatch(workerCount); + ExecutorService executor = Executors.newFixedThreadPool(workerCount); + List> futures = new ArrayList<>(); + try { + for (int worker = 0; worker < workerCount; worker++) { + futures.add(executor.submit(() -> { + ready.countDown(); + assertTrue("Hash workers did not start", ready.await(10, TimeUnit.SECONDS)); + for (int i = 0; i < 10_000; i++) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException("Hash worker cancelled"); } - }); - thread.start(); - try { - thread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); + Assert.assertArrayEquals(hash, Sha256Hash.hash(true, input)); + } + return 10_000; + })); } - }); - assertEquals(70000, countAll.get()); - assertEquals(0, countFailed.get()); + int completed = 0; + for (Future future : futures) { + completed += future.get(30, TimeUnit.SECONDS); + } + assertEquals(70_000, completed); + } finally { + executor.shutdownNow(); + assertTrue("Hash workers did not stop", executor.awaitTermination(5, TimeUnit.SECONDS)); + } } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/BandwidthProcessorTest.java b/framework/src/test/java/org/tron/core/BandwidthProcessorTest.java index cf652af3650..4958579993b 100755 --- a/framework/src/test/java/org/tron/core/BandwidthProcessorTest.java +++ b/framework/src/test/java/org/tron/core/BandwidthProcessorTest.java @@ -683,7 +683,7 @@ public void sameTokenNameCloseConsumeSuccess() { } catch (AccountResourceInsufficientException e) { Assert.assertFalse(e instanceof AccountResourceInsufficientException); } catch (TooBigTransactionException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } finally { chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(OWNER_ADDRESS)); chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(TO_ADDRESS)); @@ -791,7 +791,7 @@ public void sameTokenNameOpenConsumeSuccess() { } catch (AccountResourceInsufficientException e) { Assert.assertFalse(e instanceof AccountResourceInsufficientException); } catch (TooBigTransactionException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } finally { chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(OWNER_ADDRESS)); chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(TO_ADDRESS)); @@ -862,7 +862,7 @@ public void sameTokenNameCloseTransferToAccountNotExist() { } catch (AccountResourceInsufficientException e) { Assert.assertFalse(e instanceof AccountResourceInsufficientException); } catch (TooBigTransactionException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } finally { chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(OWNER_ADDRESS)); chainBaseManager.getAccountStore().delete(ByteArray.fromHexString(TO_ADDRESS)); diff --git a/framework/src/test/java/org/tron/core/ShieldWalletTest.java b/framework/src/test/java/org/tron/core/ShieldWalletTest.java index 0353d260eff..d0ab1eab563 100644 --- a/framework/src/test/java/org/tron/core/ShieldWalletTest.java +++ b/framework/src/test/java/org/tron/core/ShieldWalletTest.java @@ -62,14 +62,14 @@ public void testCreateShieldedTransaction1() { try { JsonFormat.merge(transactionStr1, builder1, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { TransactionCapsule transactionCapsule = wallet.createShieldedTransaction(builder1.build()); Assert.assertNotNull(transactionCapsule); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -169,14 +169,14 @@ public void testCreateShieldedTransaction2() { try { JsonFormat.merge(transactionStr2, builder2, true); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { TransactionCapsule transactionCapsule = wallet.createShieldedTransaction(builder2.build()); Assert.assertNotNull(transactionCapsule); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -277,7 +277,7 @@ public void testCreateShieldedTransactionWithoutSpendAuthSig() { try { JsonFormat.merge(transactionStr3, builder3, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -285,7 +285,7 @@ public void testCreateShieldedTransactionWithoutSpendAuthSig() { builder3.build()); Assert.assertNotNull(transactionCapsule); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -295,7 +295,7 @@ public void testGetNewShieldedAddress() { ShieldedAddressInfo shieldedAddressInfo = wallet.getNewShieldedAddress(); Assert.assertNotNull(shieldedAddressInfo); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -329,7 +329,7 @@ public void testCreateShieldedContractParameters() throws ContractExeException { try { JsonFormat.merge(parameter, builder, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -337,7 +337,7 @@ public void testCreateShieldedContractParameters() throws ContractExeException { builder.build()); Assert.assertNotNull(shieldedTRC20Parameters); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -370,7 +370,7 @@ public void testCreateShieldedContractParameters2() throws ContractExeException try { JsonFormat.merge(parameter, builder, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } PrivateShieldedTRC20Parameters.Builder finalBuilder = builder; @@ -401,7 +401,7 @@ public void testCreateShieldedContractParameters2() throws ContractExeException try { JsonFormat.merge(parameter2, builder, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } PrivateShieldedTRC20Parameters.Builder finalBuilder1 = builder; @@ -443,7 +443,7 @@ public void testCreateShieldedContractParametersWithoutAsk() throws ContractExeE try { JsonFormat.merge(parameter, builder, false); } catch (ParseException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -451,7 +451,7 @@ public void testCreateShieldedContractParametersWithoutAsk() throws ContractExeE wallet1.createShieldedContractParametersWithoutAsk(builder.build()); Assert.assertNotNull(shieldedTRC20Parameters); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/WalletTest.java b/framework/src/test/java/org/tron/core/WalletTest.java index 9dbab338b67..ec8656c2876 100644 --- a/framework/src/test/java/org/tron/core/WalletTest.java +++ b/framework/src/test/java/org/tron/core/WalletTest.java @@ -722,7 +722,7 @@ public void testGetDelegatedResource() { Assert.assertEquals(0L, delegatedResourceList.getDelegatedResource(0).getExpireTimeForBandwidth()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java index 250f7b9dc01..5707a8801f2 100644 --- a/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/AccountPermissionUpdateActuatorTest.java @@ -169,7 +169,7 @@ private void processAndCheckInvalid(AccountPermissionUpdateActuator actuator, Assert.assertTrue(true); Assert.assertEquals(expectedMsg, e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -248,7 +248,7 @@ public void successUpdatePermissionKey() { Assert.assertEquals(activePermission1, activePermission); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } 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 c830cd091e6..748706a38ad 100644 --- a/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/FreezeBalanceActuatorTest.java @@ -145,7 +145,7 @@ public void testFreezeBalanceForBandwidth() { Assert.assertEquals(owner.getFrozenBalance(), frozenBalance); Assert.assertEquals(frozenBalance, owner.getTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -171,7 +171,7 @@ public void testFreezeBalanceForEnergy() { Assert.assertEquals(frozenBalance, owner.getEnergyFrozenBalance()); Assert.assertEquals(frozenBalance, owner.getTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -203,7 +203,7 @@ public void testFreezeDelegatedBalanceForBandwidthWithContractAddress() { } catch (ContractValidateException e) { Assert.assertEquals("Do not allow delegate resources to contract addresses", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -268,7 +268,7 @@ public void testFreezeDelegatedBalanceForBandwidth() { } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -399,7 +399,7 @@ public void testFreezeDelegatedBalanceForCpuSameNameTokenActive() { .contains(ByteString.copyFrom(ByteArray.fromHexString(OWNER_ADDRESS)))); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -438,7 +438,7 @@ public void testFreezeDelegatedBalanceForCpuSameNameTokenClose() { totalEnergyWeightAfter); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -458,7 +458,7 @@ public void freezeLessThanZero() { } catch (ContractValidateException e) { Assert.assertEquals("frozenBalance must be positive", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -479,7 +479,7 @@ public void freezeMoreThanBalance() { Assert.assertEquals( "frozenBalance must be less than or equal to accountBalance", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -500,7 +500,7 @@ public void invalidOwnerAddress() { } catch (ContractValidateException e) { Assert.assertEquals("Invalid address", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -522,7 +522,7 @@ public void invalidOwnerAccount() { Assert.assertEquals("Account[" + OWNER_ACCOUNT_INVALID + "] not exists", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -546,7 +546,7 @@ public void durationLessThanMin() { Assert.assertEquals("frozenDuration must be less than " + maxFrozenTime + " days " + "and more than " + minFrozenTime + " days", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -569,7 +569,7 @@ public void durationMoreThanMax() { Assert.assertEquals("frozenDuration must be less than " + maxFrozenTime + " days " + "and more than " + minFrozenTime + " days", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -590,7 +590,7 @@ public void lessThan1TrxTest() { Assert.assertEquals("frozenBalance must be greater than or equal to 1 TRX", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -616,7 +616,7 @@ public void frozenNumTest() { } catch (ContractValidateException e) { Assert.assertEquals("frozenCount must be 0 or 1", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -663,7 +663,7 @@ public void testFreezeBalanceForEnergyWithoutOldTronPowerAfterNewResourceModel() Assert.assertEquals(-1L, owner.getInstance().getOldTronPower()); Assert.assertEquals(0L, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -694,7 +694,7 @@ public void testFreezeBalanceForEnergyWithOldTronPowerAfterNewResourceModel() { Assert.assertEquals(100L, owner.getInstance().getOldTronPower()); Assert.assertEquals(100L, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -725,7 +725,7 @@ public void testFreezeBalanceForTronPowerWithOldTronPowerAfterNewResourceModel() Assert.assertEquals(100L, owner.getInstance().getOldTronPower()); Assert.assertEquals(frozenBalance + 100L, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/actuator/FreezeBalanceV2ActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/FreezeBalanceV2ActuatorTest.java index 92e7cfa78ca..58c43d3087f 100644 --- a/framework/src/test/java/org/tron/core/actuator/FreezeBalanceV2ActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/FreezeBalanceV2ActuatorTest.java @@ -138,7 +138,7 @@ public void testFreezeBalanceForBandwidth() { Assert.assertEquals(owner.getFrozenV2BalanceForBandwidth(), frozenBalance); Assert.assertEquals(frozenBalance, owner.getTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -163,7 +163,7 @@ public void testFreezeBalanceForEnergy() { Assert.assertEquals(frozenBalance, owner.getAllFrozenBalanceForEnergy()); Assert.assertEquals(frozenBalance, owner.getTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -183,7 +183,7 @@ public void freezeLessThanZero() { } catch (ContractValidateException e) { Assert.assertEquals("frozenBalance must be positive", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -203,7 +203,7 @@ public void freezeMoreThanBalance() { Assert.assertEquals("frozenBalance must be less than or equal to accountBalance", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -223,7 +223,7 @@ public void invalidOwnerAddress() { } catch (ContractValidateException e) { Assert.assertEquals("Invalid address", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -244,7 +244,7 @@ public void invalidOwnerAccount() { Assert.assertEquals("Account[" + OWNER_ACCOUNT_INVALID + "] not exists", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -264,7 +264,7 @@ public void lessThan1TrxTest() { Assert.assertEquals("frozenBalance must be greater than or equal to 1 TRX", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -310,7 +310,7 @@ public void testFreezeBalanceForEnergyWithoutOldTronPowerAfterNewResourceModel() Assert.assertEquals(-1L, owner.getInstance().getOldTronPower()); Assert.assertEquals(0L, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -341,7 +341,7 @@ public void testFreezeBalanceForEnergyWithOldTronPowerAfterNewResourceModel() { Assert.assertEquals(100L, owner.getInstance().getOldTronPower()); Assert.assertEquals(100L, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -372,7 +372,7 @@ public void testFreezeBalanceForTronPowerWithOldTronPowerAfterNewResourceModel() Assert.assertEquals(100L, owner.getTronPower()); Assert.assertEquals(frozenBalance + 100, owner.getAllTronPower()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/actuator/MarketCancelOrderActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/MarketCancelOrderActuatorTest.java index 4966ef67987..540c8530c22 100644 --- a/framework/src/test/java/org/tron/core/actuator/MarketCancelOrderActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/MarketCancelOrderActuatorTest.java @@ -290,7 +290,7 @@ public void notBelongToTheAccount() throws Exception { } catch (ContractValidateException e) { Assert.assertEquals("Order does not belong to the account!", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -332,7 +332,7 @@ public void noEnoughBalance() throws Exception { } catch (ContractValidateException e) { Assert.assertEquals("No enough balance !", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } finally { // reset fee dbManager.getDynamicPropertiesStore().saveMarketCancelFee(0L); diff --git a/framework/src/test/java/org/tron/core/actuator/UnfreezeBalanceActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/UnfreezeBalanceActuatorTest.java index 7f74ee3fcc5..75072079796 100644 --- a/framework/src/test/java/org/tron/core/actuator/UnfreezeBalanceActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/UnfreezeBalanceActuatorTest.java @@ -136,7 +136,7 @@ public void testUnfreezeBalanceForBandwidth() { totalNetWeightAfter + frozenBalance / 1000_000L); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -206,7 +206,7 @@ public void testUnfreezeSelfAndOthersForBandwidth() { logger.error("ContractValidateException", e); Assert.fail(); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } UnfreezeBalanceActuator actuator = new UnfreezeBalanceActuator(); @@ -221,7 +221,7 @@ public void testUnfreezeSelfAndOthersForBandwidth() { Assert.assertEquals(0, afterWeight); Assert.assertEquals(code.SUCESS, ret.getInstance().getRet()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } dbManager.getDynamicPropertiesStore().saveAllowNewReward(0); } @@ -259,7 +259,7 @@ public void testUnfreezeBalanceForEnergy() { Assert.assertEquals(totalEnergyWeightBefore, totalEnergyWeightAfter + frozenBalance / 1000_000L); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -346,7 +346,7 @@ public void testUnfreezeDelegatedBalanceForBandwidth() { delegatedResourceAccountIndexCapsuleReceiver.getFromAccountsList().size()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -414,7 +414,7 @@ public void testUnfreezeDelegatedBalanceForBandwidthWithDeletedReceiver() { "Receiver Account[41abd4b9367799eaa3197fecb144eb71de1e049150] does not exist", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } dbManager.getDynamicPropertiesStore().saveAllowTvmConstantinople(1); @@ -446,7 +446,7 @@ public void testUnfreezeDelegatedBalanceForBandwidthWithDeletedReceiver() { delegatedResourceAccountIndexCapsuleReceiver.getFromAccountsList().size()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -525,7 +525,7 @@ public void testUnfreezeDelegatedBalanceForBandwidthWithRecreatedReceiver() { "AcquiredDelegatedFrozenBalanceForBandwidth[10] < delegatedBandwidth[1000000000]", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); @@ -563,7 +563,7 @@ public void testUnfreezeDelegatedBalanceForBandwidthWithRecreatedReceiver() { logger.error("", e); Assert.fail(); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -630,7 +630,7 @@ public void testUnfreezeDelegatedBalanceForBandwidthSameTokenNameClose() { } catch (ContractValidateException e) { Assert.assertEquals("no frozenBalance(BANDWIDTH)", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -679,7 +679,7 @@ public void testUnfreezeDelegatedBalanceForCpu() { Assert.assertEquals(0L, ownerResult.getDelegatedFrozenBalanceForEnergy()); Assert.assertEquals(0L, receiverResult.getAllFrozenBalanceForEnergy()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -724,7 +724,7 @@ public void testUnfreezeDelegatedBalanceForCpuWithDeletedReceiver() { "Receiver Account[41abd4b9367799eaa3197fecb144eb71de1e049150] does not exist", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } dbManager.getDynamicPropertiesStore().saveAllowTvmConstantinople(1); @@ -740,7 +740,7 @@ public void testUnfreezeDelegatedBalanceForCpuWithDeletedReceiver() { Assert.assertEquals(0L, ownerResult.getTronPower()); Assert.assertEquals(0L, ownerResult.getDelegatedFrozenBalanceForEnergy()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -798,7 +798,7 @@ public void testUnfreezeDelegatedBalanceForCpuWithRecreatedReceiver() { "AcquiredDelegatedFrozenBalanceForEnergy[10] < delegatedEnergy[1000000000]", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } dbManager.getDynamicPropertiesStore().saveAllowShieldedTransaction(1); @@ -817,7 +817,7 @@ public void testUnfreezeDelegatedBalanceForCpuWithRecreatedReceiver() { receiver = dbManager.getAccountStore().get(receiver.createDbKey()); Assert.assertEquals(0, receiver.getAcquiredDelegatedFrozenBalanceForEnergy()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -842,7 +842,7 @@ public void invalidOwnerAddress() { } catch (ContractValidateException e) { Assert.assertEquals("Invalid address", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -868,7 +868,7 @@ public void invalidOwnerAccount() { Assert.assertEquals("Account[" + OWNER_ACCOUNT_INVALID + "] does not exist", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -886,7 +886,7 @@ public void noFrozenBalance() { } catch (ContractValidateException e) { Assert.assertEquals("no frozenBalance(BANDWIDTH)", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -911,7 +911,7 @@ public void notTimeToUnfreeze() { } catch (ContractValidateException e) { Assert.assertEquals("It's not time to unfreeze(BANDWIDTH).", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -938,7 +938,7 @@ public void testClearVotes() { Assert.assertNotNull(votesCapsule); Assert.assertEquals(0, votesCapsule.getNewVotes().size()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // if had votes @@ -957,7 +957,7 @@ public void testClearVotes() { Assert.assertNotNull(votesCapsule); Assert.assertEquals(0, votesCapsule.getNewVotes().size()); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -1078,7 +1078,7 @@ public void testUnfreezeBalanceForEnergyWithOldTronPowerAfterNewResourceModel() Assert.assertEquals(0L, owner.getVotesList().size()); Assert.assertEquals(owner.getInstance().getOldTronPower(), -1L); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -1111,7 +1111,7 @@ public void testUnfreezeBalanceForEnergyWithoutOldTronPowerAfterNewResourceModel Assert.assertEquals(1L, owner.getVotesList().size()); Assert.assertEquals(owner.getInstance().getOldTronPower(), -1L); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -1144,7 +1144,7 @@ public void testUnfreezeBalanceForTronPowerWithOldTronPowerAfterNewResourceModel Assert.assertEquals(0L, owner.getVotesList().size()); Assert.assertEquals(owner.getInstance().getOldTronPower(), -1L); } catch (ContractValidateException | ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuatorTest.java b/framework/src/test/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuatorTest.java index 40347f7c5fb..feb25f401da 100644 --- a/framework/src/test/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuatorTest.java +++ b/framework/src/test/java/org/tron/core/actuator/WithdrawExpireUnfreezeActuatorTest.java @@ -145,7 +145,7 @@ public void invalidOwnerAccount() { } catch (ContractValidateException e) { assertEquals("Account[" + OWNER_ACCOUNT_INVALID + "] not exists", e.getMessage()); } catch (ContractExeException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java index 16a3cb3a5bb..9f38e2ec813 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/ProposalUtilTest.java @@ -390,7 +390,7 @@ private void testEnergyAdjustmentProposal() { ProposalUtil.validator(dynamicPropertiesStore, forkUtils, ProposalType.ALLOW_ENERGY_ADJUSTMENT.getCode(), 1); } catch (Throwable t) { - Assert.fail(); + throw new AssertionError("Unexpected exception", t); } ProposalCapsule proposalCapsule = new ProposalCapsule(ByteString.empty(), 0); diff --git a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java index 54e611e0aac..d67afac2816 100644 --- a/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java +++ b/framework/src/test/java/org/tron/core/actuator/utils/TransactionUtilTest.java @@ -18,6 +18,11 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -446,21 +451,32 @@ public void estimateConsumeBandWidthSizeCorner() { } @Test - public void testConcurrentToString() throws InterruptedException { + public void testConcurrentToString() throws Exception { Transaction.Builder builder = Transaction.newBuilder(); TransactionCapsule trx = new TransactionCapsule(builder.build()); - List threadList = new ArrayList<>(); - int n = 10; - for (int i = 0; i < n; i++) { - threadList.add(new Thread(() -> trx.toString())); - } - for (int i = 0; i < n; i++) { - threadList.get(i).start(); - } - for (int i = 0; i < n; i++) { - threadList.get(i).join(); + int threadCount = 10; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + CountDownLatch start = new CountDownLatch(1); + List> results = new ArrayList<>(); + try { + for (int i = 0; i < threadCount; i++) { + results.add(executor.submit(() -> { + start.await(); + return trx.toString(); + })); + } + start.countDown(); + String expected = results.get(0).get(10, TimeUnit.SECONDS); + Assert.assertNotNull(expected); + for (Future result : results) { + Assert.assertEquals(expected, result.get(10, TimeUnit.SECONDS)); + } + } finally { + start.countDown(); + executor.shutdownNow(); + Assert.assertTrue("Concurrent toString workers did not terminate", + executor.awaitTermination(5, TimeUnit.SECONDS)); } - Assert.assertTrue(true); } @Test diff --git a/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java b/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java index b258fbf99a1..f59c10a9254 100644 --- a/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/BlockCapsuleTest.java @@ -8,6 +8,11 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.junit.AfterClass; import org.junit.Assert; @@ -278,19 +283,30 @@ public void testValidateSignatureThrowsForMalformedSignature() throws Exception } @Test - public void testConcurrentToString() throws InterruptedException { - List threadList = new ArrayList<>(); - int n = 10; - for (int i = 0; i < n; i++) { - threadList.add(new Thread(() -> blockCapsule0.toString())); - } - for (int i = 0; i < n; i++) { - threadList.get(i).start(); - } - for (int i = 0; i < n; i++) { - threadList.get(i).join(); + public void testConcurrentToString() throws Exception { + int threadCount = 10; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + CountDownLatch start = new CountDownLatch(1); + List> results = new ArrayList<>(); + try { + for (int i = 0; i < threadCount; i++) { + results.add(executor.submit(() -> { + start.await(); + return blockCapsule0.toString(); + })); + } + start.countDown(); + String expected = results.get(0).get(10, TimeUnit.SECONDS); + Assert.assertNotNull(expected); + for (Future result : results) { + Assert.assertEquals(expected, result.get(10, TimeUnit.SECONDS)); + } + } finally { + start.countDown(); + executor.shutdownNow(); + Assert.assertTrue("Concurrent toString workers did not terminate", + executor.awaitTermination(5, TimeUnit.SECONDS)); } - Assert.assertTrue(true); } } diff --git a/framework/src/test/java/org/tron/core/capsule/ContractStateCapsuleTest.java b/framework/src/test/java/org/tron/core/capsule/ContractStateCapsuleTest.java index c90ad89abb3..8c931b49aa5 100644 --- a/framework/src/test/java/org/tron/core/capsule/ContractStateCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/ContractStateCapsuleTest.java @@ -2,8 +2,10 @@ import org.junit.After; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mockito; +import org.tron.common.VMConfigRule; import org.tron.core.config.args.Args; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.vm.config.VMConfig; @@ -11,6 +13,9 @@ public class ContractStateCapsuleTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + @Test public void testCatchUpCycle() { ContractStateCapsule capsule = new ContractStateCapsule( diff --git a/framework/src/test/java/org/tron/core/capsule/ExchangeCapsuleTest.java b/framework/src/test/java/org/tron/core/capsule/ExchangeCapsuleTest.java index 42dd0438593..d0d88f1641f 100644 --- a/framework/src/test/java/org/tron/core/capsule/ExchangeCapsuleTest.java +++ b/framework/src/test/java/org/tron/core/capsule/ExchangeCapsuleTest.java @@ -136,7 +136,7 @@ public void testExchange() throws ContractValidateException { Assert.assertEquals(buyBalance, exchangeCapsule.getSecondTokenBalance()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/config/TronLogShutdownHookTest.java b/framework/src/test/java/org/tron/core/config/TronLogShutdownHookTest.java index 85ade6ba7fa..cb10dcfeb11 100644 --- a/framework/src/test/java/org/tron/core/config/TronLogShutdownHookTest.java +++ b/framework/src/test/java/org/tron/core/config/TronLogShutdownHookTest.java @@ -1,10 +1,13 @@ package org.tron.core.config; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -12,6 +15,8 @@ public class TronLogShutdownHookTest { private boolean originalShutDown; + private Thread runner; + private final AtomicReference failure = new AtomicReference<>(); @Before public void saveShutDownFlag() { @@ -19,66 +24,67 @@ public void saveShutDownFlag() { } @After - public void restoreShutDownFlag() { - TronLogShutdownHook.shutDown = originalShutDown; + public void restoreShutDownFlag() throws InterruptedException { + try { + if (runner != null) { + runner.interrupt(); + runner.join(5000); + assertFalse("Shutdown hook worker leaked", runner.isAlive()); + } + assertNull("Shutdown hook worker failed", failure.get()); + } finally { + TronLogShutdownHook.shutDown = originalShutDown; + } } - @Test(timeout = 5_000) + @Test(timeout = 5000) public void returnsImmediatelyWhenAlreadyShutDown() { TronLogShutdownHook.shutDown = true; - - TronLogShutdownHook hook = new TronLogShutdownHook(); - long startNs = System.nanoTime(); - hook.run(); - long elapsedMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); - assertTrue("hook should exit fast when shutDown==true, elapsed=" + elapsedMs + "ms", - elapsedMs < 2_000); + new TronLogShutdownHook().run(); } @Test(timeout = 10_000) public void wakesUpWhenShutDownFlagFlips() throws InterruptedException { TronLogShutdownHook.shutDown = false; - - TronLogShutdownHook hook = new TronLogShutdownHook(); - Thread runner = new Thread(hook, "shutdown-hook-test-runner"); - runner.setDaemon(true); - runner.start(); - - Thread.sleep(300); - long flipNs = System.nanoTime(); + CountDownLatch waiting = new CountDownLatch(1); + TronLogShutdownHook hook = observedHook(waiting); + startWorker(hook); + assertTrue("Hook did not enter its wait loop", waiting.await(5, TimeUnit.SECONDS)); TronLogShutdownHook.shutDown = true; - - runner.join(5_000); - long elapsedAfterFlipMs = - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - flipNs); - - assertFalse("runner should have exited after flag flipped, still alive", - runner.isAlive()); - // The loop sleeps in 100 ms slices, so it should wake up well inside one - // slice's worth of jitter. 1 s is comfortable even on slow CI. - assertTrue("hook should return shortly after flag flip, elapsed=" - + elapsedAfterFlipMs + "ms", elapsedAfterFlipMs < 1_000); + runner.join(5000); + assertFalse("Hook did not exit after flag flipped", runner.isAlive()); } @Test(timeout = 10_000) public void preservesInterruptStatusWhenInterrupted() throws InterruptedException { TronLogShutdownHook.shutDown = false; - - TronLogShutdownHook hook = new TronLogShutdownHook(); - AtomicBoolean interruptedAfterRun = new AtomicBoolean(false); - Thread runner = new Thread(() -> { + CountDownLatch waiting = new CountDownLatch(1); + TronLogShutdownHook hook = observedHook(waiting); + AtomicBoolean interruptedAfterRun = new AtomicBoolean(); + startWorker(() -> { hook.run(); interruptedAfterRun.set(Thread.currentThread().isInterrupted()); - }, "shutdown-hook-test-interrupt"); - runner.setDaemon(true); - runner.start(); - - Thread.sleep(200); + }); + assertTrue("Hook did not enter its wait loop", waiting.await(5, TimeUnit.SECONDS)); runner.interrupt(); + runner.join(5000); + assertFalse("Hook did not exit after interrupt", runner.isAlive()); + assertTrue("Hook did not preserve interrupt status", interruptedAfterRun.get()); + } - runner.join(5_000); - assertFalse("runner should have exited after interrupt", runner.isAlive()); - assertTrue("run() must re-assert interrupt status after catching " - + "InterruptedException", interruptedAfterRun.get()); + private TronLogShutdownHook observedHook(CountDownLatch waiting) { + return new TronLogShutdownHook() { + @Override + public void addInfo(String message) { + waiting.countDown(); + } + }; + } + + private void startWorker(Runnable task) { + runner = new Thread(task, "shutdown-hook-test"); + runner.setDaemon(true); + runner.setUncaughtExceptionHandler((thread, error) -> failure.set(error)); + runner.start(); } } diff --git a/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java b/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java index 35cbbd1096f..590fb7dbcad 100755 --- a/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java +++ b/framework/src/test/java/org/tron/core/db/MarketPairPriceToOrderStoreTest.java @@ -99,7 +99,7 @@ public void testOrderWithSamePair() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } byte[] nextKey = marketPairPriceToOrderStore.getNextKey(pairPriceKey2); @@ -157,7 +157,7 @@ public void testOrderWithSamePairOrdinal() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } byte[] nextKey = marketPairPriceToOrderStore.getNextKey(pairPriceKey2); @@ -227,7 +227,7 @@ public void testAddPrice() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } byte[] nextKey = marketPairPriceToOrderStore.getNextKey(pairPriceKey2); @@ -284,7 +284,7 @@ public void testAddPriceWithoutHeadKey() { .assertArrayEquals(capsule2.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertTrue(marketPairPriceToOrderStore.has(pairPriceKey2)); @@ -297,7 +297,7 @@ public void testAddPriceWithoutHeadKey() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertFalse(marketPairPriceToOrderStore.has(pairPriceKey1)); @@ -312,7 +312,7 @@ public void testAddPriceWithoutHeadKey() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } byte[] nextKey = marketPairPriceToOrderStore.getNextKey(pairPriceKey2); @@ -366,7 +366,7 @@ public void testAddPriceAndHeadKey() { .assertArrayEquals(capsule1.getData(), marketPairPriceToOrderStore.get(pairPriceKey1).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } marketPairPriceToOrderStore.put(pairPriceKey2, capsule2); @@ -375,7 +375,7 @@ public void testAddPriceAndHeadKey() { .assertArrayEquals(capsule2.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } marketPairPriceToOrderStore.put(pairPriceKey3, capsule3); @@ -394,7 +394,7 @@ public void testAddPriceAndHeadKey() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } byte[] nextKey = marketPairPriceToOrderStore.getNextKey(pairPriceKey2); @@ -531,7 +531,7 @@ public void testPriceSeqWithSamePair() { .assertArrayEquals(capsule2.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // pairPriceKey1 and pairPriceKey2 has the same value, @@ -549,7 +549,7 @@ public void testPriceSeqWithSamePair() { .assertArrayEquals(capsule1.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertFalse(marketPairPriceToOrderStore.has(pairPriceKey0)); @@ -581,7 +581,7 @@ public void testPriceSeqWithSamePair() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // We will not have pairPriceKey2 in DB @@ -656,7 +656,7 @@ public void testPriceSeqWithSamePairNoGCD() { .assertArrayEquals(capsule2.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // pairPriceKey1 and pairPriceKey2 has the same value, @@ -674,7 +674,7 @@ public void testPriceSeqWithSamePairNoGCD() { .assertArrayEquals(capsule1.getData(), marketPairPriceToOrderStore.get(pairPriceKey2).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertFalse(marketPairPriceToOrderStore.has(pairPriceKey0)); @@ -706,7 +706,7 @@ public void testPriceSeqWithSamePairNoGCD() { .assertArrayEquals(capsule3.getData(), marketPairPriceToOrderStore.get(pairPriceKey3).getData()); } catch (ItemNotFoundException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // We will not have pairPriceKey2 in DB diff --git a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java index e2815e46063..8280113977d 100644 --- a/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java +++ b/framework/src/test/java/org/tron/core/event/BlockEventGetTest.java @@ -4,6 +4,7 @@ import com.google.protobuf.ByteString; import java.io.IOException; +import java.lang.reflect.Field; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -16,11 +17,13 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; import org.tron.api.GrpcAPI; import org.tron.common.TestConstants; +import org.tron.common.VMConfigRule; import org.tron.common.application.TronApplicationContext; import org.tron.common.logsfilter.EventPluginConfig; import org.tron.common.logsfilter.EventPluginLoader; @@ -53,6 +56,12 @@ public class BlockEventGetTest extends BlockGenerate { @ClassRule public static final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + + private static Field pluginInstanceField; + private static EventPluginLoader originalPluginLoader; + static ChainBaseManager chainManager; private final String key = PublicMethod.getRandomPrivateKey(); @@ -76,13 +85,17 @@ public static String dbPath() { try { return temporaryFolder.newFolder().toString(); } catch (IOException e) { - Assert.fail("create temp folder failed"); + throw new AssertionError("create temp folder failed", e); } - return null; } @BeforeClass - public static void init() { + public static void init() throws ReflectiveOperationException { + pluginInstanceField = EventPluginLoader.class.getDeclaredField("instance"); + pluginInstanceField.setAccessible(true); + originalPluginLoader = (EventPluginLoader) pluginInstanceField.get(null); + // Install before creating the context so all its services see the test-owned loader. + pluginInstanceField.set(null, new EventPluginLoader()); Args.setParam(new String[] {"--output-directory", dbPath()}, TestConstants.TEST_CONF); context = new TronApplicationContext(DefaultConfig.class); } @@ -113,10 +126,6 @@ public void before() throws IOException { // Reset global static flag that other tests may leave as true, which would prevent // ConfigLoader.load() from updating VMConfig during VMActuator.execute(). ConfigLoader.disable = false; - // Reset filterQuery so FilterQueryTest's leftover state does not suppress processTrigger - // coverage when tests share the same Gradle forkEvery JVM batch. - EventPluginLoader.getInstance().setFilterQuery(null); - DynamicPropertiesStore dps = dbManager.getDynamicPropertiesStore(); dps.saveAllowTvmTransferTrc10(1); dps.saveAllowTvmConstantinople(1); @@ -124,9 +133,21 @@ public void before() throws IOException { } @AfterClass - public static void after() throws IOException { - context.destroy(); - Args.clearParam(); + public static void after() throws IllegalAccessException { + try { + if (context != null) { + context.close(); + } + } finally { + context = null; + try { + Args.clearParam(); + } finally { + if (pluginInstanceField != null) { + pluginInstanceField.set(null, originalPluginLoader); + } + } + } } @Test @@ -174,7 +195,7 @@ public void test() throws Exception { EventPluginConfig config = new EventPluginConfig(); config.setSendQueueLength(1000); - config.setBindPort(5555); + config.setBindPort(PublicMethod.chooseRandomPort()); config.setUseNativeQueue(true); config.setTriggerConfigList(new ArrayList<>()); @@ -205,8 +226,8 @@ public void test() throws Exception { contractlogTriggerConfig.setRedundancy(true); config.getTriggerConfigList().add(contractlogTriggerConfig); - EventPluginLoader.getInstance().start(config); try { + Assert.assertTrue(EventPluginLoader.getInstance().start(config)); BlockEvent blockEvent = blockEventGet.getBlockEvent(1); Assert.assertNotNull(blockEvent); Assert.assertEquals(1, blockEvent.getTransactionLogTriggerCapsules().size()); @@ -216,8 +237,8 @@ public void test() throws Exception { Assert.assertEquals(100, blockEvent.getTransactionLogTriggerCapsules().get(0).getTransactionLogTrigger() .getEnergyUnitPrice()); - } catch (Exception e) { - Assert.fail(); + } finally { + EventPluginLoader.getInstance().stopPlugin(); } } @@ -295,4 +316,4 @@ public void getTransactionTriggers() throws Exception { Assert.assertEquals(0, list.get(0).getTransactionLogTrigger().getEnergyUsageTotal()); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java index 1485d726235..af3a0353c94 100644 --- a/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java +++ b/framework/src/test/java/org/tron/core/event/HistoryEventServiceTest.java @@ -1,16 +1,27 @@ package org.tron.core.event; import static org.mockito.Mockito.mock; - -import java.lang.reflect.Method; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.common.util.concurrent.Uninterruptibles; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; +import org.springframework.test.util.ReflectionTestUtils; import org.tron.common.logsfilter.EventPluginLoader; -import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.core.ChainBaseManager; -import org.tron.core.capsule.BlockCapsule; +import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.db.Manager; +import org.tron.core.services.event.BlockEventCache; import org.tron.core.services.event.BlockEventGet; import org.tron.core.services.event.BlockEventLoad; import org.tron.core.services.event.HistoryEventService; @@ -21,77 +32,99 @@ public class HistoryEventServiceTest { - HistoryEventService historyEventService = new HistoryEventService(); - - @Test(timeout = 60_000) - public void test() throws Exception { - EventPluginLoader instance = mock(EventPluginLoader.class); - Mockito.when(instance.isUseNativeQueue()).thenReturn(true); - Mockito.when(instance.isUseNativeQueue()).thenReturn(false); - - ReflectUtils.setFieldValue(historyEventService, "instance", instance); - - DynamicPropertiesStore dynamicPropertiesStore = mock(DynamicPropertiesStore.class); - ChainBaseManager chainBaseManager = mock(ChainBaseManager.class); + private final HistoryEventService service = new HistoryEventService(); + private final EventPluginLoader plugin = mock(EventPluginLoader.class); + private final SolidEventService solid = mock(SolidEventService.class); + private final RealtimeEventService realtime = mock(RealtimeEventService.class); + private final BlockEventLoad load = mock(BlockEventLoad.class); + private final BlockEventGet get = mock(BlockEventGet.class); + private final ChainBaseManager chain = mock(ChainBaseManager.class); + private final DynamicPropertiesStore properties = mock(DynamicPropertiesStore.class); + private final Map savedCacheState = new LinkedHashMap<>(); + + @Before + public void setUp() { + for (String field : new String[]{"solidNum", "head", "solidId", "blockEventMap", "numMap"}) { + savedCacheState.put(field, ReflectionTestUtils.getField(BlockEventCache.class, field)); + } + // init() clears both maps, so use test-owned maps to preserve the original contents. + ReflectionTestUtils.setField(BlockEventCache.class, "blockEventMap", new ConcurrentHashMap<>()); + ReflectionTestUtils.setField(BlockEventCache.class, "numMap", new ConcurrentHashMap<>()); Manager manager = mock(Manager.class); - ReflectUtils.setFieldValue(historyEventService, "manager", manager); - Mockito.when(manager.getChainBaseManager()).thenReturn(chainBaseManager); - Mockito.when(manager.getDynamicPropertiesStore()).thenReturn(dynamicPropertiesStore); - Mockito.when(chainBaseManager.getHeadBlockId()).thenReturn(new BlockCapsule.BlockId()); - - SolidEventService solidEventService = new SolidEventService(); - RealtimeEventService realtimeEventService = new RealtimeEventService(); - BlockEventLoad blockEventLoad = new BlockEventLoad(); - ReflectUtils.setFieldValue(blockEventLoad, "instance", instance); - ReflectUtils.setFieldValue(blockEventLoad, "manager", manager); - - ReflectUtils.setFieldValue(historyEventService, "solidEventService", solidEventService); - ReflectUtils.setFieldValue(historyEventService, "realtimeEventService", realtimeEventService); - ReflectUtils.setFieldValue(historyEventService, "blockEventLoad", blockEventLoad); - historyEventService.init(); - historyEventService.close(); - solidEventService.close(); - realtimeEventService.close(); - blockEventLoad.close(); - - solidEventService = mock(SolidEventService.class); - ReflectUtils.setFieldValue(historyEventService, "solidEventService", solidEventService); - realtimeEventService = mock(RealtimeEventService.class); - ReflectUtils.setFieldValue(historyEventService, "realtimeEventService", realtimeEventService); - blockEventLoad = mock(BlockEventLoad.class); - ReflectUtils.setFieldValue(historyEventService, "blockEventLoad", blockEventLoad); - - Mockito.when(instance.getStartSyncBlockNum()).thenReturn(0L); - - Mockito.when(dynamicPropertiesStore.getLatestSolidifiedBlockNum()).thenReturn(0L); - Mockito.when(chainBaseManager.getBlockIdByNum(0L)) - .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 0)); - historyEventService.init(); - - BlockEvent be2 = new BlockEvent(); - BlockCapsule.BlockId b2 = new BlockCapsule.BlockId(BlockEventCacheTest.getBlockId(), 2); - be2.setBlockId(b2); - - BlockEventGet blockEventGet = mock(BlockEventGet.class); - ReflectUtils.setFieldValue(historyEventService, "blockEventGet", blockEventGet); - Mockito.when(blockEventGet.getBlockEvent(1)).thenReturn(be2); - - Mockito.when(instance.getStartSyncBlockNum()).thenReturn(1L); - Mockito.when(dynamicPropertiesStore.getLatestSolidifiedBlockNum()).thenReturn(1L); + when(manager.getChainBaseManager()).thenReturn(chain); + when(manager.getDynamicPropertiesStore()).thenReturn(properties); + when(chain.getHeadBlockId()).thenReturn(new BlockId()); + ReflectionTestUtils.setField(service, "manager", manager); + ReflectionTestUtils.setField(service, "instance", plugin); + ReflectionTestUtils.setField(service, "solidEventService", solid); + ReflectionTestUtils.setField(service, "realtimeEventService", realtime); + ReflectionTestUtils.setField(service, "blockEventLoad", load); + ReflectionTestUtils.setField(service, "blockEventGet", get); + } - Mockito.when(chainBaseManager.getBlockIdByNum(1L)) - .thenReturn(new BlockCapsule.BlockId(Sha256Hash.ZERO_HASH, 1)); + @After + public void tearDown() { + try { + service.close(); + } finally { + Thread worker = (Thread) ReflectionTestUtils.getField(service, "thread"); + if (worker != null) { + worker.interrupt(); + Uninterruptibles.joinUninterruptibly(worker, 5, TimeUnit.SECONDS); + Assert.assertFalse("History worker did not terminate", worker.isAlive()); + } + // Restore only after the worker can no longer change the shared cache. + savedCacheState.forEach((field, value) -> + ReflectionTestUtils.setField(BlockEventCache.class, field, value)); + savedCacheState.clear(); + } + } - Mockito.when(instance.isUseNativeQueue()).thenReturn(true); + @Test + public void testInitFromHead() { + service.init(); + verify(realtime).init(); + verify(solid).init(); + verify(load).init(); + } - Method method1 = historyEventService.getClass().getDeclaredMethod("syncEvent"); - method1.setAccessible(true); - method1.invoke(historyEventService); + @Test(timeout = 10_000) + public void testSyncHistory() throws Exception { + when(plugin.getStartSyncBlockNum()).thenReturn(1L); + when(plugin.isUseNativeQueue()).thenReturn(true); + when(properties.getLatestSolidifiedBlockNum()).thenReturn(2L); + BlockId blockId = new BlockId(Sha256Hash.ZERO_HASH, 1); + BlockEvent block = new BlockEvent(blockId); + when(get.getBlockEvent(1L)).thenReturn(block); + when(chain.getBlockIdByNum(1L)).thenReturn(blockId); + + service.init(); + Thread worker = (Thread) ReflectionTestUtils.getField(service, "thread"); + worker.join(5000); + Assert.assertFalse("History sync did not complete", worker.isAlive()); + verify(realtime).flush(block, false); + verify(solid).flush(block); + verify(realtime).init(); + verify(solid).init(); + verify(load).init(); + } - Mockito.when(instance.isUseNativeQueue()).thenReturn(false); - Mockito.when(instance.isBusy()).thenReturn(true); - historyEventService.init(); - Thread.sleep(1000); - historyEventService.close(); + @Test(timeout = 10_000) + public void testCloseWhilePluginBusy() throws Exception { + when(plugin.getStartSyncBlockNum()).thenReturn(1L); + when(properties.getLatestSolidifiedBlockNum()).thenReturn(2L); + CountDownLatch busy = new CountDownLatch(1); + when(plugin.isBusy()).thenAnswer(invocation -> { + busy.countDown(); + return true; + }); + + service.init(); + Assert.assertTrue("Worker did not reach busy plugin", busy.await(5, TimeUnit.SECONDS)); + service.close(); + Thread worker = (Thread) ReflectionTestUtils.getField(service, "thread"); + Assert.assertFalse("History worker ignored close", worker.isAlive()); + verify(get, never()).getBlockEvent(1L); + verify(load, never()).init(); } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/BuildTransactionTest.java b/framework/src/test/java/org/tron/core/jsonrpc/BuildTransactionTest.java index 56cfd25ae5d..386aaebe51d 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/BuildTransactionTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/BuildTransactionTest.java @@ -77,7 +77,7 @@ public void testTransferContract() { ContractType contractType = buildArguments.getContractType(wallet); Assert.assertEquals(ContractType.TransferContract, contractType); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -93,7 +93,7 @@ public void testTransferAssertContract() { ContractType contractType = buildArguments.getContractType(wallet); Assert.assertEquals(ContractType.TransferAssetContract, contractType); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -138,7 +138,7 @@ public void testCreateSmartContract() { ContractType contractType = buildArguments.getContractType(wallet); Assert.assertEquals(ContractType.CreateSmartContract, contractType); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -160,7 +160,7 @@ public void testTriggerSmartContract() { ContractType contractType = buildArguments.getContractType(wallet); Assert.assertEquals(ContractType.TriggerSmartContract, contractType); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } 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 2fcb624002e..6422a231e71 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/ConcurrentHashMapTest.java @@ -1,16 +1,14 @@ package org.tron.core.jsonrpc; -import static org.tron.common.math.Maths.random; -import static org.tron.common.math.Maths.round; - import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; +import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; import org.tron.common.es.ExecutorServiceManager; @@ -20,192 +18,90 @@ import org.tron.core.services.jsonrpc.TronJsonRpcImpl; import org.tron.core.services.jsonrpc.filters.BlockFilterAndResult; -@Slf4j public class ConcurrentHashMapTest { private static final String EXECUTOR_NAME = "jsonrpc-concurrent-map-test"; - private final TronJsonRpcImpl jsonRpc = new TronJsonRpcImpl(null, null); - - private static int randomInt(int minInt, int maxInt) { - return (int) round(random(true) * (maxInt - minInt) + minInt, true); - } - /** - * test producer and consumer model in getFilterChanges after newBlockFilter. - * Firstly, sum of all consumers' number of messages is same as producer generates. - * Secondly, message of every consumer is continuous, not interject with another - * when consumes parallel. - */ @Test - public void testHandleBlockHash() { - int times = 100; - int eachCount = 200; - - Map conMap = jsonRpc.getBlockFilter2ResultFull(); - Map> resultMap1 = new ConcurrentHashMap<>(); // used to check result - Map> resultMap2 = new ConcurrentHashMap<>(); // used to check result - Map> resultMap3 = new ConcurrentHashMap<>(); // used to check result - - for (int i = 0; i < 5; i++) { - BlockFilterAndResult filterAndResult = new BlockFilterAndResult(); - String filterID = String.valueOf(i); - - conMap.put(filterID, filterAndResult); - resultMap1.put(filterID, new ArrayList<>()); - resultMap2.put(filterID, new ArrayList<>()); - resultMap3.put(filterID, new ArrayList<>()); - } - - try { - Thread.sleep(200); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Assert.fail("Interrupted during test setup: " + e.getMessage()); - } - - ExecutorService executor = ExecutorServiceManager.newFixedThreadPool(EXECUTOR_NAME, 4, true); - - 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); - - for (int j = 1 + (i - 1) * eachCount; j <= i * eachCount; j++) { - BlockFilterCapsule blockFilterCapsule = - new BlockFilterCapsule(String.valueOf(j), false); - jsonRpc.handleBLockFilter(blockFilterCapsule); - } - try { - Thread.sleep(randomInt(50, 100)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError("putThread interrupted", e); - } + public void testHandleBlockHash() throws Exception { + int count = 20_000; + int filterCount = 5; + CountDownLatch ready = new CountDownLatch(4); + CountDownLatch producerDone = new CountDownLatch(1); + ExecutorService executor = ExecutorServiceManager.newFixedThreadPool(EXECUTOR_NAME, 4); + try (TronJsonRpcImpl jsonRpc = new TronJsonRpcImpl(null, null)) { + try { + Map filters = jsonRpc.getBlockFilter2ResultFull(); + for (int i = 0; i < filterCount; i++) { + filters.put(String.valueOf(i), new BlockFilterAndResult()); } - }); - - Future getTask1 = executor.submit(() -> { - for (int t = 1; t <= times * 2; t++) { - - try { - Thread.sleep(randomInt(50, 100)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError("getThread1 interrupted", e); - } - - logger.info("Thread1 get time {}", t); - - for (int k = 0; k < 5; k++) { - try { - Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, - jsonRpc.getEventFilter2ResultFull()); - - for (Object str : blockHashList) { - resultMap1.get(String.valueOf(k)).add(str.toString()); - } - - } catch (ItemNotFoundException e) { - Assert.fail("Filter ID should always exist: " + e.getMessage()); + List>>> consumers = new ArrayList<>(); + for (int consumer = 0; consumer < 3; consumer++) { + consumers.add(executor.submit(() -> { + List> results = new ArrayList<>(); + for (int i = 0; i < filterCount; i++) { + results.add(new ArrayList<>()); } - } - } - }); - - Future getTask2 = executor.submit(() -> { - for (int t = 1; t <= times * 2; t++) { - - try { - Thread.sleep(randomInt(50, 100)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError("getThread2 interrupted", e); - } - - logger.info("Thread2 get time {}", t); - - for (int k = 0; k < 5; k++) { - try { - Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, - jsonRpc.getEventFilter2ResultFull()); - - // if (blockHashList.length == 0) { - // continue; - // } - - for (Object str : blockHashList) { - resultMap2.get(String.valueOf(k)).add(str.toString()); - } - - } catch (ItemNotFoundException e) { - Assert.fail("Filter ID should always exist: " + e.getMessage()); + ready.countDown(); + Assert.assertTrue("Workers did not start", ready.await(10, TimeUnit.SECONDS)); + // Completion, rather than a fixed number of polls, determines when to stop. + while (!producerDone.await(1, TimeUnit.MILLISECONDS)) { + drainFilters(jsonRpc, filters, results); } - } + drainFilters(jsonRpc, filters, results); + return results; + })); } - }); - - Future getTask3 = executor.submit(() -> { - for (int t = 1; t <= times * 2; t++) { - + Future producer = executor.submit(() -> { try { - Thread.sleep(randomInt(50, 100)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError("getThread3 interrupted", e); - } - - logger.info("Thread3 get time {}", t); - - for (int k = 0; k < 5; k++) { - try { - Object[] blockHashList = jsonRpc.getFilterResult(String.valueOf(k), conMap, - jsonRpc.getEventFilter2ResultFull()); - - for (Object str : blockHashList) { - try { - resultMap3.get(String.valueOf(k)).add(str.toString()); - } catch (Exception e) { - throw new AssertionError("resultMap3 get " + k + " exception", e); - } + ready.countDown(); + Assert.assertTrue("Workers did not start", ready.await(10, TimeUnit.SECONDS)); + for (int i = 1; i <= count; i++) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException("Producer cancelled"); } - - } catch (ItemNotFoundException e) { - Assert.fail("Filter ID should always exist: " + e.getMessage()); + jsonRpc.handleBLockFilter(new BlockFilterCapsule(String.valueOf(i), false)); } + return null; + } finally { + producerDone.countDown(); } + }); + producer.get(30, TimeUnit.SECONDS); + List>> results = new ArrayList<>(); + for (Future>> consumer : consumers) { + results.add(consumer.get(30, TimeUnit.SECONDS)); } - }); - - 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()); + Set expected = new HashSet<>(); + for (int i = 1; i <= count; i++) { + expected.add(ByteArray.toJsonHex(String.valueOf(i))); } + for (int filter = 0; filter < filterCount; filter++) { + List received = new ArrayList<>(); + for (List> result : results) { + received.addAll(result.get(filter)); + } + Assert.assertEquals("Unexpected event count for filter " + filter, + count, received.size()); + Assert.assertEquals("Missing or duplicate events for filter " + filter, + expected, new HashSet<>(received)); + Assert.assertTrue(filters.get(String.valueOf(filter)).getResult().isEmpty()); + } + } finally { + executor.shutdownNow(); + Assert.assertTrue("Filter workers did not stop", + executor.awaitTermination(5, TimeUnit.SECONDS)); } - } finally { - ExecutorServiceManager.shutdownAndAwaitTermination(executor, EXECUTOR_NAME); } + } - logger.info("-----------------------------------------------------------------------"); - - for (int i = 0; i < 5; i++) { - List pResult = resultMap1.get(String.valueOf(i)); - pResult.addAll(resultMap2.get(String.valueOf(i))); - pResult.addAll(resultMap3.get(String.valueOf(i))); - - for (int j = 1; j <= times * eachCount; j++) { - // if (!pResult.contains(ByteArray.toJsonHex(String.valueOf(j)))) { - // logger.info("key {} not contains {}", i, j); - // } - Assert.assertTrue(pResult.contains(ByteArray.toJsonHex(String.valueOf(j)))); + private void drainFilters(TronJsonRpcImpl jsonRpc, Map filters, + List> results) throws ItemNotFoundException { + for (int filter = 0; filter < results.size(); filter++) { + Object[] batch = jsonRpc.getFilterResult(String.valueOf(filter), filters, + jsonRpc.getEventFilter2ResultFull()); + for (Object hash : batch) { + results.get(filter).add((String) hash); } - - Assert.assertEquals(times * eachCount, pResult.size()); } } - } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java index 49f875f3823..3c862c28820 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonRpcTest.java @@ -137,7 +137,7 @@ public void testAddressCompatibleToByteArray() { Assert.assertArrayEquals(expectedBytes, addressCompatibleToByteArray(addressNoPre)); Assert.assertArrayEquals(expectedBytes, addressCompatibleToByteArray(addressWithPre)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -163,7 +163,7 @@ public void testAddressToByteArray() { Assert.assertArrayEquals(expectedBytes, addressToByteArray(rawAddress)); Assert.assertArrayEquals(expectedBytes, addressToByteArray(addressNoPre)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //test padding 0 ahead if length(address) = 39 @@ -175,7 +175,7 @@ public void testAddressToByteArray() { Assert.assertArrayEquals(addressToByteArray(address1), expectedBytes2); Assert.assertArrayEquals(addressToByteArray(address1), addressToByteArray(address2)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // oversized input rejected before fromHexString @@ -207,7 +207,7 @@ public void testLogFilter() { new String[] {"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"}, null)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //63-char form: leading zero stripped by some clients, padded back to the same topic @@ -220,7 +220,7 @@ public void testLogFilter() { new String[] {null, "0x" + paddedAddressTopic.substring(1)}, null)); Assert.assertArrayEquals(full.getTopics().get(1)[0], stripped.getTopics().get(1)[0]); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -274,7 +274,7 @@ public void testLogFilter() { new LogFilter( new FilterRequest(null, null, "0xaa6612f03443517ced2bdcf27958c22353ceeab9", null, null)); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //address length of 42 hex string with 41 ahead will be invalid @@ -409,7 +409,7 @@ public void testGetConditions() { getBloomIndex("0x00000000000000000000000056178a0d5f301baf6cf3e1cd53d9863437345bf9")); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -449,7 +449,7 @@ public void testGetConditionWithHashCollision() { getBloomIndex("0x3038114c1a1e72c5bfa8b003bc3650ad2ba254a0")); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java index e8d14ace060..95020f9ef43 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/JsonrpcServiceTest.java @@ -226,7 +226,7 @@ public void testWeb3Sha3() { try { result = tronJsonRpc.web3Sha3("0x1122334455667788"); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals("0x1360118a9c9fd897720cf4e26de80683f402dd7c28e000aa98ea51b85c60161c", @@ -252,7 +252,7 @@ public void testGetBlockTransactionCountByHash() { result = tronJsonRpc.ethGetBlockTransactionCountByHash( "0x1111111111111111111111111111111111111111111111111111111111111111"); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertNull(result); @@ -260,7 +260,7 @@ public void testGetBlockTransactionCountByHash() { result = tronJsonRpc.ethGetBlockTransactionCountByHash( Hex.toHexString((blockCapsule1.getBlockId().getBytes()))); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getTransactions().size()), result); @@ -286,7 +286,7 @@ public void testGetBlockTransactionCountByNumber() { try { result = tronJsonRpc.ethGetBlockTransactionCountByNumber("latest"); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getTransactions().size()), result); @@ -294,7 +294,7 @@ public void testGetBlockTransactionCountByNumber() { result = tronJsonRpc.ethGetBlockTransactionCountByNumber( ByteArray.toJsonHex(blockCapsule1.getNum())); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getTransactions().size()), result); @@ -317,7 +317,7 @@ public void testGetBlockByHash() { tronJsonRpc.ethGetBlockByHash(Hex.toHexString((blockCapsule1.getBlockId().getBytes())), false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getNum()), blockResult.getNumber()); Assert.assertEquals(blockCapsule1.getTransactions().size(), @@ -333,7 +333,7 @@ public void testGetBlockByNumber() { blockResult = tronJsonRpc.ethGetBlockByNumber(ByteArray.toJsonHex(blockCapsule1.getNum()), false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getNum()), blockResult.getNumber()); Assert.assertEquals(blockCapsule1.getTransactions().size(), @@ -344,7 +344,7 @@ public void testGetBlockByNumber() { try { blockResult = tronJsonRpc.ethGetBlockByNumber("earliest", false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(0L), blockResult.getNumber()); Assert.assertEquals(ByteArray.toJsonHex(blockCapsule0.getNum()), blockResult.getNumber()); @@ -355,7 +355,7 @@ public void testGetBlockByNumber() { try { blockResult = tronJsonRpc.ethGetBlockByNumber("latest", false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(LATEST_BLOCK_NUM), blockResult.getNumber()); Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getNum()), blockResult.getNumber()); @@ -364,7 +364,7 @@ public void testGetBlockByNumber() { try { blockResult = tronJsonRpc.ethGetBlockByNumber("finalized", false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(LATEST_SOLIDIFIED_BLOCK_NUM), blockResult.getNumber()); Assert.assertEquals(ByteArray.toJsonHex(blockCapsule2.getNum()), blockResult.getNumber()); @@ -397,7 +397,7 @@ public void testGetTransactionByHash() { transactionResult = tronJsonRpc.getTransactionByHash( "0x1111111111111111111111111111111111111111111111111111111111111111"); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertNull(transactionResult); @@ -405,7 +405,7 @@ public void testGetTransactionByHash() { transactionResult = tronJsonRpc.getTransactionByHash( ByteArray.toJsonHex(transactionCapsule1.getTransactionId().getBytes())); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals(ByteArray.toJsonHex(transactionCapsule1.getBlockNum()), transactionResult.getBlockNumber()); @@ -484,7 +484,7 @@ public void testBlockTagParsing() { long blkNum = parseBlockTag("latest", wallet); Assert.assertEquals(LATEST_BLOCK_NUM, blkNum); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // parseBlockTag: earliest -> 0 @@ -492,7 +492,7 @@ public void testBlockTagParsing() { long blkNum = parseBlockTag("earliest", wallet); Assert.assertEquals(0L, blkNum); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // parseBlockTag: finalized -> solidBlockNum @@ -500,7 +500,7 @@ public void testBlockTagParsing() { long blkNum = parseBlockTag("finalized", wallet); Assert.assertEquals(LATEST_SOLIDIFIED_BLOCK_NUM, blkNum); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // parseBlockNumber: hex -> number @@ -508,7 +508,7 @@ public void testBlockTagParsing() { long blkNum = parseBlockNumber("0xa", wallet); Assert.assertEquals(10L, blkNum); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // parseBlockNumber: bad hex -> throws @@ -545,7 +545,7 @@ public void testGetTrxBalance() { balance = tronJsonRpc.getTrxBalance("0xabd4b9367799eaa3197fecb144eb71de1e049abc", "latest"); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Assert.assertEquals("0x2540be400", balance); } @@ -604,7 +604,7 @@ public void testGetStorageAt() { addr, "0x0", "latest"); Assert.assertEquals(ByteArray.toJsonHex(new byte[32]), value); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -643,7 +643,7 @@ public void testGetABIOfSmartContract() { "0xabd4b9367799eaa3197fecb144eb71de1e049abc", "latest"); Assert.assertEquals("0x", code); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -681,7 +681,7 @@ public void testGetTransactionByBlockNumberAndIndex() { Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getNum()), result.getBlockNumber()); Assert.assertEquals(ByteArray.toJsonHex(0L), result.getTransactionIndex()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // index out of range in an existing block returns null @@ -690,7 +690,7 @@ public void testGetTransactionByBlockNumberAndIndex() { ByteArray.toJsonHex(blockCapsule1.getNum()), "0x5"); Assert.assertNull(result); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // negative index is out of range too -> null (not an Internal error) @@ -699,7 +699,7 @@ public void testGetTransactionByBlockNumberAndIndex() { ByteArray.toJsonHex(blockCapsule1.getNum()), "0x-1"); Assert.assertNull(result); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // leading zeros are tolerated: "0x00" parses to index 0 @@ -709,7 +709,7 @@ public void testGetTransactionByBlockNumberAndIndex() { Assert.assertNotNull(result); Assert.assertEquals(ByteArray.toJsonHex(0L), result.getTransactionIndex()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // oversized index (> 8 hex digits) rejected before parsing @@ -724,7 +724,7 @@ public void testGetTransactionByBlockNumberAndIndex() { Assert.assertNotNull(result); Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getNum()), result.getBlockNumber()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // finalized -> blockCapsule2 (solid), has 1 tx @@ -733,7 +733,7 @@ public void testGetTransactionByBlockNumberAndIndex() { tronJsonRpc.getTransactionByBlockNumberAndIndex("finalized", "0x0"); Assert.assertNotNull(result); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // non-existent block number returns null (not an error) @@ -741,7 +741,7 @@ public void testGetTransactionByBlockNumberAndIndex() { TransactionResult result = tronJsonRpc.getTransactionByBlockNumberAndIndex("0x1", "0x0"); Assert.assertNull(result); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // pending tag rejected @@ -826,7 +826,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(100, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // fromBlock is not empty and smaller than currentMaxBlockNum, toBlock is empty @@ -836,7 +836,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(20, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // fromBlock is not empty and bigger than currentMaxBlockNum, toBlock is empty @@ -846,7 +846,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(120, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // fromBlock is empty, toBlock is not empty and smaller than currentMaxBlockNum @@ -856,7 +856,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(20, logFilterWrapper.getFromBlock()); Assert.assertEquals(20, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // fromBlock is empty, toBlock is not empty and bigger than currentMaxBlockNum @@ -866,7 +866,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(100, logFilterWrapper.getFromBlock()); Assert.assertEquals(120, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // fromBlock is not empty, toBlock is not empty @@ -876,7 +876,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(20, logFilterWrapper.getFromBlock()); Assert.assertEquals(120, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException fromToEx = Assert.assertThrows(JsonRpcInvalidParamsException.class, @@ -891,7 +891,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(0, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { LogFilterWrapper logFilterWrapper = new LogFilterWrapper(new FilterRequest("latest", null, @@ -899,7 +899,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(100, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException pendingFilterEx = Assert.assertThrows( JsonRpcInvalidParamsException.class, @@ -912,7 +912,7 @@ public void testLogFilterWrapper() { Assert.assertEquals(LATEST_SOLIDIFIED_BLOCK_NUM, logFilterWrapper.getFromBlock()); Assert.assertEquals(Long.MAX_VALUE, logFilterWrapper.getToBlock()); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException testSyntaxEx = Assert.assertThrows( JsonRpcInvalidParamsException.class, @@ -925,7 +925,7 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException rangeEx1 = Assert.assertThrows( @@ -940,7 +940,7 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x0", "latest", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException rangeEx2 = Assert.assertThrows( @@ -956,13 +956,13 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x64", "latest", null, null, null), 5_000, null, true); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("0x64", "latest", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // from = 100 @@ -977,7 +977,7 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x64", "latest", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // from = 9_000 @@ -985,26 +985,26 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x2328", "latest", null, null, null), LATEST_BLOCK_NUM, null, true); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("0x2328", "latest", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("latest", "latest", null, null, null), LATEST_BLOCK_NUM, null, true); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("latest", "latest", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } int oldMaxBlockRange = Args.getInstance().getJsonRpcMaxBlockRange(); @@ -1013,13 +1013,13 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, true); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().setJsonRpcMaxBlockRange(0); @@ -1027,13 +1027,13 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, true); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().setJsonRpcMaxBlockRange(-2); @@ -1041,13 +1041,13 @@ public void testLogFilterWrapper() { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, true); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { new LogFilterWrapper(new FilterRequest("0x0", "0x1f40", null, null, null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } JsonRpcInvalidParamsException shortHashEx = Assert.assertThrows( @@ -1106,7 +1106,7 @@ public void testMaxSubTopics() { "exceed max topics: " + Args.getInstance().getJsonRpcMaxSubTopics(), e.getMessage()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { @@ -1118,7 +1118,7 @@ public void testMaxSubTopics() { "exceed max topics: " + Args.getInstance().getJsonRpcMaxSubTopics(), e.getMessage()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } int oldMaxSubTopics = Args.getInstance().getJsonRpcMaxSubTopics(); @@ -1127,7 +1127,7 @@ public void testMaxSubTopics() { new LogFilterWrapper(new FilterRequest("0xbb8", "0x1f40", null, topics.toArray(), null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().setJsonRpcMaxSubTopics(0); @@ -1135,13 +1135,13 @@ public void testMaxSubTopics() { new LogFilterWrapper(new FilterRequest("0xbb8", "0x1f40", null, topics.toArray(), null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { tronJsonRpc.newFilter(new FilterRequest("0xbb8", "0x1f40", null, topics.toArray(), null)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().setJsonRpcMaxSubTopics(-2); @@ -1149,13 +1149,13 @@ public void testMaxSubTopics() { new LogFilterWrapper(new FilterRequest("0xbb8", "0x1f40", null, topics.toArray(), null), LATEST_BLOCK_NUM, null, false); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { tronJsonRpc.newFilter(new FilterRequest("0xbb8", "0x1f40", null, topics.toArray(), null)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().setJsonRpcMaxSubTopics(oldMaxSubTopics); @@ -1172,21 +1172,21 @@ public void testMethodBlockRange() { "exceed max block range: " + Args.getInstance().jsonRpcMaxBlockRange, e.getMessage()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { tronJsonRpc.newFilter(new FilterRequest("0x0", "0x1f40", null, null, null)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } try { tronJsonRpc.getLogs(new FilterRequest("0x0", "0x1", null, null, null)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -1210,7 +1210,7 @@ public void testGetLogs() { Assert.assertEquals(ByteArray.toJsonHex(blockCapsule1.getTimeStamp() / 1000), log.getBlockTimestamp()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -1220,7 +1220,7 @@ public void testNewFilterFinalizedBlock() { try { tronJsonRpc.newFilter(new FilterRequest(null, null, null, null, null)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Exception e1 = Assert.assertThrows(Exception.class, @@ -1565,7 +1565,7 @@ public void testWeb3ClientVersion() { String javaVersion = versions[versions.length - 1]; Assert.assertTrue("Java1.8".equals(javaVersion) || "Java17".equals(javaVersion)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java index 2151801fc59..fdca2dbcd79 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/LogMatchExactlyTest.java @@ -62,7 +62,7 @@ public void testMatchOneAddress1() { null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -75,7 +75,7 @@ public void testMatchOneAddress2() { null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -88,7 +88,7 @@ public void testMatchOneAddress3() { null)); Assert.assertFalse(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -104,7 +104,7 @@ public void testMatchMultiAddress() { null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -116,7 +116,7 @@ public void testMatchOneTopic1() { new String[] {topicTest1}, null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -128,7 +128,7 @@ public void testMatchOneTopic2() { new String[] {topicTest2}, null)); Assert.assertFalse(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -143,7 +143,7 @@ public void testMatchMultiTopic1() { new Object[] {topicList}, null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -160,7 +160,7 @@ public void testMatchMultiTopic2() { new Object[] {null, topicList}, null)); Assert.assertFalse(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -179,7 +179,7 @@ public void testMatchMultiTopic3() { new Object[] {topicList1, null, topicList2}, null)); Assert.assertTrue(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -206,7 +206,7 @@ public void testMatchAddressMultiTopic() { new Object[] {topicTest2, null, topicList2}, null)); Assert.assertFalse(logFilter.matchesExactly(transactionInfo.getLog(0))); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -237,7 +237,7 @@ public void testMatchBlock() { Assert.assertEquals(logFilterElement1, logFilterElement2); } catch (JsonRpcInvalidParamsException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } } 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 39bcc30e278..4c1c11d5307 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/SectionBloomStoreTest.java @@ -56,7 +56,7 @@ public void testPutAndGet() { sectionBloomStore.put(100, 101, bitSet); Assert.assertEquals(bitSet, sectionBloomStore.get(100, 101)); } catch (EventBloomException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -116,7 +116,7 @@ public void testWriteAndQuery() { try { sectionBloomStore.write(10000); } catch (EventBloomException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //add2 @@ -126,7 +126,7 @@ public void testWriteAndQuery() { try { sectionBloomStore.write(20000); } catch (EventBloomException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //add3 @@ -136,7 +136,7 @@ public void testWriteAndQuery() { try { sectionBloomStore.write(30000); } catch (EventBloomException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } long currentMaxBlockNum = 50000; @@ -152,7 +152,7 @@ public void testWriteAndQuery() { List possibleBlockList = logBlockQuery.getPossibleBlock(); Assert.assertTrue(possibleBlockList.contains(10000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //query multi address @@ -170,7 +170,7 @@ public void testWriteAndQuery() { Assert.assertTrue(possibleBlockList.contains(10000L)); Assert.assertTrue(possibleBlockList.contains(30000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //query one topic @@ -186,7 +186,7 @@ public void testWriteAndQuery() { Assert.assertTrue(possibleBlockList.contains(10000L)); Assert.assertTrue(possibleBlockList.contains(20000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //query another topic @@ -201,7 +201,7 @@ public void testWriteAndQuery() { List possibleBlockList = logBlockQuery.getPossibleBlock(); Assert.assertTrue(possibleBlockList.contains(30000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //query multi topic in "or" condition @@ -221,7 +221,7 @@ public void testWriteAndQuery() { Assert.assertTrue(possibleBlockList.contains(20000L)); Assert.assertTrue(possibleBlockList.contains(30000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //add4 @@ -231,7 +231,7 @@ public void testWriteAndQuery() { try { sectionBloomStore.write(10000); } catch (EventBloomException e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //query multi topic in "and" condition. Match Bloom only, but not match exactly. @@ -246,7 +246,7 @@ public void testWriteAndQuery() { List possibleBlockList = logBlockQuery.getPossibleBlock(); Assert.assertTrue(possibleBlockList.contains(10000L)); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java index 24ca71a74bc..dd4d7b03fea 100644 --- a/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java +++ b/framework/src/test/java/org/tron/core/jsonrpc/WalletCursorTest.java @@ -150,7 +150,7 @@ public void testEnableInFullNode() { tronJsonRpc.buildTransaction(buildArguments); tronJsonRpc.close(); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } diff --git a/framework/src/test/java/org/tron/core/metrics/MetricsApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/MetricsApiServiceTest.java index f96a03d92e3..93e2f8c199a 100644 --- a/framework/src/test/java/org/tron/core/metrics/MetricsApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/MetricsApiServiceTest.java @@ -5,8 +5,8 @@ import org.junit.Test; import org.tron.common.BaseMethodTest; import org.tron.common.parameter.CommonParameter; +import org.tron.common.utils.PublicMethod; import org.tron.core.config.args.Args; -import org.tron.core.services.RpcApiService; import org.tron.program.Version; import org.tron.protos.Protocol; @@ -14,9 +14,7 @@ public class MetricsApiServiceTest extends BaseMethodTest { private static String dbDirectory = "metrics-database"; - private static int port = 10001; private MetricsApiService metricsApiService; - private RpcApiService rpcApiService; @Override protected String[] extraArgs() { @@ -29,7 +27,7 @@ protected String[] extraArgs() { @Override protected void afterInit() { CommonParameter parameter = Args.getInstance(); - parameter.setNodeListenPort(port); + parameter.setNodeListenPort(PublicMethod.chooseRandomPort()); parameter.getSeedNode().getAddressList().clear(); parameter.setNodeExternalIp("127.0.0.1"); metricsApiService = context.getBean(MetricsApiService.class); diff --git a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java index dd260a1b869..022051acf1a 100644 --- a/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java +++ b/framework/src/test/java/org/tron/core/metrics/prometheus/PrometheusApiServiceTest.java @@ -58,6 +58,9 @@ public class PrometheusApiServiceTest extends BaseTest { @Resource private ChainBaseManager chainManager; + private double initialProcessedBlocks; + private double initialErrorLogs; + static { Args.setParam(new String[] {"-d", dbPath()}, TestConstants.TEST_CONF); Args.getInstance().setNodeListenPort(10000 + port.incrementAndGet()); @@ -67,6 +70,8 @@ public class PrometheusApiServiceTest extends BaseTest { protected static void initParameter(CommonParameter parameter) { parameter.setMetricsPrometheusEnable(true); + // Only the registry is asserted; let the OS allocate a port for each test JVM. + parameter.setMetricsPrometheusPort(0); } protected void check(byte[] address, Map witnessAndAccount) throws Exception { @@ -83,7 +88,7 @@ protected void check(byte[] address, Map witnessAndAccount) "tron:block_process_latency_seconds_count", new String[] {"sync"}, new String[] {"false"}); Assert.assertNotNull(pushBlock); - Assert.assertEquals(pushBlock.intValue(), blocks + 1); + Assert.assertEquals(blocks + 1, pushBlock - initialProcessedBlocks, 0.0); String minerBase58 = StringUtil.encode58Check(address); // Query histogram bucket le="0.0" for empty blocks @@ -112,7 +117,8 @@ protected void check(byte[] address, Map witnessAndAccount) Double errorLogs = CollectorRegistry.defaultRegistry.getSampleValue( "tron:error_info_total", new String[] {"net"}, new String[] {MetricLabels.UNDEFINED}); - Assert.assertNull(errorLogs); + Assert.assertEquals("Unexpected net error logs during block processing", initialErrorLogs, + errorLogs == null ? 0.0 : errorLogs, 0.0); } @Before @@ -146,6 +152,13 @@ private void generateBlock(Map witnessAndAccount) throws Exc @Test public void testMetric() throws Exception { + Double processedBlocks = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:block_process_latency_seconds_count", + new String[] {"sync"}, new String[] {"false"}); + initialProcessedBlocks = processedBlocks == null ? 0.0 : processedBlocks; + Double errorLogs = CollectorRegistry.defaultRegistry.getSampleValue( + "tron:error_info_total", new String[] {"net"}, new String[] {MetricLabels.UNDEFINED}); + initialErrorLogs = errorLogs == null ? 0.0 : errorLogs; final ECKey ecKey = ECKey.fromPrivate(privateKey); Assert.assertNotNull(ecKey); @@ -206,4 +219,4 @@ private BlockCapsule createTestBlockCapsule(long time, return blockCapsule; } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/BlockMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/BlockMsgHandlerTest.java index 82ea2b6cb57..7722fa51f53 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/BlockMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/BlockMsgHandlerTest.java @@ -1,171 +1,148 @@ package org.tron.core.net.messagehandler; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; - -import com.google.common.collect.ImmutableList; +import com.google.common.cache.CacheBuilder; import com.google.protobuf.ByteString; -import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.InetAddress; import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; -import javax.annotation.Resource; -import lombok.extern.slf4j.Slf4j; +import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; -import org.tron.common.BaseTest; -import org.tron.common.TestConstants; -import org.tron.common.utils.ByteArray; +import org.tron.common.utils.ReflectUtils; import org.tron.common.utils.Sha256Hash; import org.tron.core.Constant; import org.tron.core.capsule.BlockCapsule; import org.tron.core.capsule.BlockCapsule.BlockId; import org.tron.core.config.Parameter; -import org.tron.core.config.args.Args; import org.tron.core.exception.P2pException; import org.tron.core.net.TronNetDelegate; import org.tron.core.net.message.adv.BlockMessage; import org.tron.core.net.peer.Item; import org.tron.core.net.peer.PeerConnection; -import org.tron.p2p.connection.Channel; +import org.tron.core.net.service.adv.AdvService; +import org.tron.core.net.service.fetchblock.FetchBlockService; +import org.tron.core.net.service.sync.SyncService; +import org.tron.core.services.WitnessProductBlockService; import org.tron.protos.Protocol.Inventory.InventoryType; import org.tron.protos.Protocol.Transaction; -@Slf4j -public class BlockMsgHandlerTest extends BaseTest { - - @Resource +public class BlockMsgHandlerTest { private BlockMsgHandler handler; - @Resource private PeerConnection peer; - - /** - * init context. - */ - @BeforeClass - public static void init() { - Args.setParam(new String[] {"--output-directory", dbPath(), "--debug"}, - TestConstants.TEST_CONF); - } + private TronNetDelegate delegate; + private AdvService advService; + private SyncService syncService; + private FetchBlockService fetchService; + private WitnessProductBlockService witnessService; @Before - public void before() throws Exception { - Channel c1 = new Channel(); - InetSocketAddress a1 = new InetSocketAddress("100.1.1.1", 100); - Field field = c1.getClass().getDeclaredField("inetAddress"); - field.setAccessible(true); - field.set(c1, a1.getAddress()); - peer.setChannel(c1); + public void before() { + // Each test owns its handler and collaborators; no Spring singleton is modified. + handler = new BlockMsgHandler(); + peer = Mockito.mock(PeerConnection.class); + delegate = Mockito.mock(TronNetDelegate.class); + advService = Mockito.mock(AdvService.class); + syncService = Mockito.mock(SyncService.class); + fetchService = Mockito.mock(FetchBlockService.class); + witnessService = Mockito.mock(WitnessProductBlockService.class); + ReflectUtils.setFieldValue(handler, "tronNetDelegate", delegate); + ReflectUtils.setFieldValue(handler, "advService", advService); + ReflectUtils.setFieldValue(handler, "syncService", syncService); + ReflectUtils.setFieldValue(handler, "fetchBlockService", fetchService); + ReflectUtils.setFieldValue(handler, "witnessProductBlockService", witnessService); + ReflectUtils.setFieldValue(handler, "fastForward", false); + Mockito.when(peer.getAdvInvRequest()).thenReturn(new ConcurrentHashMap<>()); + Mockito.when(peer.getSyncBlockRequested()).thenReturn(new ConcurrentHashMap<>()); + Mockito.when(peer.getSyncBlockInProcess()).thenReturn(new HashSet<>()); + Mockito.when(peer.getAdvInvReceive()).thenReturn(CacheBuilder.newBuilder().build()); + Mockito.when(peer.getInetAddress()).thenReturn(InetAddress.getLoopbackAddress()); + Mockito.when(peer.getInetSocketAddress()).thenReturn( + new InetSocketAddress(InetAddress.getLoopbackAddress(), 100)); } @Test - public void testProcessMessage() { - BlockCapsule blockCapsule; - BlockMessage msg; - try { - blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, - System.currentTimeMillis(), Sha256Hash.ZERO_HASH.getByteString()); - msg = new BlockMessage(blockCapsule); - handler.processMessage(peer, msg); - } catch (P2pException e) { - assertEquals("no request", e.getMessage()); - } - - try { - List transactionList = ImmutableList.of( - Transaction.newBuilder() - .setRawData(Transaction.raw.newBuilder() - .setData( - ByteString.copyFrom( - new byte[Parameter.ChainConstant.BLOCK_SIZE + Constant.ONE_THOUSAND]))) - .build()); - blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH.getByteString(), - System.currentTimeMillis() + 10000, transactionList); - msg = new BlockMessage(blockCapsule); - System.out.println("len = " + blockCapsule.getInstance().getSerializedSize()); - peer.getAdvInvRequest() - .put(new Item(msg.getBlockId(), InventoryType.BLOCK), System.currentTimeMillis()); - handler.processMessage(peer, msg); - } catch (P2pException e) { - //System.out.println(e); - assertEquals("block size over limit", e.getMessage()); - } - - try { - blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, - System.currentTimeMillis() + 10000, Sha256Hash.ZERO_HASH.getByteString()); - msg = new BlockMessage(blockCapsule); - peer.getAdvInvRequest() - .put(new Item(msg.getBlockId(), InventoryType.BLOCK), System.currentTimeMillis()); - handler.processMessage(peer, msg); - } catch (P2pException e) { - //System.out.println(e); - assertEquals("block time error", e.getMessage()); - } + public void testUnrequestedBlock() { + BlockMessage msg = new BlockMessage(block(1, 1)); + assertRejected(msg, "no request"); + } - try { - blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, - System.currentTimeMillis() + 1000, Sha256Hash.ZERO_HASH.getByteString()); - msg = new BlockMessage(blockCapsule); - peer.getSyncBlockRequested() - .put(msg.getBlockId(), System.currentTimeMillis()); - handler.processMessage(peer, msg); - } catch (P2pException e) { - //System.out.println(e); - } + @Test + public void testOversizedBlock() { + Transaction trx = Transaction.newBuilder().setRawData(Transaction.raw.newBuilder() + .setData(ByteString.copyFrom(new byte[Parameter.ChainConstant.BLOCK_SIZE + + Constant.ONE_THOUSAND]))).build(); + BlockCapsule block = new BlockCapsule(1, Sha256Hash.ZERO_HASH.getByteString(), 1, + Collections.singletonList(trx)); + assertRejected(new BlockMessage(block), "block size over limit"); + } - try { - blockCapsule = new BlockCapsule(1, Sha256Hash.ZERO_HASH, - System.currentTimeMillis() + 1000, Sha256Hash.ZERO_HASH.getByteString()); - msg = new BlockMessage(blockCapsule); - peer.getAdvInvRequest() - .put(new Item(msg.getBlockId(), InventoryType.BLOCK), System.currentTimeMillis()); - handler.processMessage(peer, msg); - } catch (NullPointerException | P2pException e) { - logger.error("error", e); - } + @Test + public void testFutureBlock() { + assertRejected(new BlockMessage(block(1, Long.MAX_VALUE)), "block time error"); } @Test - public void testProcessBlock() { - TronNetDelegate tronNetDelegate = Mockito.mock(TronNetDelegate.class); + public void testSyncBlock() throws Exception { + BlockMessage msg = new BlockMessage(block(1, 1)); + peer.getSyncBlockRequested().put(msg.getBlockId(), 1L); + handler.processMessage(peer, msg); + Assert.assertTrue(peer.getSyncBlockRequested().isEmpty()); + Assert.assertTrue(peer.getSyncBlockInProcess().contains(msg.getBlockId())); + Mockito.verify(syncService).processBlock(peer, msg); + Mockito.verifyNoInteractions(delegate, advService, fetchService, witnessService); + } - try { - Field field = handler.getClass().getDeclaredField("tronNetDelegate"); - field.setAccessible(true); - field.set(handler, tronNetDelegate); + @Test + public void testAdvertisedBlock() throws Exception { + BlockCapsule block = block(1, 1); + BlockMessage msg = new BlockMessage(block); + peer.getAdvInvRequest().put(new Item(msg.getBlockId(), InventoryType.BLOCK), 1L); + stubValidBlock(block); + handler.processMessage(peer, msg); + Assert.assertTrue(peer.getAdvInvRequest().isEmpty()); + Mockito.verify(fetchService).blockFetchSuccess(msg.getBlockId()); + Mockito.verify(delegate).processBlock(block, false); + Mockito.verify(advService).broadcast(Mockito.any(BlockMessage.class)); + Mockito.verify(witnessService).validWitnessProductTwoBlock(block); + } - BlockCapsule blockCapsule0 = new BlockCapsule(1, - Sha256Hash.wrap(ByteString - .copyFrom(ByteArray - .fromHexString( - "9938a342238077182498b464ac0292229938a342238077182498b464ac029222"))), - 1234, - ByteString.copyFrom("1234567".getBytes())); + @Test + public void testProcessBlock() throws Exception { + BlockCapsule block = block(1, 1); + stubValidBlock(block); + peer.getAdvInvReceive().put(new Item(block.getBlockId(), InventoryType.BLOCK), 1L); + Method method = BlockMsgHandler.class.getDeclaredMethod("processBlock", + PeerConnection.class, BlockCapsule.class); + method.setAccessible(true); + method.invoke(handler, peer, block); + Mockito.verify(delegate).processBlock(block, false); + Mockito.verify(peer).setBlockBothHave(block.getBlockId()); + Mockito.verify(witnessService).validWitnessProductTwoBlock(block); + } - peer.getAdvInvReceive() - .put(new Item(blockCapsule0.getBlockId(), InventoryType.BLOCK), - System.currentTimeMillis()); + private void assertRejected(BlockMessage msg, String reason) { + P2pException failure = Assert.assertThrows(P2pException.class, + () -> handler.processMessage(peer, msg)); + Assert.assertEquals(P2pException.TypeEnum.BAD_MESSAGE, failure.getType()); + Assert.assertEquals(reason, failure.getMessage()); + Mockito.verifyNoInteractions(delegate, advService, syncService, fetchService, witnessService); + } - Mockito.doReturn(true).when(tronNetDelegate).validBlock(any(BlockCapsule.class)); - Mockito.doReturn(true).when(tronNetDelegate).containBlock(any(BlockId.class)); - Mockito.doReturn(blockCapsule0.getBlockId()).when(tronNetDelegate).getHeadBlockId(); - Mockito.doNothing().when(tronNetDelegate).processBlock(any(BlockCapsule.class), anyBoolean()); - List peers = new ArrayList<>(); - peers.add(peer); - Mockito.doReturn(peers).when(tronNetDelegate).getActivePeer(); + private void stubValidBlock(BlockCapsule block) throws Exception { + Mockito.when(delegate.validBlock(block)).thenReturn(true); + Mockito.when(delegate.containBlock(Mockito.any(BlockId.class))).thenReturn(true); + Mockito.when(delegate.getHeadBlockId()).thenReturn(block.getBlockId()); + Mockito.when(delegate.getActivePeer()).thenReturn(Collections.singletonList(peer)); + } - Method method = handler.getClass() - .getDeclaredMethod("processBlock", PeerConnection.class, BlockCapsule.class); - method.setAccessible(true); - method.invoke(handler, peer, blockCapsule0); - } catch (Exception e) { - Assert.fail(); - } + private BlockCapsule block(long number, long timestamp) { + BlockCapsule block = new BlockCapsule(number, Sha256Hash.ZERO_HASH, timestamp, + Sha256Hash.ZERO_HASH.getByteString()); + block.setMerkleRoot(); + return block; } } diff --git a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java index ed2121d360f..5105ce58dc7 100644 --- a/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java +++ b/framework/src/test/java/org/tron/core/net/messagehandler/TransactionsMsgHandlerTest.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; @@ -12,12 +13,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; -import lombok.Getter; -import org.joda.time.DateTime; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.tron.common.BaseTest; import org.tron.common.TestConstants; @@ -46,98 +47,115 @@ public static void init() { } @Test - public void testProcessMessage() { - TransactionsMsgHandler transactionsMsgHandler = new TransactionsMsgHandler(); + public void testProcessMessage() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); try { - transactionsMsgHandler.init(); - - PeerConnection peer = Mockito.mock(PeerConnection.class); - TronNetDelegate tronNetDelegate = Mockito.mock(TronNetDelegate.class); + ExecutorService pool = installMockPool(handler); + TronNetDelegate delegate = Mockito.mock(TronNetDelegate.class); AdvService advService = Mockito.mock(AdvService.class); + ChainBaseManager chainManager = Mockito.mock(ChainBaseManager.class); + ReflectUtils.setFieldValue(handler, "tronNetDelegate", delegate); + ReflectUtils.setFieldValue(handler, "advService", advService); + ReflectUtils.setFieldValue(handler, "chainBaseManager", chainManager); + Assert.assertFalse(handler.isBusy()); - Field field = TransactionsMsgHandler.class.getDeclaredField("tronNetDelegate"); - field.setAccessible(true); - field.set(transactionsMsgHandler, tronNetDelegate); + PeerConnection peer = Mockito.mock(PeerConnection.class); + Protocol.Transaction trx = buildTransferMessage(1).getTransactions().getTransactions(0); + trx = trx.toBuilder().setRawData(trx.getRawData().toBuilder() + .setExpiration(1_700_000_060_000L)).build(); + TransactionsMessage msg = new TransactionsMessage(Collections.singletonList(trx)); + stubAdvInvRequest(peer, msg); + handler.processMessage(peer, msg); - Assert.assertFalse(transactionsMsgHandler.isBusy()); + Assert.assertTrue(peer.getAdvInvRequest().isEmpty()); + ArgumentCaptor submitted = ArgumentCaptor.forClass(Runnable.class); + Mockito.verify(pool).submit(submitted.capture()); + Mockito.verify(delegate).getCachedTransactionSize(); + Mockito.verifyNoMoreInteractions(delegate); + Mockito.verifyNoInteractions(advService, chainManager); + Mockito.when(chainManager.getNextBlockSlotTime()).thenReturn(1_700_000_000_000L); + submitted.getValue().run(); + Mockito.verify(delegate).pushTransaction(Mockito.any()); + Mockito.verify(advService).broadcast(Mockito.any(TransactionMessage.class)); + } finally { + handler.close(); + } + } - BalanceContract.TransferContract transferContract = BalanceContract.TransferContract - .newBuilder() - .setAmount(10) - .setOwnerAddress(ByteString.copyFrom(ByteArray.fromHexString("121212a9cf"))) - .setToAddress(ByteString.copyFrom(ByteArray.fromHexString("232323a9cf"))).build(); + @Test + public void testSmartContractQueueFull() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + try { + ExecutorService pool = installMockPool(handler); + // Leave the scheduler stopped so queue capacity does not depend on thread timing. + BlockingQueue smartQueue = new LinkedBlockingQueue<>(1); + ReflectUtils.setFieldValue(handler, "smartContractQueue", smartQueue); + PeerConnection peer = Mockito.mock(PeerConnection.class); + Protocol.Transaction trx = TvmTestUtils.generateTriggerSmartContractAndGetTransaction( + ByteArray.fromHexString("121212a9cf"), ByteArray.fromHexString("121212a9cf"), + ByteArray.fromHexString("123456"), 100, 100000000, 0, 0); + TransactionsMessage msg = new TransactionsMessage(Collections.singletonList(trx)); + stubAdvInvRequest(peer, msg); + handler.processMessage(peer, msg); + Assert.assertTrue(peer.getAdvInvRequest().isEmpty()); + Assert.assertEquals(1, smartQueue.size()); + TransactionsMsgHandler.TrxEvent queued = smartQueue.peek(); + Assert.assertSame(peer, queued.getPeer()); + Assert.assertEquals(new TransactionMessage(trx).getMessageId(), + queued.getMsg().getMessageId()); + + // A second requested transaction is dropped when the queue is already full. + Protocol.Transaction second = trx.toBuilder().setRawData(trx.getRawData().toBuilder() + .setTimestamp(1)).build(); + TransactionsMessage secondMsg = new TransactionsMessage( + Collections.singletonList(second)); + stubAdvInvRequest(peer, secondMsg); + handler.processMessage(peer, secondMsg); + Assert.assertTrue(peer.getAdvInvRequest().isEmpty()); + Assert.assertEquals(1, smartQueue.size()); + Assert.assertSame(queued, smartQueue.peek()); + Mockito.verify(pool, Mockito.never()).submit(Mockito.any(Runnable.class)); + } finally { + handler.close(); + } + } - long transactionTimestamp = DateTime.now().minusDays(4).getMillis(); + @Test + public void testTransactionWithoutContract() throws Exception { + TransactionsMsgHandler handler = new TransactionsMsgHandler(); + try { + ExecutorService pool = installMockPool(handler); + PeerConnection peer = Mockito.mock(PeerConnection.class); Protocol.Transaction trx = Protocol.Transaction.newBuilder().setRawData( - Protocol.Transaction.raw.newBuilder().setTimestamp(transactionTimestamp) - .setRefBlockNum(1) - .addContract( - Protocol.Transaction.Contract.newBuilder() - .setType(Protocol.Transaction.Contract.ContractType.TransferContract) - .setParameter(Any.pack(transferContract)).build()).build()) - .build(); - Map advInvRequest = new ConcurrentHashMap<>(); - Item item = new Item(new TransactionMessage(trx).getMessageId(), - Protocol.Inventory.InventoryType.TRX); - advInvRequest.put(item, 0L); - Mockito.when(peer.getAdvInvRequest()).thenReturn(advInvRequest); + Protocol.Transaction.raw.newBuilder().setTimestamp(1).setRefBlockNum(1)).build(); + TransactionsMessage msg = new TransactionsMessage(Collections.singletonList(trx)); + Mockito.when(peer.getAdvInvRequest()).thenReturn(new ConcurrentHashMap<>()); + P2pException missingRequest = Assert.assertThrows(P2pException.class, + () -> handler.processMessage(peer, msg)); + Assert.assertEquals(TypeEnum.BAD_MESSAGE, missingRequest.getType()); - List transactionList = new ArrayList<>(); - transactionList.add(trx); - transactionsMsgHandler.processMessage(peer, new TransactionsMessage(transactionList)); - Assert.assertNull(advInvRequest.get(item)); - //Thread.sleep(10); - BlockingQueue smartContractQueue = - new LinkedBlockingQueue(2); - smartContractQueue.offer(new TrxEvent(null, null)); - smartContractQueue.offer(new TrxEvent(null, null)); - Field field1 = TransactionsMsgHandler.class.getDeclaredField("smartContractQueue"); - field1.setAccessible(true); - field1.set(transactionsMsgHandler, smartContractQueue); - Protocol.Transaction trx1 = TvmTestUtils.generateTriggerSmartContractAndGetTransaction( - ByteArray.fromHexString("121212a9cf"), - ByteArray.fromHexString("121212a9cf"), - ByteArray.fromHexString("123456"), - 100, 100000000, 0, 0); - Map advInvRequest1 = new ConcurrentHashMap<>(); - Item item1 = new Item(new TransactionMessage(trx1).getMessageId(), - Protocol.Inventory.InventoryType.TRX); - advInvRequest1.put(item1, 0L); - Mockito.when(peer.getAdvInvRequest()).thenReturn(advInvRequest1); - List transactionList1 = new ArrayList<>(); - transactionList1.add(trx1); - transactionsMsgHandler.processMessage(peer, new TransactionsMessage(transactionList1)); - Assert.assertNull(advInvRequest.get(item1)); - - // test 0 contract - Protocol.Transaction trx2 = Protocol.Transaction.newBuilder().setRawData( - Protocol.Transaction.raw.newBuilder().setTimestamp(transactionTimestamp) - .setRefBlockNum(1).build()) - .build(); - List transactionList2 = new ArrayList<>(); - transactionList2.add(trx2); - try { - transactionsMsgHandler.processMessage(peer, new TransactionsMessage(transactionList2)); - } catch (Exception ep) { - Assert.assertTrue(true); - } - Map advInvRequest2 = new ConcurrentHashMap<>(); - Item item2 = new Item(new TransactionMessage(trx2).getMessageId(), - Protocol.Inventory.InventoryType.TRX); - advInvRequest2.put(item2, 0L); - Mockito.when(peer.getAdvInvRequest()).thenReturn(advInvRequest2); - try { - transactionsMsgHandler.processMessage(peer, new TransactionsMessage(transactionList2)); - } catch (Exception ep) { - Assert.assertTrue(true); - } - } catch (Exception e) { - Assert.fail(); + stubAdvInvRequest(peer, msg); + P2pException missingContract = Assert.assertThrows(P2pException.class, + () -> handler.processMessage(peer, msg)); + Assert.assertEquals(TypeEnum.BAD_TRX, missingContract.getType()); + Assert.assertEquals(1, peer.getAdvInvRequest().size()); + Mockito.verify(pool, Mockito.never()).submit(Mockito.any(Runnable.class)); } finally { - transactionsMsgHandler.close(); + handler.close(); } } + private ExecutorService installMockPool(TransactionsMsgHandler handler) throws Exception { + ExecutorService original = (ExecutorService) ReflectUtils.getFieldObject(handler, + "trxHandlePool"); + original.shutdown(); + Assert.assertTrue(original.awaitTermination(5, TimeUnit.SECONDS)); + ExecutorService pool = Mockito.mock(ExecutorService.class); + Mockito.when(pool.awaitTermination(Mockito.anyLong(), Mockito.any())).thenReturn(true); + ReflectUtils.setFieldValue(handler, "trxHandlePool", pool); + return pool; + } + @Test public void testProcessMessageAfterClose() throws Exception { TransactionsMsgHandler handler = new TransactionsMsgHandler(); @@ -157,13 +175,9 @@ public void testProcessMessageAfterClose() throws Exception { public void testRejectedExecution() throws Exception { TransactionsMsgHandler handler = new TransactionsMsgHandler(); try { - ExecutorService mockPool = Mockito.mock(ExecutorService.class); + ExecutorService mockPool = installMockPool(handler); Mockito.when(mockPool.submit(Mockito.any(Runnable.class))) .thenThrow(new RejectedExecutionException("pool closed")); - Field poolField = TransactionsMsgHandler.class.getDeclaredField("trxHandlePool"); - poolField.setAccessible(true); - poolField.set(handler, mockPool); - PeerConnection peer = Mockito.mock(PeerConnection.class); TransactionsMessage msg = buildTransferMessage(2); stubAdvInvRequest(peer, msg); @@ -183,16 +197,12 @@ public void testCloseDuringProcessing() throws Exception { Field closedField = TransactionsMsgHandler.class.getDeclaredField("isClosed"); closedField.setAccessible(true); - ExecutorService mockPool = Mockito.mock(ExecutorService.class); + ExecutorService mockPool = installMockPool(handler); // on the first submit, flip isClosed to true so the second iteration breaks Mockito.when(mockPool.submit(Mockito.any(Runnable.class))).thenAnswer(inv -> { closedField.set(handler, true); return null; }); - Field poolField = TransactionsMsgHandler.class.getDeclaredField("trxHandlePool"); - poolField.setAccessible(true); - poolField.set(handler, mockPool); - PeerConnection peer = Mockito.mock(PeerConnection.class); TransactionsMessage msg = buildTransferMessage(2); stubAdvInvRequest(peer, msg); @@ -340,8 +350,8 @@ public void testDuplicateTransactionRejected() throws Exception { @Test public void testInvalidSigLength() throws Exception { TransactionsMsgHandler handler = new TransactionsMsgHandler(); - handler.init(); try { + ExecutorService pool = installMockPool(handler); PeerConnection peer = Mockito.mock(PeerConnection.class); BalanceContract.TransferContract transferContract = BalanceContract.TransferContract @@ -417,6 +427,7 @@ public void testInvalidSigLength() throws Exception { paddedList.add(paddedSigTrx); stubAdvInvRequest(peer, new TransactionsMessage(paddedList)); handler.processMessage(peer, new TransactionsMessage(paddedList)); + Mockito.verify(pool, Mockito.times(2)).submit(Mockito.any(Runnable.class)); } finally { handler.close(); } @@ -426,37 +437,26 @@ public void testInvalidSigLength() throws Exception { public void testIsBusyWithCachedTransactions() throws Exception { TransactionsMsgHandler handler = new TransactionsMsgHandler(); - int threshold = Args.getInstance().getMaxTrxCacheSize(); - TronNetDelegate tronNetDelegateMock = Mockito.mock(TronNetDelegate.class); - Field field = TransactionsMsgHandler.class.getDeclaredField("tronNetDelegate"); - field.setAccessible(true); - field.set(handler, tronNetDelegateMock); - - // queue and smartContractQueue are empty, but cached size > threshold - Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(threshold + 1); - Assert.assertTrue(handler.isBusy()); - - // boundary: cached size == threshold, isBusy() uses strict >, so not busy - Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(threshold); - Assert.assertFalse(handler.isBusy()); - - Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(0); - Assert.assertFalse(handler.isBusy()); - } + try { + int threshold = Args.getInstance().getMaxTrxCacheSize(); + TronNetDelegate tronNetDelegateMock = Mockito.mock(TronNetDelegate.class); + Field field = TransactionsMsgHandler.class.getDeclaredField("tronNetDelegate"); + field.setAccessible(true); + field.set(handler, tronNetDelegateMock); - class TrxEvent { + // queue and smartContractQueue are empty, but cached size > threshold + Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(threshold + 1); + Assert.assertTrue(handler.isBusy()); - @Getter - private PeerConnection peer; - @Getter - private TransactionMessage msg; - @Getter - private long time; + // boundary: cached size == threshold, isBusy() uses strict >, so not busy + Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(threshold); + Assert.assertFalse(handler.isBusy()); - public TrxEvent(PeerConnection peer, TransactionMessage msg) { - this.peer = peer; - this.msg = msg; - this.time = System.currentTimeMillis(); + Mockito.when(tronNetDelegateMock.getCachedTransactionSize()).thenReturn(0); + Assert.assertFalse(handler.isBusy()); + } finally { + handler.close(); } } + } diff --git a/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java b/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java index 89041cb9885..57a9da98079 100644 --- a/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/EffectiveCheckServiceTest.java @@ -2,10 +2,15 @@ import java.lang.reflect.Method; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Resource; +import org.junit.After; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.tron.common.BaseTest; import org.tron.common.TestConstants; import org.tron.common.utils.PublicMethod; @@ -17,6 +22,20 @@ public class EffectiveCheckServiceTest extends BaseTest { + private P2pConfig savedP2pConfig; + private boolean p2pStarted; + + @After + public void closeP2p() { + if (p2pStarted) { + try { + TronNetService.getP2pService().close(); + } finally { + ReflectUtils.setFieldValue(tronNetService, "p2pConfig", savedP2pConfig); + } + } + } + @Resource private EffectiveCheckService service; @Resource @@ -45,15 +64,29 @@ public void testFind() { P2pConfig p2pConfig = new P2pConfig(); p2pConfig.setIp("127.0.0.1"); p2pConfig.setPort(port); + savedP2pConfig = TronNetService.getP2pConfig(); ReflectUtils.setFieldValue(tronNetService, "p2pConfig", p2pConfig); + p2pStarted = true; TronNetService.getP2pService().start(p2pConfig); - service.triggerNext(); - Assert.assertNull(service.getCur()); + ScheduledExecutorService executor = Mockito.mock(ScheduledExecutorService.class); + Mockito.when(executor.submit(Mockito.any(Runnable.class))) + .thenReturn(CompletableFuture.completedFuture(null)); + ScheduledExecutorService originalExecutor = ReflectUtils.getFieldValue(service, "executor"); + try { + ReflectUtils.setFieldValue(service, "executor", executor); + service.triggerNext(); + ArgumentCaptor task = ArgumentCaptor.forClass(Runnable.class); + Mockito.verify(executor).submit(task.capture()); + task.getValue().run(); + Assert.assertNull(service.getCur()); - ReflectUtils.invokeMethod(service, "resetCount"); - InetSocketAddress cur = new InetSocketAddress("192.168.0.1", port); - service.setCur(cur); - service.onDisconnect(cur); + ReflectUtils.invokeMethod(service, "resetCount"); + InetSocketAddress cur = new InetSocketAddress("192.168.0.1", port); + service.setCur(cur); + service.onDisconnect(cur); + } finally { + ReflectUtils.setFieldValue(service, "executor", originalExecutor); + } } } diff --git a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java index b8b0d5f6deb..3a0fc74c73f 100644 --- a/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java +++ b/framework/src/test/java/org/tron/core/net/services/HandShakeServiceTest.java @@ -142,7 +142,7 @@ public void testInvalidHelloMessage() { HelloMessage helloMessage3 = new HelloMessage(builder.build().toByteArray()); Assert.assertFalse(helloMessage3.valid()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -202,7 +202,7 @@ public void testRelayHelloMessage() throws NoSuchMethodException { HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } Args.getInstance().fastForward = false; } @@ -231,7 +231,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } //genesisBlock is not equal => INCOMPATIBLE_CHAIN @@ -247,7 +247,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // peer's solidityBlock <= my solidityBlock, but not contained @@ -270,7 +270,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } // peer's solidityBlock <= my solidityBlock, but not contained @@ -280,7 +280,7 @@ public void testLowAndGenesisBlockNum() throws NoSuchMethodException { HelloMessage helloMessage = new HelloMessage(builder.build().toByteArray()); method.invoke(p2pEventHandler, peer, helloMessage.getSendBytes()); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } @@ -310,7 +310,7 @@ public void testProcessHelloMessage() { HandshakeService handshakeService = new HandshakeService(); handshakeService.processHelloMessage(p, helloMessage); } catch (Exception e) { - Assert.fail(); + throw new AssertionError("Unexpected exception", e); } } 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 a1bffd5bf1f..34022b66d5a 100644 --- a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java +++ b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java @@ -102,6 +102,8 @@ public class ComputeRewardTest extends BaseMethodTest { private static final byte[] SR_ADDRESS_26 = ByteArray.fromHexString( "4105b9e8af8ee371cad87317f442d155b39fbd1c25"); + private final Map flushServices = new HashMap<>(); + private static DynamicPropertiesStore propertiesStore; private static DelegationStore delegationStore; private static AccountStore accountStore; @@ -127,9 +129,16 @@ protected void afterInit() { setUp(); } + @Override + protected void beforeDestroy() { + // Stop test-owned workers before BaseMethodTest closes the stores they access. + flushServices.forEach((name, executor) -> + ExecutorServiceManager.shutdownAndAwaitTermination(executor, "flush-service-" + name)); + flushServices.clear(); + } + private void setUp() { // mock flush service - Map flushServices = new HashMap<>(); flushServices.put("propertiesStore", MoreExecutors.listeningDecorator( ExecutorServiceManager.newSingleThreadExecutor( "flush-service-propertiesStore"))); diff --git a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java index ce0a09da94a..24acf944b78 100644 --- a/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java +++ b/framework/src/test/java/org/tron/core/services/NodeInfoServiceTest.java @@ -29,6 +29,9 @@ @Slf4j public class NodeInfoServiceTest extends BaseTest { + private P2pConfig savedP2pConfig; + private boolean p2pStarted; + @Resource protected NodeInfoService nodeInfoService; @Resource @@ -47,7 +50,17 @@ public static void init() { @After public void clearPeers() { - closePeer(); + try { + closePeer(); + } finally { + if (p2pStarted) { + try { + TronNetService.getP2pService().close(); + } finally { + ReflectUtils.setFieldValue(tronNetService, "p2pConfig", savedP2pConfig); + } + } + } } @Test @@ -73,7 +86,9 @@ private void addPeer() { P2pConfig p2pConfig = new P2pConfig(); p2pConfig.setIp("127.0.0.1"); p2pConfig.setPort(port); + savedP2pConfig = TronNetService.getP2pConfig(); ReflectUtils.setFieldValue(tronNetService, "p2pConfig", p2pConfig); + p2pStarted = true; TronNetService.getP2pService().start(p2pConfig); ApplicationContext ctx = (ApplicationContext) ReflectUtils.getFieldObject(p2pEventHandler, diff --git a/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java b/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java index c99b6064d15..58b71b4ada5 100644 --- a/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/HttpApiAccessFilterTest.java @@ -1,18 +1,19 @@ package org.tron.core.services.filter; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Resource; -import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.tron.common.BaseTest; @@ -34,7 +35,7 @@ public class HttpApiAccessFilterTest extends BaseTest { private HttpApiOnPBFTService httpApiOnPBFTService; @Resource private HttpApiAccessFilter httpApiAccessFilter; - private static final CloseableHttpClient httpClient = HttpClients.createDefault(); + private final CloseableHttpClient httpClient = HttpClients.createDefault(); static { Args.setParam(new String[]{"-d", dbPath()}, TestConstants.TEST_CONF); @@ -49,7 +50,7 @@ public class HttpApiAccessFilterTest extends BaseTest { } @Test - public void testHttpFilter() { + public void testHttpFilter() throws IOException { appT.startup(); List disabledApiList = new ArrayList<>(); disabledApiList.add("getaccount"); @@ -80,7 +81,7 @@ public void testHttpFilter() { Args.getInstance().setDisabledApiList(disabledApiList); String response = sendGetRequest(url); Assert.assertEquals("{\"Error\":\"this API is unavailable due to config\"}", - response); + response.trim()); Args.getInstance().setDisabledApiList(emptyList); int statusCode = getRequestCode(url); @@ -89,39 +90,25 @@ public void testHttpFilter() { } } - private String sendGetRequest(String url) { + @After + public void closeHttpClient() throws IOException { + httpClient.close(); + } + + private String sendGetRequest(String url) throws IOException { HttpGet request = new HttpGet(url); request.setHeader("User-Agent", "Java client"); - HttpResponse response; - try { - response = httpClient.execute(request); - BufferedReader rd = new BufferedReader( - new InputStreamReader(response.getEntity().getContent())); - StringBuilder result = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - return result.toString(); - } catch (IOException e) { - e.printStackTrace(); + try (CloseableHttpResponse response = httpClient.execute(request)) { + return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); } - return null; } - private int getRequestCode(String url) { + private int getRequestCode(String url) throws IOException { HttpGet request = new HttpGet(url); request.setHeader("User-Agent", "Java client"); - HttpResponse response; - - try { - response = httpClient.execute(request); + try (CloseableHttpResponse response = httpClient.execute(request)) { return response.getStatusLine().getStatusCode(); - } catch (IOException e) { - e.printStackTrace(); } - - return 0; } @Test diff --git a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java index 5c9b1d9a52c..00cc79aa816 100644 --- a/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java +++ b/framework/src/test/java/org/tron/core/services/filter/LiteFnQueryHttpFilterTest.java @@ -3,17 +3,16 @@ import static org.tron.core.ChainBaseManager.NodeType.FULL; import static org.tron.core.ChainBaseManager.NodeType.LITE; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Set; import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -56,9 +55,9 @@ public void init() { } @Test - public void testHttpFilter() { + public void testHttpFilter() throws IOException { Set urlPathSets = LiteFnQueryHttpFilter.getFilterPaths(); - urlPathSets.forEach(urlPath -> { + for (String urlPath : urlPathSets) { if (urlPath.contains("/walletsolidity")) { fullHttpPort = Args.getInstance().getSolidityHttpPort(); } else if (urlPath.contains("/walletpbft")) { @@ -71,10 +70,10 @@ public void testHttpFilter() { chainBaseManager.setNodeType(LITE); Args.getInstance().setOpenHistoryQueryWhenLiteFN(false); String response = sendGetRequest(url); - logger.info("response:{}", response); + Assert.assertEquals("this API is closed because this node is a lite fullnode", response); // test lite fullnode with history query opened - chainBaseManager.setNodeType(FULL); + chainBaseManager.setNodeType(LITE); Args.getInstance().setOpenHistoryQueryWhenLiteFN(true); response = sendGetRequest(url); Assert.assertNotEquals("this API is closed because this node is a lite fullnode", @@ -86,43 +85,20 @@ public void testHttpFilter() { response = sendGetRequest(url); Assert.assertNotEquals("this API is closed because this node is a lite fullnode", response); - }); + } } - private String sendGetRequest(String url) { - HttpGet request = new HttpGet(url); - request.setHeader("User-Agent", "Java client"); - HttpResponse response; - try { - response = httpClient.execute(request); - BufferedReader rd = new BufferedReader( - new InputStreamReader(response.getEntity().getContent())); - StringBuilder result = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - return result.toString(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; + @After + public void closeHttpClient() throws IOException { + httpClient.close(); } - private String sendPostRequest(String url, String body) throws IOException { - HttpPost request = new HttpPost(url); + private String sendGetRequest(String url) throws IOException { + HttpGet request = new HttpGet(url); request.setHeader("User-Agent", "Java client"); - StringEntity entity = new StringEntity(body); - request.setEntity(entity); - HttpResponse response = httpClient.execute(request); - BufferedReader rd = new BufferedReader( - new InputStreamReader(response.getEntity().getContent())); - StringBuilder result = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); + try (CloseableHttpResponse response = httpClient.execute(request)) { + return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); } - return result.toString(); } } diff --git a/framework/src/test/java/org/tron/core/services/http/BroadcastServletTest.java b/framework/src/test/java/org/tron/core/services/http/BroadcastServletTest.java index d6bf3850f30..6b7a39c2521 100644 --- a/framework/src/test/java/org/tron/core/services/http/BroadcastServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/BroadcastServletTest.java @@ -1,114 +1,53 @@ package org.tron.core.services.http; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintStream; import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLStreamHandlerFactory; -import java.nio.charset.StandardCharsets; +import java.io.StringReader; +import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.PublicMethod; -import org.tron.core.services.http.solidity.mockito.HttpUrlStreamHandler; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.test.util.ReflectionTestUtils; +import org.tron.api.GrpcAPI; +import org.tron.common.TestConstants; +import org.tron.core.Wallet; +import org.tron.core.actuator.TransactionFactory; +import org.tron.core.config.args.Args; +import org.tron.json.JSONObject; +import org.tron.protos.Protocol.Transaction; +import org.tron.protos.Protocol.Transaction.Contract.ContractType; +import org.tron.protos.contract.BalanceContract.TransferContract; -@Slf4j public class BroadcastServletTest { - private static HttpUrlStreamHandler httpUrlStreamHandler; - private BroadcastServlet broadcastServlet; - private HttpServletRequest request; - private HttpServletResponse response; - private HttpURLConnection httpUrlConnection; - private OutputStreamWriter outputStreamWriter; - private URL url; - - /** - * init before class. - */ - @BeforeClass - public static void init() { - // Allows for mocking URL connections - URLStreamHandlerFactory urlStreamHandlerFactory = mock(URLStreamHandlerFactory.class); - try { - URL.setURLStreamHandlerFactory(urlStreamHandlerFactory); - } catch (Error e) { - logger.info("Ignore error: {}", e.getMessage()); - } - - - httpUrlStreamHandler = new HttpUrlStreamHandler(); - given(urlStreamHandlerFactory.createURLStreamHandler("http")).willReturn(httpUrlStreamHandler); - - } - - /** - * set up. - * - */ @Before public void setUp() { - broadcastServlet = new BroadcastServlet(); - this.request = mock(HttpServletRequest.class); - this.response = mock(HttpServletResponse.class); - this.httpUrlConnection = mock(HttpURLConnection.class); - this.outputStreamWriter = mock(OutputStreamWriter.class); - httpUrlStreamHandler.resetConnections(); + Args.setParam(new String[0], TestConstants.TEST_CONF); } - /** - * after test. - */ @After public void tearDown() { - if (FileUtil.deleteDir(new File("temp.txt"))) { - logger.info("Release resources successful."); - } else { - logger.info("Release resources failure."); - } + Args.clearParam(); } @Test public void doPostTest() throws IOException { - URLStreamHandlerFactory urlStreamHandlerFactory = mock(URLStreamHandlerFactory.class); - httpUrlStreamHandler = new HttpUrlStreamHandler(); - given(urlStreamHandlerFactory.createURLStreamHandler("http")).willReturn(httpUrlStreamHandler); - - broadcastServlet = new BroadcastServlet(); - this.request = mock(HttpServletRequest.class); - this.response = mock(HttpServletResponse.class); - this.httpUrlConnection = mock(HttpURLConnection.class); - this.outputStreamWriter = mock(OutputStreamWriter.class); - httpUrlStreamHandler.resetConnections(); - - final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - String href = "http://127.0.0.1:" - + PublicMethod.chooseRandomPort() + "/wallet/broadcasttransaction"; - httpUrlStreamHandler.addConnection(new URL(href), httpUrlConnection); - httpUrlConnection.setRequestMethod("POST"); - httpUrlConnection.setRequestProperty("Content-Type", "application/json"); - httpUrlConnection.setRequestProperty("Connection", "Keep-Alive"); - httpUrlConnection.setUseCaches(false); - httpUrlConnection.setDoOutput(true); + BroadcastServlet servlet = new BroadcastServlet(); + Wallet wallet = mock(Wallet.class); + ReflectionTestUtils.setField(servlet, "wallet", wallet); + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); String postData = "{\"signature\":[\"97c825b41c77de2a8bd65b3df55cd4c0df59c307c0187e" + "42321dcc1cc455ddba583dd9502e17cfec5945b34cad0511985a6165999092a6dec84c2bdd9" + "7e649fc01\"],\"txID\":\"454f156bf1256587ff6ccdbc56e64ad0c51e4f8efea5490dcbc7" @@ -119,44 +58,23 @@ public void doPostTest() throws IOException { + "eapis.com/protocol.TransferContract\"},\"type\":\"TransferCon" + "tract\"}],\"ref_block_bytes\":\"267e\",\"ref_block_hash\":\"9a447d222e8" + "de9f2\",\"expiration\":1530893064000,\"timestamp\":1530893006233}}"; - httpUrlConnection.setRequestProperty("Content-Length", String.valueOf(postData.length())); - - when(httpUrlConnection.getOutputStream()).thenReturn(outContent); - OutputStreamWriter out = new OutputStreamWriter(httpUrlConnection.getOutputStream(), - StandardCharsets.UTF_8); - out.write(postData); - out.flush(); - out.close(); - PrintWriter writer = new PrintWriter("temp.txt"); - when(response.getWriter()).thenReturn(writer); - - broadcastServlet.doPost(request, response); - // Get Response Body - String line; - StringBuilder result = new StringBuilder(); - - byte[] buffer = new byte[1024]; - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer); - when(httpUrlConnection.getInputStream()).thenReturn(byteArrayInputStream); - BufferedReader in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(), - StandardCharsets.UTF_8)); - - while ((line = in.readLine()) != null) { - result.append(line).append("\n"); - } - Assert.assertNotNull(result); - in.close(); - writer.flush(); - FileInputStream fileInputStream = new FileInputStream("temp.txt"); - InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - - StringBuilder sb = new StringBuilder(); - String text; - while ((text = bufferedReader.readLine()) != null) { - sb.append(text); + when(request.getReader()).thenReturn(new BufferedReader(new StringReader(postData))); + when(wallet.broadcastTransaction(org.mockito.ArgumentMatchers.any(Transaction.class))) + .thenReturn(GrpcAPI.Return.newBuilder().setResult(true).build()); + StringWriter body = new StringWriter(); + try (MockedStatic contracts = Mockito.mockStatic(TransactionFactory.class); + PrintWriter writer = new PrintWriter(body)) { + contracts.when(() -> TransactionFactory.getContract(ContractType.TransferContract)) + .thenReturn(TransferContract.class); + when(response.getWriter()).thenReturn(writer); + servlet.doPost(request, response); + writer.flush(); + JSONObject result = JSONObject.parseObject(body.toString()); + Assert.assertEquals(Boolean.TRUE, result.get("result")); + Assert.assertNotNull(result.getString("txid")); + ArgumentCaptor transaction = ArgumentCaptor.forClass(Transaction.class); + verify(wallet).broadcastTransaction(transaction.capture()); + Assert.assertEquals(1, transaction.getValue().getRawData().getContractCount()); } - Assert.assertTrue(sb.toString().contains("null")); - httpUrlConnection.disconnect(); } -} \ No newline at end of file +} diff --git a/framework/src/test/java/org/tron/core/services/http/TriggerSmartContractServletTest.java b/framework/src/test/java/org/tron/core/services/http/TriggerSmartContractServletTest.java index bae9523401b..e02edceff9b 100644 --- a/framework/src/test/java/org/tron/core/services/http/TriggerSmartContractServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/TriggerSmartContractServletTest.java @@ -1,8 +1,17 @@ package org.tron.core.services.http; import com.google.gson.JsonObject; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import lombok.extern.slf4j.Slf4j; -import org.apache.http.HttpResponse; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.bouncycastle.util.encoders.Hex; import org.junit.Assert; import org.junit.Before; @@ -12,12 +21,12 @@ import org.tron.common.TestConstants; import org.tron.common.utils.ByteArray; import org.tron.common.utils.PublicMethod; -import org.tron.common.utils.client.utils.HttpMethed; import org.tron.core.capsule.ContractCapsule; import org.tron.core.config.args.Args; import org.tron.core.store.StoreFactory; import org.tron.core.vm.repository.Repository; import org.tron.core.vm.repository.RepositoryImpl; +import org.tron.json.JSONObject; import org.tron.protos.Protocol; import org.tron.protos.contract.SmartContractOuterClass; @@ -61,28 +70,25 @@ public void before() { @Test - public void testNormalCall() { - HttpMethed.waitToProduceOneBlock(httpNode); + public void testNormalCall() throws IOException { JsonObject parameter = new JsonObject(); parameter.addProperty("owner_address", ByteArray.toHexString(ownerAddr)); parameter.addProperty("contract_address", ByteArray.toHexString(contractAddr)); parameter.addProperty("function_selector", "test()"); - HttpResponse triggersmartcontract1 = invokeToLocal("triggersmartcontract", parameter); - HttpResponse triggersmartcontract2 = invokeToLocal("triggerconstantcontract", parameter); - HttpResponse triggersmartcontract3 = invokeToLocal("estimateenergy", parameter); - Assert.assertNotNull(triggersmartcontract1); - Assert.assertNotNull(triggersmartcontract2); - Assert.assertNotNull(triggersmartcontract3); - } - - public static HttpResponse invokeToLocal( - String method, JsonObject parameter) { - try { - final String requestUrl = "http://" + httpNode + "/wallet/" + method; - return HttpMethed.createConnect(requestUrl, parameter); - } catch (Exception e) { - e.printStackTrace(); - return null; + RequestConfig timeouts = RequestConfig.custom().setConnectTimeout(5000) + .setConnectionRequestTimeout(5000).setSocketTimeout(10000).build(); + try (CloseableHttpClient client = HttpClients.custom() + .setDefaultRequestConfig(timeouts).build()) { + for (String method : new String[]{"triggersmartcontract", "triggerconstantcontract", + "estimateenergy"}) { + HttpPost request = new HttpPost("http://" + httpNode + "/wallet/" + method); + request.setEntity(new StringEntity(parameter.toString(), ContentType.APPLICATION_JSON)); + try (CloseableHttpResponse response = client.execute(request)) { + Assert.assertEquals(method, 200, response.getStatusLine().getStatusCode()); + String body = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); + Assert.assertNotNull(method, JSONObject.parseObject(body)); + } + } } } } diff --git a/framework/src/test/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServletTest.java b/framework/src/test/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServletTest.java index e1abb41d1e1..96901efa994 100644 --- a/framework/src/test/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServletTest.java +++ b/framework/src/test/java/org/tron/core/services/http/solidity/GetTransactionByIdSolidityServletTest.java @@ -1,202 +1,76 @@ package org.tron.core.services.http.solidity; -import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.protobuf.ByteString; import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintStream; import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLStreamHandlerFactory; -import java.nio.charset.StandardCharsets; +import java.io.StringReader; +import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import org.tron.common.utils.FileUtil; -import org.tron.common.utils.PublicMethod; -import org.tron.core.services.http.solidity.mockito.HttpUrlStreamHandler; +import org.springframework.test.util.ReflectionTestUtils; +import org.tron.common.TestConstants; +import org.tron.common.utils.ByteArray; +import org.tron.core.Wallet; +import org.tron.core.config.args.Args; - -@Slf4j public class GetTransactionByIdSolidityServletTest { - private static HttpUrlStreamHandler httpUrlStreamHandler; - private GetTransactionByIdSolidityServlet getTransactionByIdSolidityServlet; + private static final String TX_ID = + "309b6fa3d01353e46f57dd8a8f27611f98e392b50d035cef213f2c55225a8bd2"; + private GetTransactionByIdSolidityServlet servlet; + private Wallet wallet; private HttpServletRequest request; private HttpServletResponse response; - private HttpURLConnection httpUrlConnection; - private OutputStreamWriter outputStreamWriter; - private URL url; - - /** - * . - */ - @BeforeClass - public static void init() { - // Allows for mocking URL connections - URLStreamHandlerFactory urlStreamHandlerFactory = mock(URLStreamHandlerFactory.class); - try { - URL.setURLStreamHandlerFactory(urlStreamHandlerFactory); - } catch (Error e) { - logger.info("Ignore error: {}", e.getMessage()); - } - - httpUrlStreamHandler = new HttpUrlStreamHandler(); - given(urlStreamHandlerFactory.createURLStreamHandler("http")).willReturn(httpUrlStreamHandler); - } - - /** - * Init. - */ @Before public void setUp() { - getTransactionByIdSolidityServlet = new GetTransactionByIdSolidityServlet(); - this.request = mock(HttpServletRequest.class); - this.response = mock(HttpServletResponse.class); - this.httpUrlConnection = mock(HttpURLConnection.class); - this.outputStreamWriter = mock(OutputStreamWriter.class); - httpUrlStreamHandler.resetConnections(); + Args.setParam(new String[0], TestConstants.TEST_CONF); + servlet = new GetTransactionByIdSolidityServlet(); + wallet = mock(Wallet.class); + ReflectionTestUtils.setField(servlet, "wallet", wallet); + request = mock(HttpServletRequest.class); + response = mock(HttpServletResponse.class); } - /** - * Release Resource. - */ @After public void tearDown() { - if (FileUtil.deleteDir(new File("temp.txt"))) { - logger.info("Release resources successful."); - } else { - logger.info("Release resources failure."); - } + Args.clearParam(); } @Test public void doPostTest() throws IOException { - - //send Post request - - final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - String href = "http://127.0.0.1:" - + PublicMethod.chooseRandomPort() + "/walletsolidity/gettransactioninfobyid"; - httpUrlStreamHandler.addConnection(new URL(href), httpUrlConnection); - httpUrlConnection.setRequestMethod("POST"); - httpUrlConnection.setRequestProperty("Content-Type", "application/json"); - httpUrlConnection.setRequestProperty("Connection", "Keep-Alive"); - httpUrlConnection.setUseCaches(false); - httpUrlConnection.setDoOutput(true); - String postData = "{\"value\": \"309b6fa3d01353e46f57dd8a8f27611f98e392b50d035cef21" - + "3f2c55225a8bd2\"}"; - httpUrlConnection.setRequestProperty("Content-Length", String.valueOf(postData.length())); - - when(httpUrlConnection.getOutputStream()).thenReturn(outContent); - OutputStreamWriter out = new OutputStreamWriter(httpUrlConnection.getOutputStream(), - StandardCharsets.UTF_8); - out.write(postData); - out.flush(); - out.close(); - PrintWriter writer = new PrintWriter("temp.txt"); - when(response.getWriter()).thenReturn(writer); - - getTransactionByIdSolidityServlet.doPost(request, response); - // Get Response Body - String line; - StringBuilder result = new StringBuilder(); - - byte[] buffer = new byte[1024]; - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer); - when(httpUrlConnection.getInputStream()).thenReturn(byteArrayInputStream); - BufferedReader in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(), - StandardCharsets.UTF_8)); - - while ((line = in.readLine()) != null) { - result.append(line).append("\n"); - } - Assert.assertNotNull(result); - in.close(); - writer.flush(); - FileInputStream fileInputStream = new FileInputStream("temp.txt"); - InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - - StringBuilder sb = new StringBuilder(); - String text; - while ((text = bufferedReader.readLine()) != null) { - sb.append(text); - } - Assert.assertTrue(sb.toString().contains("null")); - httpUrlConnection.disconnect(); + when(request.getReader()).thenReturn(new BufferedReader( + new StringReader("{\"value\":\"" + TX_ID + "\"}"))); + assertMissingTransactionResponse(true); } @Test public void doGetTest() throws IOException { + when(request.getParameter("value")).thenReturn(TX_ID); + assertMissingTransactionResponse(false); + } - final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - String href = "http://127.0.0.1:" - + PublicMethod.chooseRandomPort() + "/walletsolidity/gettransactioninfobyid"; - httpUrlStreamHandler.addConnection(new URL(href), httpUrlConnection); - httpUrlConnection.setRequestMethod("GET"); - httpUrlConnection.setRequestProperty("Content-Type", "application/json"); - httpUrlConnection.setRequestProperty("Connection", "Keep-Alive"); - httpUrlConnection.setUseCaches(false); - httpUrlConnection.setDoOutput(true); - String postData = "{\"value\": \"309b6fa3d01353e46f57dd8a8f27611f98e392b50d035cef21" - + "3f2c55225a8bd2\"}"; - httpUrlConnection.setRequestProperty("Content-Length", String.valueOf(postData.length())); - - when(httpUrlConnection.getOutputStream()).thenReturn(outContent); - OutputStreamWriter out = new OutputStreamWriter(httpUrlConnection.getOutputStream(), - StandardCharsets.UTF_8); - out.write(postData); - out.flush(); - out.close(); - PrintWriter writer = new PrintWriter("temp.txt"); - when(response.getWriter()).thenReturn(writer); - - getTransactionByIdSolidityServlet.doPost(request, response); - // Get Response Body - String line; - StringBuilder result = new StringBuilder(); - - byte[] buffer = new byte[1024]; - ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buffer); - when(httpUrlConnection.getInputStream()).thenReturn(byteArrayInputStream); - BufferedReader in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(), - StandardCharsets.UTF_8)); - - while ((line = in.readLine()) != null) { - result.append(line).append("\n"); - } - Assert.assertNotNull(result); - in.close(); - writer.flush(); - FileInputStream fileInputStream = new FileInputStream("temp.txt"); - InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); - BufferedReader bufferedReader = new BufferedReader(inputStreamReader); - - StringBuilder sb = new StringBuilder(); - String text; - while ((text = bufferedReader.readLine()) != null) { - sb.append(text); + private void assertMissingTransactionResponse(boolean post) throws IOException { + StringWriter body = new StringWriter(); + try (PrintWriter writer = new PrintWriter(body)) { + when(response.getWriter()).thenReturn(writer); + if (post) { + servlet.doPost(request, response); + } else { + servlet.doGet(request, response); + } + writer.flush(); + Assert.assertEquals("{}", body.toString().trim()); + verify(wallet).getTransactionById(ByteString.copyFrom(ByteArray.fromHexString(TX_ID))); } - Assert.assertTrue(sb.toString().contains("null")); - httpUrlConnection.disconnect(); } } - diff --git a/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java b/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java index 8ea0f908899..2a64cc98b7b 100644 --- a/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java +++ b/framework/src/test/java/org/tron/core/services/ratelimiter/GlobalRateLimiterTest.java @@ -7,10 +7,11 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.FakeTimeRateLimiter; import com.google.common.util.concurrent.RateLimiter; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -22,6 +23,11 @@ public class GlobalRateLimiterTest { + private Object savedIpQps; + private Object savedLimiter; + private Object savedCache; + private MockedStatic rateLimiterFactory; + /** * Reset GlobalRateLimiter's static state to known rates before each test. * Static fields are initialized at class-load time from Args, so we must @@ -31,6 +37,12 @@ public class GlobalRateLimiterTest { public void setUp() throws Exception { String[] a = new String[0]; Args.setParam(a, TestConstants.TEST_CONF); + savedIpQps = field("IP_QPS").get(null); + savedLimiter = field("rateLimiter").get(null); + savedCache = field("cache").get(null); + rateLimiterFactory = mockStatic(RateLimiter.class, Mockito.CALLS_REAL_METHODS); + rateLimiterFactory.when(() -> RateLimiter.create(Mockito.anyDouble())) + .thenAnswer(invocation -> FakeTimeRateLimiter.create(invocation.getArgument(0))); resetGlobalRateLimiter(2.0, 1.0); } @@ -40,13 +52,8 @@ private static void resetGlobalRateLimiter(double globalQps, double ipQps) throw ipQpsField.setAccessible(true); ipQpsField.set(null, ipQps); - // Create a fresh rate limiter, then sleep one stable interval (1000/qps ms) so - // Guava's SmoothBursty accumulates exactly 1 stored permit. With 1 stored permit - // the first tryAcquire() consumes it (no advance of nextFreeTicket), and the second - // call pre-bills the next slot and still returns true — giving exactly floor(qps)=2 - // consecutive successes without touching Guava-internal fields. - RateLimiter rl = RateLimiter.create(globalQps); - Thread.sleep((long) (1000.0 / globalQps)); + // One stored permit plus the next reserved slot allow exactly two immediate requests. + RateLimiter rl = FakeTimeRateLimiter.createWithStoredPermit(globalQps); Field rateLimiterField = GlobalRateLimiter.class.getDeclaredField("rateLimiter"); rateLimiterField.setAccessible(true); @@ -241,8 +248,25 @@ private static void injectIpQps(double qps) throws Exception { f.set(null, qps); } - @AfterClass - public static void destroy() { - Args.clearParam(); + private static Field field(String name) throws Exception { + Field field = GlobalRateLimiter.class.getDeclaredField(name); + field.setAccessible(true); + return field; + } + + @After + public void destroy() throws Exception { + try { + if (rateLimiterFactory != null) { + rateLimiterFactory.close(); + } + if (savedLimiter != null) { + field("IP_QPS").set(null, savedIpQps); + field("rateLimiter").set(null, savedLimiter); + field("cache").set(null, savedCache); + } + } finally { + Args.clearParam(); + } } } 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 5ab85a42bbf..76df2e753cf 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 @@ -1,15 +1,16 @@ package org.tron.core.services.ratelimiter.adaptor; import com.google.common.cache.Cache; +import com.google.common.util.concurrent.FakeTimeRateLimiter; import com.google.common.util.concurrent.RateLimiter; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.tron.common.TestConstants; -import org.tron.common.es.ExecutorServiceManager; import org.tron.common.utils.ReflectUtils; import org.tron.core.config.args.Args; import org.tron.core.services.ratelimiter.RuntimeData; @@ -23,14 +24,25 @@ public class AdaptorTest { + private MockedStatic rateLimiterFactory; + @Before public void setUp() { Args.setParam(new String[0], TestConstants.TEST_CONF); + rateLimiterFactory = Mockito.mockStatic(RateLimiter.class, Mockito.CALLS_REAL_METHODS); + rateLimiterFactory.when(() -> RateLimiter.create(Mockito.anyDouble())) + .thenAnswer(invocation -> FakeTimeRateLimiter.create(invocation.getArgument(0))); } - @AfterClass - public static void tearDown() { - Args.clearParam(); + @After + public void tearDown() { + try { + if (rateLimiterFactory != null) { + rateLimiterFactory.close(); + } + } finally { + Args.clearParam(); + } } /** @@ -184,7 +196,8 @@ public void testQpsRateLimiterAdapter() throws Exception { .parseDouble(ReflectUtils.getFieldValue(strategy.getMapParams().get("qps"), "value").toString()), 0.0); - Thread.sleep(1000); + ReflectUtils.setFieldValue(strategy, "rateLimiter", + FakeTimeRateLimiter.createWithStoredPermit(1)); boolean flag = strategy.tryAcquire(); Assert.assertTrue(flag); @@ -201,4 +214,3 @@ public void testQpsRateLimiterAdapter() throws Exception { } } - diff --git a/framework/src/test/java/org/tron/core/vm/OperationRegistryTest.java b/framework/src/test/java/org/tron/core/vm/OperationRegistryTest.java index b6568f8a862..01da9daa508 100644 --- a/framework/src/test/java/org/tron/core/vm/OperationRegistryTest.java +++ b/framework/src/test/java/org/tron/core/vm/OperationRegistryTest.java @@ -3,11 +3,16 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; +import org.junit.Rule; import org.junit.Test; +import org.tron.common.VMConfigRule; import org.tron.core.vm.config.VMConfig; public class OperationRegistryTest { + @Rule + public final VMConfigRule vmConfigRule = new VMConfigRule(); + @Test public void constantAndTransactionExecutionsUseDedicatedTables() { JumpTable transactionTable = OperationRegistry.prepareAndGetTable(false); diff --git a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java index b471aeb2e42..5252ee1660f 100644 --- a/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/LibrustzcashTest.java @@ -17,12 +17,14 @@ import static org.tron.common.zksnark.JLibsodium.CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES; import com.google.protobuf.ByteString; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Optional; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.stream.LongStream; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.BeforeClass; @@ -30,6 +32,7 @@ import org.junit.Test; import org.tron.common.BaseTest; import org.tron.common.TestConstants; +import org.tron.common.math.StrictMathWrapper; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; import org.tron.common.zksnark.IncrementalMerkleTreeContainer; @@ -275,28 +278,25 @@ public void calBenchmarkSpendConcurrent() throws Exception { int count = 2; - CountDownLatch countDownLatch = new CountDownLatch(count); - int availableProcessors = Runtime.getRuntime().availableProcessors(); logger.info("availableProcessors:" + availableProcessors); - - ExecutorService generatePool = - Executors.newFixedThreadPool( - availableProcessors, - r -> new Thread(r, "generate-transaction")); - + ExecutorService generatePool = Executors.newFixedThreadPool( + StrictMathWrapper.min(count, availableProcessors), + r -> new Thread(r, "generate-transaction")); long startGenerate = System.currentTimeMillis(); - LongStream.range(0L, count).forEach(l -> generatePool.execute(() -> { - try { - benchmarkCreateSpend(); - } catch (Exception ex) { - ex.printStackTrace(); - logger.error("", ex); + try { + List> results = new ArrayList<>(); + for (int i = 0; i < count; i++) { + results.add(generatePool.submit(this::benchmarkCreateSpend)); } - })); - - countDownLatch.await(); - generatePool.shutdown(); + for (Future result : results) { + result.get(60, TimeUnit.SECONDS); + } + } finally { + generatePool.shutdownNow(); + assertTrue("Benchmark workers did not terminate", + generatePool.awaitTermination(5, TimeUnit.SECONDS)); + } logger.info("generate cost time:" + (System.currentTimeMillis() - startGenerate)); } diff --git a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java index 5854b731e97..1252029f467 100755 --- a/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java +++ b/framework/src/test/java/org/tron/core/zksnark/ShieldedReceiveTest.java @@ -8,7 +8,6 @@ import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import java.lang.reflect.Field; import java.security.SignatureException; import java.util.Arrays; import java.util.HashSet; @@ -171,6 +170,9 @@ public void init() { return; } consensusService.start(); + // Initialize consensus metadata, but keep block production under test control. + // A background block would reset the pending session and discard synthetic Merkle roots. + dposTask.stop(); chainBaseManager.getDynamicPropertiesStore().saveTotalShieldedPoolValue(10_000_000_000L); init = true; } @@ -2538,12 +2540,9 @@ public void pushSameSkAndScanAndSpend() throws Exception { boolean ok2 = dbManager.pushTransaction(transactionCap2); Assert.assertTrue(ok2); } finally { - // DposTask.init() does not reset isRunning (it stays false after stop()), so force it back - // to true via reflection before restarting. - Field isRunning = DposTask.class.getDeclaredField("isRunning"); - isRunning.setAccessible(true); - isRunning.set(dposTask, true); + // Restore consensus metadata without restarting background block production. consensusService.start(); + dposTask.stop(); } } diff --git a/framework/src/test/java/org/tron/program/SolidityNodeTest.java b/framework/src/test/java/org/tron/program/SolidityNodeTest.java index ade00374bc4..84b31ab4021 100755 --- a/framework/src/test/java/org/tron/program/SolidityNodeTest.java +++ b/framework/src/test/java/org/tron/program/SolidityNodeTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import com.google.common.util.concurrent.Uninterruptibles; import com.google.protobuf.ByteString; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -15,6 +16,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; @@ -79,41 +81,53 @@ private void setFlag(boolean value) throws Exception { // ── gRPC / HTTP service integration ────────────────────────────────────────── @Test - public void testSolidityGrpcCall() { - rpcApiService.start(); - DatabaseGrpcClient databaseGrpcClient = null; - String address = Args.getInstance().getTrustNodeAddr().split(":")[0] + ":" + rpcPort; + public void testSolidityGrpcCall() throws Exception { + DatabaseGrpcClient client = null; + DatabaseGrpcClient addressedClient = null; try { - databaseGrpcClient = new DatabaseGrpcClient(address); - } catch (Exception e) { - logger.error("Failed to create database grpc client {}", address); + Assert.assertTrue(rpcApiService.start().get(5, TimeUnit.SECONDS)); + String address = "127.0.0.1:" + rpcPort; + client = new DatabaseGrpcClient(address); + Assert.assertNotNull(client.getDynamicProperties()); + Block genesisBlock = client.getBlock(0); + Assert.assertNotNull(genesisBlock); + Assert.assertFalse(genesisBlock.getTransactionsList().isEmpty()); + Assert.assertNotNull(client.getBlock(-1)); + addressedClient = new DatabaseGrpcClient("127.0.0.1", rpcPort); + Assert.assertNotNull(addressedClient.getDynamicProperties()); + } finally { + try { + shutdownDatabaseClient(client); + } finally { + try { + shutdownDatabaseClient(addressedClient); + } finally { + Assert.assertTrue(rpcApiService.stop().get(5, TimeUnit.SECONDS)); + } + } } + } - Assert.assertNotNull(databaseGrpcClient); - DynamicProperties dynamicProperties = databaseGrpcClient.getDynamicProperties(); - Assert.assertNotNull(dynamicProperties); - - Block genesisBlock = databaseGrpcClient.getBlock(0); - Assert.assertNotNull(genesisBlock); - Assert.assertFalse(genesisBlock.getTransactionsList().isEmpty()); - Block invalidBlock = databaseGrpcClient.getBlock(-1); - Assert.assertNotNull(invalidBlock); - try { - databaseGrpcClient = new DatabaseGrpcClient(address, -1); - } catch (Exception e) { - logger.error("Failed to create database grpc client {}", address); + private void shutdownDatabaseClient(DatabaseGrpcClient client) throws Exception { + if (client != null) { + client.shutdown(); + Field channel = DatabaseGrpcClient.class.getDeclaredField("channel"); + channel.setAccessible(true); + Assert.assertTrue("Database channel did not terminate", + ((io.grpc.ManagedChannel) channel.get(client)).awaitTermination(5, TimeUnit.SECONDS)); } - databaseGrpcClient.shutdown(); - rpcApiService.stop(); } @Test - public void testSolidityNodeHttpApiService() { - solidityNodeHttpApiService.start(); - // start again - solidityNodeHttpApiService.start(); - solidityNodeHttpApiService.stop(); - Assert.assertTrue(true); + public void testSolidityNodeHttpApiService() throws Exception { + // HttpService creates a new Jetty server on start; stop each instance before restarting. + for (int i = 0; i < 2; i++) { + try { + Assert.assertTrue(solidityNodeHttpApiService.start().get(5, TimeUnit.SECONDS)); + } finally { + Assert.assertTrue(solidityNodeHttpApiService.stop().get(5, TimeUnit.SECONDS)); + } + } } // ── lifecycle ───────────────────────────────────────────────────────────────── @@ -330,45 +344,28 @@ public void testGetBlockByNumWhenClosed() throws Exception { } } - /** - * getBlockByNum() must break immediately — without a 1-second sleep — when a - * gRPC exception is thrown while flag races to false (the P3 shutdown-race fix). - * The invocation time is measured directly so the assertion is independent of - * Spring-context startup overhead. - */ + /** Verify shutdown skips the retry sleep without relying on CI scheduling latency. */ @Test(timeout = 5000) public void testGetBlockByNumNoErrorOnExceptionDuringShutdown() throws Exception { - Method m = SolidityNode.class.getDeclaredMethod("getBlockByNum", long.class); - m.setAccessible(true); - Field clientField = getField("databaseGrpcClient"); - Object origClient = clientField.get(solidityNode); - setFlag(true); // precondition: while(flag) must be entered; do not rely on test-ordering - try { - DatabaseGrpcClient mockClient = mock(DatabaseGrpcClient.class); - // flag races to false inside the gRPC call — exact close() race - Mockito.when(mockClient.getBlock(42L)).thenAnswer(inv -> { - setFlag(false); - throw new RuntimeException("channel closed during shutdown"); - }); - clientField.set(solidityNode, mockClient); - - long start = System.currentTimeMillis(); - InvocationTargetException t = assertThrows(InvocationTargetException.class, () -> { - m.invoke(solidityNode, 42L); - }); - assertTrue(t.getCause() instanceof RuntimeException); - assertEquals("SolidityNode is closing.", t.getCause().getMessage()); - long elapsed = System.currentTimeMillis() - start; - // Without the fix the catch sleeps exceptionSleepTime (1000 ms) before - // re-checking the while condition. With the fix it breaks immediately. - assertTrue("Expected break without sleep (<500 ms), got " + elapsed + " ms", - elapsed < 500); - // No retry: exactly one gRPC call must be made. - Mockito.verify(mockClient, Mockito.times(1)).getBlock(42L); - } finally { - setFlag(true); - clientField.set(solidityNode, origClient); - } + SolidityNode observed = Mockito.spy(solidityNode); + Field flag = getField("flag"); + flag.set(observed, true); + DatabaseGrpcClient client = mock(DatabaseGrpcClient.class); + Mockito.when(client.getBlock(42L)).thenAnswer(invocation -> { + flag.set(observed, false); + throw new RuntimeException("channel closed during shutdown"); + }); + getField("databaseGrpcClient").set(observed, client); + Mockito.doNothing().when(observed).sleep(Mockito.anyLong()); + Method method = SolidityNode.class.getDeclaredMethod("getBlockByNum", long.class); + method.setAccessible(true); + + InvocationTargetException failure = assertThrows(InvocationTargetException.class, + () -> method.invoke(observed, 42L)); + assertTrue(failure.getCause() instanceof RuntimeException); + assertEquals("SolidityNode is closing.", failure.getCause().getMessage()); + Mockito.verify(observed, Mockito.never()).sleep(Mockito.anyLong()); + Mockito.verify(client).getBlock(42L); } // ── getLastSolidityBlockNum() ───────────────────────────────────────────────── @@ -555,6 +552,7 @@ public void testGetBlockProcessesOneBlock() throws Exception { @Test(timeout = 8000) @SuppressWarnings("unchecked") public void testGetBlockShutdownPaths() throws Exception { + boolean origFlag = getFlag(); long origID = atomicLong("ID").get(); long origRemote = atomicLong("remoteBlockNum").get(); Field clientField = getField("databaseGrpcClient"); @@ -564,6 +562,7 @@ public void testGetBlockShutdownPaths() throws Exception { LinkedBlockingDeque queue = (LinkedBlockingDeque) getField("blockQueue").get(solidityNode); + Thread worker = null; try { // ── Part 1: interrupt during blockQueue.put() ────────────────────────── // Fill the queue to capacity so the next put() call blocks. @@ -584,18 +583,20 @@ public void testGetBlockShutdownPaths() throws Exception { Method getBlockM = SolidityNode.class.getDeclaredMethod("getBlock"); getBlockM.setAccessible(true); - Thread t = new Thread(() -> { + AtomicReference workerFailure = new AtomicReference<>(); + worker = new Thread(() -> { try { getBlockM.invoke(solidityNode); } catch (Exception e) { - Thread.currentThread().interrupt(); + workerFailure.set(e); } }); - t.start(); + worker.start(); Thread.sleep(200); // let the thread block inside blockQueue.put() - t.interrupt(); // simulate ExecutorService.shutdownNow() - t.join(4000); - assertFalse("getBlock must exit cleanly when interrupted during put()", t.isAlive()); + worker.interrupt(); // simulate ExecutorService.shutdownNow() + worker.join(4000); + assertFalse("getBlock must exit cleanly when interrupted during put()", worker.isAlive()); + Assert.assertNull("getBlock worker failed", workerFailure.get()); queue.clear(); setFlag(true); @@ -614,7 +615,8 @@ public void testGetBlockShutdownPaths() throws Exception { // Must return without throwing and without infinite retry. getBlockM.invoke(solidityNode); } finally { - setFlag(true); + stopWorker(worker); + setFlag(origFlag); queue.clear(); atomicLong("ID").set(origID); atomicLong("remoteBlockNum").set(origRemote); @@ -670,36 +672,50 @@ public void testProcessSolidityBlockProcessesQueuedBlock() throws Exception { */ @Test(timeout = 8000) public void testProcessSolidityBlockHandlesInterrupt() throws Exception { + boolean origFlag = getFlag(); TronNetDelegate mockDelegate = mock(TronNetDelegate.class); Mockito.when(mockDelegate.isHitDown()).thenReturn(false); Field delegateField = getField("tronNetDelegate"); Object origDelegate = delegateField.get(solidityNode); - delegateField.set(solidityNode, mockDelegate); Method m = SolidityNode.class.getDeclaredMethod("processSolidityBlock"); m.setAccessible(true); + AtomicReference workerFailure = new AtomicReference<>(); Thread t = new Thread(() -> { try { m.invoke(solidityNode); - } catch (Exception ignored) { - // InvocationTargetException should not happen; the method handles interrupt internally + } catch (Exception e) { + workerFailure.set(e); } }); try { + delegateField.set(solidityNode, mockDelegate); t.start(); Thread.sleep(150); // let the thread enter blockQueue.poll(1000 ms) t.interrupt(); t.join(5000); assertFalse("processSolidityBlock must exit after interrupt", t.isAlive()); + Assert.assertNull("processSolidityBlock worker failed", workerFailure.get()); } finally { - setFlag(true); + stopWorker(t); + setFlag(origFlag); delegateField.set(solidityNode, origDelegate); } } // ── private helpers ────────────────────────────────────────────────────────── + private void stopWorker(Thread worker) throws Exception { + // A timeout may interrupt the test thread before it reaches the normal shutdown path. + setFlag(false); + if (worker != null) { + worker.interrupt(); + Uninterruptibles.joinUninterruptibly(worker, 5, TimeUnit.SECONDS); + assertFalse("Test worker must stop before restoring shared state", worker.isAlive()); + } + } + private static Field getField(String name) throws Exception { Field f = SolidityNode.class.getDeclaredField(name); f.setAccessible(true); diff --git a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java index 4ee7567ec28..7447e30952f 100644 --- a/plugins/src/test/java/org/tron/plugins/DbLiteTest.java +++ b/plugins/src/test/java/org/tron/plugins/DbLiteTest.java @@ -15,6 +15,7 @@ import org.junit.Rule; import org.junit.rules.TemporaryFolder; import org.tron.api.WalletGrpc; +import org.tron.common.ClassLevelAppContextFixture; import org.tron.common.TestConstants; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; @@ -65,10 +66,16 @@ public void startApp() { * shutdown the fullNode. */ public void shutdown() throws InterruptedException { - if (channelFull != null) { - channelFull.shutdownNow(); + try { + ClassLevelAppContextFixture.shutdownChannel(channelFull); + } finally { + channelFull = null; + blockingStubFull = null; + if (context != null) { + context.close(); + context = null; + } } - context.close(); } public void init(String dbType, boolean historyBalanceLookup) throws IOException { @@ -85,8 +92,13 @@ public void init(String dbType, boolean historyBalanceLookup) throws IOException } @After - public void clear() { - Args.clearParam(); + public void clear() throws InterruptedException { + try { + shutdown(); + } finally { + DbLite.reSetRecentBlks(); + Args.clearParam(); + } } public void testTools(String dbType, int checkpointVersion) diff --git a/plugins/src/test/java/org/tron/plugins/KeystoreUpdateTest.java b/plugins/src/test/java/org/tron/plugins/KeystoreUpdateTest.java index ed8f81acd32..64d8826e5d3 100644 --- a/plugins/src/test/java/org/tron/plugins/KeystoreUpdateTest.java +++ b/plugins/src/test/java/org/tron/plugins/KeystoreUpdateTest.java @@ -412,10 +412,8 @@ public void testUpdateMultipleKeystoresSameAddress() throws Exception { String address = Credentials.create(keyPair).getAddress(); // Create two keystores for the same address via direct API - WalletUtils.generateWalletFile(password, keyPair, dir, true); - // Small delay to get different filename timestamps - Thread.sleep(50); - WalletUtils.generateWalletFile(password, keyPair, dir, true); + String first = WalletUtils.generateWalletFile(password, keyPair, dir, true); + Files.copy(new File(dir, first).toPath(), new File(dir, "duplicate.json").toPath()); File pwFile = tempFolder.newFile("pw-multi.txt"); Files.write(pwFile.toPath(),