Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API test harness for Linux & OSX #4366

Merged
merged 88 commits into from Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from 86 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
88b5e9d
Add configuration support
ghubstan Jul 9, 2020
e0c27ee
Add main resource files
ghubstan Jul 9, 2020
77b6878
Add script to get Bisq app pid
ghubstan Jul 9, 2020
c0c75e2
Support starting bitcoin & bisq apps on Linux
ghubstan Jul 9, 2020
ae3b263
Add :apitest main driver, setup task & dummy tests
ghubstan Jul 9, 2020
e9baebc
Add build tasks for installing dao-setup files
ghubstan Jul 9, 2020
2cd80aa
Add subproject :apitest to gradle build file
ghubstan Jul 9, 2020
09929c8
Fix codacy problems
ghubstan Jul 9, 2020
efbaa5b
Fix codacy problems
ghubstan Jul 9, 2020
05d0ce0
Fix codacy problem
ghubstan Jul 9, 2020
ca378cd
Fix error msg spacing
ghubstan Jul 10, 2020
798fde8
Fix error msgs
ghubstan Jul 10, 2020
e61a42d
Remove MaxRAM from DEFAULT_JVM_OPTS
ghubstan Jul 10, 2020
898219a
Assume bitcoin-core is statically linked to berkeley-db
ghubstan Jul 11, 2020
a1e2536
Fix hanging background process problem
ghubstan Jul 11, 2020
8519021
Add line break in front of port config
ghubstan Jul 12, 2020
87525ca
Move test setup scaffolding into new Scaffold class
ghubstan Jul 12, 2020
2c10836
Expose grpc service stubs
ghubstan Jul 12, 2020
ffe376e
Fix codacy problems
ghubstan Jul 12, 2020
390cba1
Fix codacy problem
ghubstan Jul 12, 2020
96cabfb
Support @Skip on test classes and methods
ghubstan Jul 13, 2020
0edcc3b
Add first method test cases
ghubstan Jul 13, 2020
81aeedd
Add MethodTestSuite
ghubstan Jul 13, 2020
8da4646
Update bats version test
ghubstan Jul 13, 2020
db5a685
Make init() method public
ghubstan Jul 13, 2020
65e3370
Add license note, format tearDown(), fix comment
ghubstan Jul 13, 2020
fae661c
Run MethodTestSuite
ghubstan Jul 13, 2020
458d2f3
Add license note
ghubstan Jul 13, 2020
4d5c767
Fix codacy problem
ghubstan Jul 13, 2020
35ff4e5
Delete commented statement
ghubstan Jul 13, 2020
d782e8d
Do not run dummy test from driver
ghubstan Jul 13, 2020
84976fe
Fix varible names
ghubstan Jul 13, 2020
2678b31
Remove test scaffolding logic from ApiTestConfig
ghubstan Jul 13, 2020
486f06b
Refresh dao-setup files in Scaffold setup
ghubstan Jul 13, 2020
e17480a
Remove hacked method tests
ghubstan Jul 13, 2020
080952b
Support @Order-ing of JUnit tests
ghubstan Jul 13, 2020
4f08ec3
Add first JUnit 'method' tests
ghubstan Jul 13, 2020
84af092
Add driver for running method tests
ghubstan Jul 14, 2020
1acf340
Get rid of references to removed @Skip annotation
ghubstan Jul 14, 2020
8ed44b8
Remove @Skip annotaion
ghubstan Jul 14, 2020
45c1a97
Fix codacy problem in bash script
ghubstan Jul 14, 2020
6b738f7
Replace config 'numSetupTasks' with 'supportingApps'
ghubstan Jul 14, 2020
498939a
Allow more time for background app shutdown
ghubstan Jul 14, 2020
5df0b1e
Refactor ApiTestCase class hierarchy
ghubstan Jul 14, 2020
2cf7915
Add wallet protect method tests
ghubstan Jul 14, 2020
28aefc5
Add tests for resetting a wallet password
ghubstan Jul 14, 2020
a00bc4b
Add --bisqAppInitTime=<Long> config option
ghubstan Jul 15, 2020
cf3b545
Fix hardcoded bitcoin.conf property values
ghubstan Jul 15, 2020
d108d89
Fix comment and code styling
ghubstan Jul 15, 2020
c19afeb
Fix 'bitcoind not found' error message
ghubstan Jul 15, 2020
b2417d3
Delete throws clause from method signature
ghubstan Jul 15, 2020
e0ea9db
Fix bitcoind startup error handling
ghubstan Jul 15, 2020
431cbe7
Bump bisqAppInitTime default back up to 5s
ghubstan Jul 15, 2020
4296d96
Remove all sudo related logic from the linux pkg
ghubstan Jul 16, 2020
7d664d9
Do not "killall bitcoind" processes
ghubstan Jul 16, 2020
6edab1a
Create convenient way to call bitcoin-cli from tests
ghubstan Jul 16, 2020
687bcf1
Add FundWalletScenarioTest
ghubstan Jul 16, 2020
2852e3d
Add JUnitHelper to run tests from JUnitCore
ghubstan Jul 16, 2020
5d7133a
Do not subtract fee from 'bitcoin-cli sendtoaddress'
ghubstan Jul 17, 2020
8269a0d
Remove final modifier
ghubstan Jul 17, 2020
f7d8c0e
Do not use bitcoin.conf files
ghubstan Jul 17, 2020
1847da0
Delete unused bitcoin.conf from resources dir
ghubstan Jul 17, 2020
19346bb
Delete all JUnit related class from main sources
ghubstan Jul 19, 2020
7c974b2
Moving GrpcStubs to test sources
ghubstan Jul 19, 2020
bf584c2
Move test cases into subproject test sources
ghubstan Jul 19, 2020
591c8b2
Change :apitest:test task system property name
ghubstan Jul 20, 2020
b4d3ea7
Add comment about Bisq DAO dev environment
ghubstan Jul 20, 2020
999e9ec
Fix @BeforeClass error handling and use jupiter api
ghubstan Jul 20, 2020
13a8396
Remove unnecessary curly braces
ghubstan Jul 20, 2020
cf031e6
Change 'missing bitcoind path' error msg
ghubstan Jul 22, 2020
27ee4b8
Do not leave orphaned processes after failed teardown
ghubstan Jul 27, 2020
e2f00b7
Remove extra whiteline
ghubstan Jul 27, 2020
8bb7e12
Clarify scaffold tear down error handling
ghubstan Jul 28, 2020
685839d
Add fallbackfee param to bitcoind start cmd
ghubstan Aug 12, 2020
176f0b2
Fix BitcoinCli wrapper error handling
ghubstan Aug 13, 2020
9637cc0
Fix test fail() msg
ghubstan Aug 13, 2020
72ff4dc
Use non-default regtest bitcoind -rpcport
ghubstan Aug 15, 2020
e88bdb9
Add regtest-port-conflicts.md doc
ghubstan Aug 15, 2020
12d52e8
Fix port number typo
ghubstan Aug 15, 2020
fc54125
Add build / run / test categories docs
ghubstan Aug 17, 2020
f85ae2b
Explain how to run test cases from Intellij
ghubstan Aug 17, 2020
af7252e
Fix typo
ghubstan Aug 17, 2020
8b081ad
Update README
ghubstan Aug 17, 2020
c3abd4e
Remove white lines
ghubstan Aug 18, 2020
fa11dab
Add punctuation & re-phrase sentence in README
ghubstan Aug 19, 2020
2ba0ee9
Change access modifer
ghubstan Aug 19, 2020
ba8b9cc
Put 'empty' comments inside ignored catch blocks
ghubstan Aug 19, 2020
1de6239
Shorten line length < 120 chars
ghubstan Aug 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 83 additions & 0 deletions apitest/dao-setup.gradle
@@ -0,0 +1,83 @@
// This gradle file contains tasks to install and clean dao-setup files downloaded from
// https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip
// These tasks are not run by the default build, but they can can be run during a full
// or partial builds, or by themselves.
// To run a full Bisq clean build, test, and install dao-setup files:
// ./gradlew clean build :apitest:installDaoSetup
// To install or re-install dao-setup file only:
// ./gradlew :apitest:installDaoSetup -x test
// To clean installed dao-setup files:
// ./gradlew :apitest:cleanDaoSetup -x test
//
// The :apitest subproject will not run on Windows, and these tasks have not been
// tested on Windows.
def buildResourcesDir = project(":apitest").buildDir.path + '/resources/main'

// This task requires ant in the system $PATH.
task installDaoSetup(dependsOn: 'cleanDaoSetup') {
doLast {
println "Installing dao-setup directories in build dir $buildResourcesDir ..."
def src = 'https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip'
def destfile = project.rootDir.path + '/apitest/src/main/resources/dao-setup.zip'
def url = new URL(src)
def f = new File(destfile)
if (f.exists()) {
println "File $destfile already exists, skipping download."
} else {
if (!f.parentFile.exists())
mkdir "$buildResourcesDir"

println "Downloading $url to $buildResourcesDir ..."
url.withInputStream { i -> f.withOutputStream { it << i } }
}

// We need an ant task for unzipping the dao-setup.zip file.
println "Unzipping $destfile to $buildResourcesDir ..."
ant.unzip(src: 'src/main/resources/dao-setup.zip',
dest: 'src/main/resources',
overwrite: "true") {
// Warning: overwrite: "true" does not work if empty dirs exist, so the
// cleanDaoSetup task should be run before trying to re-install fresh
// dao-setup files.
patternset() {
include(name: '**')
exclude(name: '**/bitcoin.conf') // installed at runtime with correct blocknotify script path
exclude(name: '**/blocknotify') // installed from src/main/resources to allow port configs
}
mapper(type: "identity")
}

// Copy files from unzip target dir 'dao-setup' to build/resources/main.
def daoSetupSrc = project.rootDir.path + '/apitest/src/main/resources/dao-setup'
def daoSetupDest = buildResourcesDir + '/dao-setup'
println "Copying $daoSetupSrc to $daoSetupDest ..."
copy {
from daoSetupSrc
into daoSetupDest
}

// Move dao-setup files from build/resources/main/dao-setup to build/resources/main
file(buildResourcesDir + '/dao-setup/Bitcoin-regtest')
.renameTo(file(buildResourcesDir + '/Bitcoin-regtest'))
file(buildResourcesDir + '/dao-setup/bisq-BTC_REGTEST_Alice_dao')
.renameTo(file(buildResourcesDir + '/bisq-BTC_REGTEST_Alice_dao'))
file(buildResourcesDir + '/dao-setup/bisq-BTC_REGTEST_Bob_dao')
.renameTo(file(buildResourcesDir + '/bisq-BTC_REGTEST_Bob_dao'))
delete file(buildResourcesDir + '/dao-setup')
}
}

task cleanDaoSetup {
doLast {
// When re-installing dao-setup files before re-running tests, the bitcoin
// datadir and dao-setup dirs have to be cleaned first. This task allows
// you to re-install dao-setup files and re-run tests without having to
// re-compile any code.
println "Deleting dao-setup directories in build dir $buildResourcesDir ..."
delete file(buildResourcesDir + '/Bitcoin-regtest')
delete file(buildResourcesDir + '/bisq-BTC_REGTEST_Seed_2002')
delete file(buildResourcesDir + '/bisq-BTC_REGTEST_Arb_dao')
delete file(buildResourcesDir + '/bisq-BTC_REGTEST_Alice_dao')
delete file(buildResourcesDir + '/bisq-BTC_REGTEST_Bob_dao')
}
}
5 changes: 5 additions & 0 deletions apitest/docs/README.md
@@ -0,0 +1,5 @@
# Bisq apitest docs

- [build-run.md](build-run.md): Build and run API tests at the command line and from Intellij.
- [test-categories.md](test-categories.md): How to categorize a test case as `method`, `scenario` or `e2e`.
- [regtest-port-conflicts.md](regtest-port-conflicts.md): Avoid port conflicts when running multiple bitcoin-core apps in regtest mode.
68 changes: 68 additions & 0 deletions apitest/docs/build-run.md
@@ -0,0 +1,68 @@
# Build and Run API Test Harness

## Linux & OSX

The API test harness uses the GNU Bourne-Again SHell `bash`, and is not supported on Windows.

## Predefined DAO / Regtest Setup

The API test harness depends on the contents of https://github.com/bisq-network/bisq/raw/master/docs/dao-setup.zip.
The files contained in dao-setup.zip include a bitcoin-core wallet, a regtest genesis tx and chain of 111 blocks, plus
data directories for Bob and Alice Bisq instances. Bob & Alice wallets are pre-configured with 10 BTC each, and the
equivalent of 2.5 BTC in BSQ distributed among Bob & Alice's BSQ wallets.

See https://github.com/bisq-network/bisq/blob/master/docs/dao-setup.md for details.

## Install DAO / Regtest Setup Files

Bisq's gradle build file defines a task for downloading dao-setup.zip and extracting its contents to the
`apitest/src/main/resources` folder, and the test harness will install a fresh set of data files to the
`apitest/build/resources/main` folder during a test case's scaffold setup phase -- normally a static `@BeforeAll` method.

The dao-setup files can be downloaded during a normal build:

$ ./gradlew clean build :apitest:installDaoSetup

Or by running a single task:

$ ./gradlew :apitest:installDaoSetup

The `:apitest:installDaoSetup` task does not need to be run again until after the next time you run the gradle `clean` task.

## Run API Tests

The API test harness supports narrow & broad functional and full end to end test cases requiring
long setup and teardown times -- for example, to start a bitcoind instance, seednode, arbnode, plus Bob & Alice
Bisq instances, then shut everything down in proper order. For this reason, API test cases do not run during a normal
gradle build.

To run API test cases, pass system property`-DrunApiTests=true`.

To run all existing test cases:

$ ./gradlew :apitest:test -DrunApiTests=true

To run all test cases in a package:

$ ./gradlew :apitest:test --tests "bisq.apitest.method.*" -DrunApiTests=true

To run a single test case:

$ ./gradlew :apitest:test --tests "bisq.apitest.method.GetBalanceTest" -DrunApiTests=true

To run test cases from Intellij, add two JVM arguments to your JUnit launchers:

-DrunApiTests=true -Dlogback.configurationFile=apitest/build/resources/main/logback.xml

The `-Dlogback.configurationFile` property will prevent `logback` from printing warnings about multiple `logback.xml`
files it will find in Bisq jars `cli.jar`, `daemon.jar`, and `seednode.jar`.

## Gradle Test Reports

To see detailed test results, logs, and full stack traces for test failures, open
`apitest/build/reports/tests/test/index.html` in a browser.

## See also

- [test-categories.md](test-categories.md)

12 changes: 12 additions & 0 deletions apitest/docs/regtest-port-conflicts.md
@@ -0,0 +1,12 @@
# Avoiding bitcoin-core regtest port conflicts

Some developers may already be running a `bitcoind` or `bitcoin-qt` instance in regtest mode when they try to run API
test cases. If a `bitcoin-qt` instance is bound to the default regtest port 18444, `apitest` will not be able to start
its own bitcoind instances.

Though it would be preferable for `apitest` to change the bind port for Bisq's `bitcoinj` module at runtime, this is
not currently possible because `bitcoinj` hardcodes the default regtest mode bind port in `RegTestParams`.

To avoid the bind address:port conflict, pass a port option to your bitcoin-core instance:

bitcoin-qt -regtest -port=20444
35 changes: 35 additions & 0 deletions apitest/docs/test-categories.md
@@ -0,0 +1,35 @@
# API Test Categories

This guide describes the categorization of tests.

## Method Tests

A `method` test is the `apitest` analog of a unit test. It tests a single API method such as `getbalance`, but is not
considered a unit test because the code execution path traverses so many layers: from `gRPC` client -> `gRPC` server
side service -> one or more Bisq `core` services, and back to the client.

Method tests have direct access to `gRPC` client stubs, and test asserts are made directly on `gRPC` return values --
Java Objects.

All `method` tests are part of the `bisq.apitest.method` package.

## Scenario Tests

A `scenario` test is a narrow or broad functional test case covering a simple use case such as funding a wallet to a
complex series of trades. Generally, a scenario test case requires multiple `gRPC` method calls.

Scenario tests have direct access to `gRPC` client stubs, and test asserts are made directly on `gRPC` return values --
Java Objects.

All `scenario` tests are part of the `bisq.apitest.scenario` package.

## End to End Tests

An end to end (`e2e`) test can cover a narrow or broad use case, and all client calls go through the `CLI` shell script
`bisq-cli`. End to end tests do not have access to `gRPC` client stubs, and test asserts are made on what the end
user sees on the console -- what`gRPC CLI` prints to `STDOUT`.

As test coverage grows, stable scenario test cases should be migrated to `e2e` test cases.

All `e2e` tests are part of the `bisq.apitest.e2e` package.

15 changes: 15 additions & 0 deletions apitest/scripts/get-bisq-pid.sh
@@ -0,0 +1,15 @@
#!/bin/bash

# Find the pid of the java process by grepping for the mainClassName and appName,
# then print the 2nd column of the output to stdout.
#
# Doing this from Java is problematic, probably due to limitation of the
# apitest.linux.BashCommand implementation.


MAIN_CLASS_NAME=$1
APP_NAME=$2

# TODO args validation

ps aux | grep java | grep "${MAIN_CLASS_NAME}" | grep "${APP_NAME}" | awk '{print $2}'
80 changes: 80 additions & 0 deletions apitest/src/main/java/bisq/apitest/ApiTestMain.java
@@ -0,0 +1,80 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.apitest;

import lombok.extern.slf4j.Slf4j;

import static bisq.apitest.Scaffold.EXIT_FAILURE;
import static bisq.apitest.Scaffold.EXIT_SUCCESS;
import static java.lang.System.err;
import static java.lang.System.exit;



import bisq.apitest.config.ApiTestConfig;

/**
* ApiTestMain is a placeholder for the gradle build file, which requires a valid
* 'mainClassName' property in the :apitest subproject configuration.
*
* It does has some uses:
*
* It can be used to print test scaffolding options: bisq-apitest --help.
*
* It can be used to smoke test your bitcoind environment: bisq-apitest.
*
* It can be used to run the regtest/dao environment for release testing:
* bisq-test --shutdownAfterTests=false
*
* All method, scenario and end to end tests are found in the test sources folder.
*
* Requires bitcoind v0.19.x
*/
@Slf4j
public class ApiTestMain {

public static void main(String[] args) {
new ApiTestMain().execute(args);
}

public void execute(@SuppressWarnings("unused") String[] args) {
try {
Scaffold scaffold = new Scaffold(args).setUp();
ApiTestConfig config = scaffold.config;

if (config.skipTests) {
log.info("Skipping tests ...");
} else {
new SmokeTestBitcoind(config).run();
}

if (config.shutdownAfterTests) {
scaffold.tearDown();
exit(EXIT_SUCCESS);
} else {
log.info("Not shutting down scaffolding background processes will run until ^C / kill -15 is rcvd ...");
}

} catch (Throwable ex) {
err.println("Fault: An unexpected error occurred. " +
"Please file a report at https://bisq.network/issues");
ex.printStackTrace(err);
exit(EXIT_FAILURE);
}
}
}