From b1dab0e50adb5df780a14ae3fb8f08d0136ae343 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Fri, 5 Jan 2024 20:27:06 +1000 Subject: [PATCH] TestWatcher junit5 (#6339) * TestWatcher junit5 * add test class and method name to context * moved the testwatcher junit5 function to a new junit5 superclass * one qbft test to junit5 superclass Signed-off-by: Sally MacFarlane --------- Signed-off-by: Sally MacFarlane Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> --- acceptance-tests/dsl/build.gradle | 1 + .../acceptance/dsl/AcceptanceTestBase.java | 57 +---- .../dsl/AcceptanceTestBaseJunit5.java | 200 ++++++++++++++++++ .../dsl/AcceptanceTestBaseTestWatcher.java | 54 +++++ .../bft/qbft/QbftContractAcceptanceTest.java | 6 +- .../plugins/BadCLIOptionsPluginTest.java | 4 +- .../plugins/BesuEventsPluginTest.java | 4 +- .../plugins/PermissioningPluginTest.java | 4 +- 8 files changed, 270 insertions(+), 60 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseJunit5.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseTestWatcher.java diff --git a/acceptance-tests/dsl/build.gradle b/acceptance-tests/dsl/build.gradle index 7aff9aaffb..96a6a1c1bc 100644 --- a/acceptance-tests/dsl/build.gradle +++ b/acceptance-tests/dsl/build.gradle @@ -47,4 +47,5 @@ dependencies { implementation 'org.web3j:crypto' implementation 'org.testcontainers:testcontainers' + implementation 'org.junit.jupiter:junit-jupiter' } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java index 617e0e534c..cac4deb9d9 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java @@ -49,7 +49,6 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransa import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.ProcessBuilder.Redirect; @@ -58,14 +57,15 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.After; -import org.junit.Rule; -import org.junit.rules.TestName; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; +import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; +/** + * Superclass for acceptance tests. For now (transition to junit5 is ongoing) this class supports + * junit4 format. + */ +@ExtendWith(AcceptanceTestBaseTestWatcher.class) public class AcceptanceTestBase { private static final Logger LOG = LoggerFactory.getLogger(AcceptanceTestBase.class); @@ -131,8 +131,6 @@ public class AcceptanceTestBase { exitedSuccessfully = new ExitedWithCode(0); } - @Rule public final TestName name = new TestName(); - @After public void tearDownAcceptanceTestBase() { reportMemory(); @@ -178,49 +176,6 @@ public class AcceptanceTestBase { } } - @Rule - public TestWatcher logEraser = - new TestWatcher() { - - @Override - protected void starting(final Description description) { - MDC.put("test", description.getMethodName()); - MDC.put("class", description.getClassName()); - - final String errorMessage = "Uncaught exception in thread \"{}\""; - Thread.currentThread() - .setUncaughtExceptionHandler( - (thread, error) -> LOG.error(errorMessage, thread.getName(), error)); - Thread.setDefaultUncaughtExceptionHandler( - (thread, error) -> LOG.error(errorMessage, thread.getName(), error)); - } - - @Override - protected void failed(final Throwable e, final Description description) { - // add the result at the end of the log so it is self-sufficient - LOG.error( - "=========================================================================================="); - LOG.error("Test failed. Reported Throwable at the point of failure:", e); - LOG.error(e.getMessage()); - } - - @Override - protected void succeeded(final Description description) { - // if so configured, delete logs of successful tests - if (!Boolean.getBoolean("acctests.keepLogsOfPassingTests")) { - String pathname = - "build/acceptanceTestLogs/" - + description.getClassName() - + "." - + description.getMethodName() - + ".log"; - LOG.info("Test successful, deleting log at {}", pathname); - File file = new File(pathname); - file.delete(); - } - } - }; - protected void waitForBlockHeight(final Node node, final long blockchainHeight) { WaitUtils.waitFor( 120, diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseJunit5.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseJunit5.java new file mode 100644 index 0000000000..39b45d6e32 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseJunit5.java @@ -0,0 +1,200 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.besu.tests.acceptance.dsl; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; +import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Blockchain; +import org.hyperledger.besu.tests.acceptance.dsl.condition.admin.AdminConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.bft.BftConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.clique.CliqueConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.eth.EthConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.login.LoginConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.perm.PermissioningConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.priv.PrivConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.process.ExitedWithCode; +import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.web3.Web3Conditions; +import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; +import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeFactory; +import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.permissioning.PermissionedNodeBuilder; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.AccountTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.admin.AdminTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.bft.BftTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.clique.CliqueTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.contract.ContractTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.logging.log4j.ThreadContext; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Superclass for acceptance tests. For now (transition to junit5 is ongoing) this class supports + * junit5 format. Once the transition is complete, this class can be removed and recombined with + * AcceptanceTestBase (original). + */ +@ExtendWith(AcceptanceTestBaseTestWatcher.class) +public class AcceptanceTestBaseJunit5 { + + private static final Logger LOG = LoggerFactory.getLogger(AcceptanceTestBaseJunit5.class); + + protected final Accounts accounts; + protected final AccountTransactions accountTransactions; + protected final AdminConditions admin; + protected final AdminTransactions adminTransactions; + protected final Blockchain blockchain; + protected final CliqueConditions clique; + protected final CliqueTransactions cliqueTransactions; + protected final Cluster cluster; + protected final ContractVerifier contractVerifier; + protected final ContractTransactions contractTransactions; + protected final EthConditions eth; + protected final EthTransactions ethTransactions; + protected final BftTransactions bftTransactions; + protected final BftConditions bft; + protected final LoginConditions login; + protected final NetConditions net; + protected final BesuNodeFactory besu; + protected final PermissioningConditions perm; + protected final PermissionedNodeBuilder permissionedNodeBuilder; + protected final PermissioningTransactions permissioningTransactions; + protected final MinerTransactions minerTransactions; + protected final Web3Conditions web3; + protected final PrivConditions priv; + protected final PrivacyTransactions privacyTransactions; + protected final TxPoolConditions txPoolConditions; + protected final TxPoolTransactions txPoolTransactions; + protected final ExitedWithCode exitedSuccessfully; + + private final ExecutorService outputProcessorExecutor = Executors.newCachedThreadPool(); + + protected AcceptanceTestBaseJunit5() { + ethTransactions = new EthTransactions(); + accounts = new Accounts(ethTransactions); + adminTransactions = new AdminTransactions(); + cliqueTransactions = new CliqueTransactions(); + bftTransactions = new BftTransactions(); + accountTransactions = new AccountTransactions(accounts); + permissioningTransactions = new PermissioningTransactions(); + privacyTransactions = new PrivacyTransactions(); + contractTransactions = new ContractTransactions(); + minerTransactions = new MinerTransactions(); + + blockchain = new Blockchain(ethTransactions); + clique = new CliqueConditions(ethTransactions, cliqueTransactions); + eth = new EthConditions(ethTransactions); + bft = new BftConditions(bftTransactions); + login = new LoginConditions(); + net = new NetConditions(new NetTransactions()); + cluster = new Cluster(net); + perm = new PermissioningConditions(permissioningTransactions); + priv = new PrivConditions(privacyTransactions); + admin = new AdminConditions(adminTransactions); + web3 = new Web3Conditions(new Web3Transactions()); + besu = new BesuNodeFactory(); + txPoolTransactions = new TxPoolTransactions(); + txPoolConditions = new TxPoolConditions(txPoolTransactions); + contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor()); + permissionedNodeBuilder = new PermissionedNodeBuilder(); + exitedSuccessfully = new ExitedWithCode(0); + } + + @BeforeEach + public void setUp(final TestInfo testInfo) { + // log4j is configured to create a file per test + // build/acceptanceTestLogs/${ctx:class}.${ctx:test}.log + ThreadContext.put("class", this.getClass().getSimpleName()); + ThreadContext.put("test", testInfo.getTestMethod().get().getName()); + } + + @AfterEach + public void tearDownAcceptanceTestBase() { + reportMemory(); + cluster.close(); + } + + public void reportMemory() { + String os = System.getProperty("os.name"); + String[] command = null; + if (os.contains("Linux")) { + command = new String[] {"/usr/bin/top", "-n", "1", "-o", "%MEM", "-b", "-c", "-w", "180"}; + } + if (os.contains("Mac")) { + command = new String[] {"/usr/bin/top", "-l", "1", "-o", "mem", "-n", "20"}; + } + if (command != null) { + LOG.info("Memory usage at end of test:"); + final ProcessBuilder processBuilder = + new ProcessBuilder(command) + .redirectErrorStream(true) + .redirectInput(ProcessBuilder.Redirect.INHERIT); + try { + final Process memInfoProcess = processBuilder.start(); + outputProcessorExecutor.execute(() -> printOutput(memInfoProcess)); + memInfoProcess.waitFor(); + LOG.debug("Memory info process exited with code {}", memInfoProcess.exitValue()); + } catch (final Exception e) { + LOG.warn("Error running memory information process", e); + } + } else { + LOG.info("Don't know how to report memory for OS {}", os); + } + } + + private void printOutput(final Process process) { + try (final BufferedReader in = + new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8))) { + String line = in.readLine(); + while (line != null) { + LOG.info(line); + line = in.readLine(); + } + } catch (final IOException e) { + LOG.warn("Failed to read output from memory information process: ", e); + } + } + + protected void waitForBlockHeight(final Node node, final long blockchainHeight) { + WaitUtils.waitFor( + 120, + () -> + assertThat(node.execute(ethTransactions.blockNumber())) + .isGreaterThanOrEqualTo(BigInteger.valueOf(blockchainHeight))); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseTestWatcher.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseTestWatcher.java new file mode 100644 index 0000000000..21c4fe8fa9 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBaseTestWatcher.java @@ -0,0 +1,54 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.besu.tests.acceptance.dsl; + +import java.io.File; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestWatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AcceptanceTestBaseTestWatcher implements TestWatcher { + private static final Logger LOG = LoggerFactory.getLogger(AcceptanceTestBaseTestWatcher.class); + + @Override + public void testFailed(final ExtensionContext extensionContext, final Throwable e) { + // add the result at the end of the log, so it is self-sufficient + LOG.error( + "=========================================================================================="); + LOG.error("Test failed. Reported Throwable at the point of failure:", e); + LOG.error(e.getMessage()); + } + + @Override + public void testSuccessful(final ExtensionContext extensionContext) { + // if so configured, delete logs of successful tests + if (!Boolean.getBoolean("acctests.keepLogsOfPassingTests")) { + // log4j is configured to create a file per test + // build/acceptanceTestLogs/${ctx:class}.${ctx:test}.log + String pathname = + "build/acceptanceTestLogs/" + + extensionContext.getTestClass().get().getSimpleName() + + "." + + extensionContext.getTestMethod().get().getName() + + ".log"; + LOG.info("Test successful, deleting log at {}", pathname); + File file = new File(pathname); + file.delete(); + } + } +} diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/qbft/QbftContractAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/qbft/QbftContractAcceptanceTest.java index e3b2878b05..51500bb149 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/qbft/QbftContractAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/bft/qbft/QbftContractAcceptanceTest.java @@ -14,13 +14,13 @@ */ package org.hyperledger.besu.tests.acceptance.bft.qbft; -import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; -import org.junit.Test; +import org.junit.jupiter.api.Test; -public class QbftContractAcceptanceTest extends AcceptanceTestBase { +public class QbftContractAcceptanceTest extends AcceptanceTestBaseJunit5 { @Test public void shouldMineOnMultipleNodesEvenWhenClusterContainsNonValidator() throws Exception { diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BadCLIOptionsPluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BadCLIOptionsPluginTest.java index 02299f4fdb..9cee5dde6e 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BadCLIOptionsPluginTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BadCLIOptionsPluginTest.java @@ -17,7 +17,7 @@ package org.hyperledger.besu.tests.acceptance.plugins; import static org.assertj.core.api.Assertions.assertThat; -import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import java.io.File; @@ -33,7 +33,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -public class BadCLIOptionsPluginTest extends AcceptanceTestBase { +public class BadCLIOptionsPluginTest extends AcceptanceTestBaseJunit5 { private BesuNode node; @BeforeEach diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BesuEventsPluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BesuEventsPluginTest.java index 4906193362..a35309ef6f 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BesuEventsPluginTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/BesuEventsPluginTest.java @@ -14,7 +14,7 @@ */ package org.hyperledger.besu.tests.acceptance.plugins; -import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import java.io.File; @@ -28,7 +28,7 @@ import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class BesuEventsPluginTest extends AcceptanceTestBase { +public class BesuEventsPluginTest extends AcceptanceTestBaseJunit5 { private BesuNode pluginNode; private BesuNode minerNode; diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java index af6a3f7a16..146110237d 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/plugins/PermissioningPluginTest.java @@ -16,7 +16,7 @@ package org.hyperledger.besu.tests.acceptance.plugins; import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBaseJunit5; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; @@ -28,7 +28,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class PermissioningPluginTest extends AcceptanceTestBase { +public class PermissioningPluginTest extends AcceptanceTestBaseJunit5 { private BesuNode minerNode; private BesuNode aliceNode;