This module is where we store all of the tests that use the testing harness. This is not the place to test out the harness itself - that's what the TestHarness
module is for!
node_test_harness supports several node types, which represent the Aion implementation being tested. The node type must be specified when running the tests by passing in Java system property testNodes
. For instance, if running with Gradle, to test against a Java kernel, the invocation is ./gradlew Tests:test -PtestNodes=java
The tests expect that a kernel be at a particular location (dependent on node type being tested), so this must be set up before running the tests. For instance, for testing against Java, the expected kernel location is Tests/aion within the node_test_harness cloned repo.
For details on node types and configuring non-Java node types, see Node type configuration
The tests in this module have the ability to be incorporated into a test suite that is run concurrently against a single node instance. This allows us to not only gain a huge speed-up in terms of how long it takes for all of the tests to run, but it also allows for our individual functional tests to come together and serve as a stress test of the kernel, by flooding it with transactions simultaneously.
All tests should be written for concurrent testing unless there is a very good reason why they do not fit into the concurrent model!
In general, ./gradlew :Tests:test -PtestNodes=<nodetype>
is how you should be running tests. For Java kernel, use java
as . This will use the concurrent runner and execute the tests against the Java kernel; for changing the kernel-under-test, see Node type configuration.
To run the saturation tests: ./gradlew :Tests:test -Psaturation
To run the kernel-side (unsigned) saturation tests: ./gradlew :Tests:test -PunsignedSaturation
Every test defined in your class will be run on a unique instance of the class. This means if your test only accesses instance methods and fields then no synchronization is required on your part. If your tests access static other external resources, then you will need to synchronize them appropriately.
Your tests never touch the node! All node management and life-cycling is taken care of behind the scenes by the runner. The node is started up once before any tests are run, and shut down once all tests have been run, so that every test in the concurrent suite is interacting with the same node instance.
The following two JUnit Rules are provided: PreminedAccount
should be used as the only safe means of interacting with a premined account; LocalNodeListener
is used to listen for events on the node.
Your tests must use these rules! Since you have no access to the node itself, you cannot start up your own listener, and if you try and use the pre-mined account defined in the genesis block then you will introduce race conditions and concurrency issues into the system! These rules are here to simplify things for you and delegate concurrency concerns elsewhere.
Step 1. Write your tests like typical JUnit tests, using the two provided Rules.
Step 2. Annotate your test class with the following annotation: @RunWith(SequentialRunner.class)
. This allows your tests to be run sequentially (since you now depend on external node lifecycling, Rules, etc. for your test to run, you require a JUnit Runner that knows how to do this stuff).
Step 3. Open the ConcurrentSuite.java
class and add your test class into the list of SuiteClasses
. This allows your test to be run concurrently.
If you are running your tests from an IDE, then to run the tests concurrently simply run the ConcurrentSuite.java
class as a test class. By default, the log files will be deleted when the tests finish. To prevent this from happening, set the System Property skipCleanLogs=true
.
If you are running your tests from the command line, then navigate to the root directory of the node harness project and run ./gradlew :Tests:test
. By default, the log files will be deleted when the tests finish. To prevent this from happening, run ./gradlew :Tests:test -PskipCleanLogs=true
.
These log files are your best means of debugging if you run into problems. You can find the logs generated by a test in the logs
directory in the Tests
module. Do not modify these logs while tests are running!
If there is a very good reason why the tests you are writing cannot fit into the concurrent test suite model, then it is still recommended that you use the provided SequentialRunner
custom JUnit runner. The runner takes the burden of node lifecycle management off you, and opens this test up to easily being integrated into the concurrent model in the future if this ever becomes a possibility.
If the SequentialRunner
does not meet your needs for some reason then write your tests as regular JUnit tests.
Otherwise, write your tests as regular JUnit tests but use the provided Rule PreminedAccount
to interact with your unique premined account, and LocalNodeListener
to listen to the log messages of the node. The runner will start up and shut down the node for you, your tests should not ever need to touch an instance of the node itself.
Finally, annotate your test class with: @RunWith(SequentialRunner.class)
to enable the sequential runner. That's all.
If you are running your tests from an IDE, then to run the test sequentially simply run your test class regularly. Note that the sequential runner will run every test in the class every time, there is no way to single out a specific test to run unless you @Ignore
the other tests.
If you are running your tests from the command line, then navigate to the root directory of the node harness project and run ./gradlew :Tests:test -Psequential
. By default, the log files will be deleted when the tests finish. To prevent this from happening, run ./gradlew :Tests:test -Psequential -PskipCleanLogs=true
.
These log files are your best means of debugging if you run into problems. You can find the logs generated by a test in the logs
directory in the Tests
module. Do not modify these logs while tests are running!
By default, running tests sequentially will cause the test to be run twice -- once with Java kernel, then a second time with Rust kernel. This behaviour can be overridden (for invocation from IDE or Gradle) by adding the JVM system property testNodes
. Value values are: rust
, java
, or rust,java
. Example Gradle invocation: ./gradlew :Tests:test -Psequential -PskipCleanLogs=true -PtestNodes=java
.
Node type determines the Aion implementation that the tests will be executed against. By default, node type is Java, but can be overridden using the system property testNodes
. If running tests from IDE, set the system property in your JUnit configuration. If running tests via Gradle, set the property using the -P
argument; i.e. ./gradlew Tests:test -PtestNodes=java
. Multiple node types can be provided, i.e. ./gradlew Tests:test -PtestNodes=java,rust
.
Supported node types:
Node type name | Description |
---|---|
java | Tests implementation of Java kernel |
rust | Tests implementation of Rust kernel |
proxy | A special node type used for testing a network of kernels. The test harness communicates with a Java kernel that performs no mining; instead, that kernel acts as a proxy by peering with kernel(s) in a network, which processes the transactions. |
Each node type expects the kernel-under-test to be placed in a directory within the test harness. Note that the test harness will overwrite config files in these directories.
- for java, extract the release to
node_test_harness/Tests/aion
(so thataion.sh
is present in that directory) - for rust, extract the release to
node_test_harness/Tests/aionr
(so thataion
is present in that directory) - for proxy, extract a release of Java kernel to
node_test_harness/Tests/aionproxy
(so thataion.sh
is present in that directory)
For proxy, there is an additional set-up step to configure peering. Modify the file node_test_harness/Tests/test_resources/proxy_java_custom/config.xml
and add at least one peer from the network-under-test to the p2p nodes list.