From a5df3f1747c0e7063cd87fd8bee01d1a26b708d6 Mon Sep 17 00:00:00 2001 From: Edward Hennis Date: Tue, 23 Dec 2014 20:44:36 -0500 Subject: [PATCH] Support a "no_server" flag in test config. * Will use a running instance of rippled (possibly in a debugger). * Modify all tests to respect the server_default value. * Fail test if new account already exists and has a balance. * README.md with instructions for advanced test debugging, particularly using no_server. --- test/README.md | 79 ++++++++++++++++++++++++++++++++++++++++++ test/config-example.js | 8 ++++- test/jsonrpc-test.js | 6 ++-- test/server-test.js | 5 +-- test/testutils.js | 75 ++++++++++++++++++++++++++++++--------- test/websocket-test.js | 11 +++--- 6 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 test/README.md diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000000..6341605d86 --- /dev/null +++ b/test/README.md @@ -0,0 +1,79 @@ +# Integration tests + +## Basic usage. + +Documentation for installation of dependencies and running these +tests can be found with the +[_Rippled build instructions_][unit_testing]. +(Also for [_Windows_][windows_unit_testing], +[_OS X_][osx_unit_testing], +or [_Ubuntu_][ubuntu_unit_testing].) + +## Advanced usage. + +These instructions assume familiarity with the instructions linked above. + +### Debugging rippled + +By default, each test will start and stop an independent instance of `rippled`. +This ensures that each test is run against the known +[_genesis ledger_][genesis_ledger]. + +To use a running `rippled`, particularly one running in a debugger, follow +these steps: + +1. Make a copy the example configuration file: `cp -i test/config-example.js test/config.js` + +2. Edit `test/config.js` to select the "debug" server configuration. + * Change the existing default server to: `exports.server_default = "debug";` + (near the top of the file). + +3. Create a `rippled.cfg` file for the tests. + 1. Run `npm test`. The tests will fail. **This failure is expected.** + 2. Copy and/or rename the `tmp/server/debug/rippled.cfg` file to somewhere + convenient. + +4. Start `rippled` (in a debugger) with command line options +`-a -v --conf `. + +5. Set any desired breakpoints in the `rippled` source. + +6. Running one test per [_genesis ledger_][genesis_ledger] is highly recommended. +If the relevant `.js` file contains more than one test, change `test(` to +`test.only(` for the single desired test. + * To run multiple tests, change `test(` to `test.skip(` for any undesired tests + in the .js file. + +7. Start test(s) in the [_node-inspector_][node_inspector] debugger. +(Note that the tests can be run without the debugger, but there will probably +be problems with timeouts or reused ledgers). + 1. `node_modules/node-inspector/bin/inspector.js &` + 2. `mocha --debug --debug-brk test/` + 3. Browse to http://127.0.0.1:8080/debug?port=5858 in a browser supported + by [_node-inspector_][node_inspector] (i.e. Chrome or Safari). + +8. To run multiple tests, put a breakpoint in the following function: + * File `testutils.js` -> function `build_teardown()` -> nested function + `teardown()` -> nested series function `stop_server()`. + * When this breakpoint is + hit, stop and restart `rippled`. + +9. Use the [_node-inspector UI_][node_inspector_ui] to step through and run +the test(s) until control is handed off to `rippled`. When the request is +finished control will be handed back to node-inspector, which may or may not +stop depending on which breakpoints are set. + +### After debugging + +1. To return to the default behavior, edit `test/config.js` and change the +default server back to its original value: `exports.server_default = "alpha";`. + * Alternately, delete `test/config.js`. + +[unit_testing]: https://wiki.ripple.com/Rippled_build_instructions#Unit_testing +[windows_unit_testing]: https://wiki.ripple.com/Visual_Studio_2013_Build_Instructions#Unit_Tests_.28Recommended.29 +[osx_unit_testing]: https://wiki.ripple.com/OSX_Build_Instructions#System_Tests_.28Recommended.29 +[ubuntu_unit_testing]: https://wiki.ripple.com/Ubuntu_build_instructions#System_Tests_.28Recommended.29 +[genesis_ledger]: https://wiki.ripple.com/Genesis_ledger +[node_inspector]: https://wiki.ripple.com/Rippled_build_instructions#node-inspector +[node_inspector_ui]: https://github.com/node-inspector/node-inspector/blob/master/README.md + diff --git a/test/config-example.js b/test/config-example.js index 8d1bd92584..3bfe59460d 100644 --- a/test/config-example.js +++ b/test/config-example.js @@ -3,6 +3,7 @@ // var path = require("path"); +var extend = require('extend'); var testconfig = require("./testconfig.js"); exports.accounts = testconfig.accounts; @@ -59,6 +60,11 @@ exports.servers = { } }; +exports.servers.debug = extend({ + no_server: true, + debug_logfile: "debug.log" +}, exports.servers.alpha); + exports.http_servers = { // A local test server "zed" : { @@ -67,4 +73,4 @@ exports.http_servers = { } }; -// vim:sw=2:sts=2:ts=8:et \ No newline at end of file +// vim:sw=2:sts=2:ts=8:et diff --git a/test/jsonrpc-test.js b/test/jsonrpc-test.js index abd6922a5f..bb9210b95f 100644 --- a/test/jsonrpc-test.js +++ b/test/jsonrpc-test.js @@ -70,7 +70,7 @@ suite('JSON-RPC', function() { }); test('server info', function(done) { - var rippled_config = config.servers.alpha; + var rippled_config = testutils.get_server_config(config); var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port); client.call('server_info', [ ], function (result) { @@ -82,7 +82,7 @@ suite('JSON-RPC', function() { }); test('subscribe server', function(done) { - var rippled_config = config.servers.alpha; + var rippled_config = testutils.get_server_config(config); var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port); var http_config = config.http_servers["zed"]; @@ -100,7 +100,7 @@ suite('JSON-RPC', function() { test('subscribe ledger', function(done) { var self = this; - var rippled_config = config.servers.alpha; + var rippled_config = testutils.get_server_config(config); var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port); var http_config = config.http_servers["zed"]; diff --git a/test/server-test.js b/test/server-test.js index 41191f367b..935797195d 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -6,8 +6,9 @@ var config = testutils.init_config(); suite('Standalone server startup', function() { test('server start and stop', function(done) { - var cfg = extend({}, config.default_server_config, config.servers.alpha); - var alpha = Server.from_config("alpha", cfg); + var host = config.server_default; + var cfg = testutils.get_server_config(config, host); + var alpha = Server.from_config(host, cfg); alpha.on('started', function () { alpha.on('stopped', function () { done(); diff --git a/test/testutils.js b/test/testutils.js index 2eac59e593..671ace017c 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -23,6 +23,14 @@ function init_config() { return require('ripple-lib').config.load(get_config()); }; +exports.get_server_config = +get_server_config = +function(config, host) { + config = config || init_config(); + host = host || config.server_default; + return extend({}, config.default_server_config, config.servers[host]); +} + function prepare_tests(tests, fn) { var tests = typeof tests === 'string' ? [ tests ] : tests; var result = [ ]; @@ -88,12 +96,12 @@ function build_setup(opts, host) { var steps = [ function run_server(callback) { - if (opts.no_server) { + var server_config = get_server_config(config, host); + + if (opts.no_server || server_config.no_server) { return callback(); } - var server_config = extend({}, config.default_server_config, config.servers[host]); - data.server = Server.from_config(host, server_config, !!opts.verbose_server); // Setting undefined is a noop here @@ -151,6 +159,7 @@ function build_teardown(host) { function teardown(done) { var data = this.store[host]; var opts = data.opts; + var server_config = get_server_config(config, host); var series = [ function disconnect_websocket(callback) { @@ -162,7 +171,7 @@ function build_teardown(host) { }, function stop_server(callback) { - if (opts.no_server) { + if (opts.no_server || server_config.no_server) { callback(); } else { data.server.once('stopped', callback) @@ -172,7 +181,7 @@ function build_teardown(host) { } ]; - if (!opts.no_server && data.server.stopped) { + if (!(opts.no_server || server_config.no_server) && data.server.stopped) { done() } else { async.series(series, done); @@ -206,12 +215,9 @@ function account_dump(remote, account, callback) { // construct a json result }; -function create_accounts(remote, src, amount, accounts, callback) { - assert.strictEqual(arguments.length, 5); - - remote.set_account_seq(src, 1); - - async.forEach(accounts, function (account, callback) { +exports.fund_account = +fund_account = +function(remote, src, account, amount, callback) { // Cache the seq as 1. // Otherwise, when other operations attempt to opperate async against the account they may get confused. remote.set_account_seq(account, 1); @@ -220,17 +226,52 @@ function create_accounts(remote, src, amount, accounts, callback) { tx.payment(src, account, amount); - tx.once('proposed', function (m) { - //console.log('proposed: %s', JSON.stringify(m)); - callback(m.engine_result === 'tesSUCCESS' ? null : new Error()); + tx.once('proposed', function (result) { + //console.log('proposed: %s', JSON.stringify(result)); + callback(result.engine_result === 'tesSUCCESS' ? null : new Error()); }); - tx.once('error', function (m) { - //console.log('error: %s', JSON.stringify(m)); - callback(m); + tx.once('error', function (result) { + //console.log('error: %s', JSON.stringify(result)); + callback(result); }); tx.submit(); +} + +exports.create_account = +create_account = +function(remote, src, account, amount, callback) { + // Before creating the account, check if it exists in the ledger. + // If it does, regardless of the balance, fail the test, because + // the ledger is not in the expected state. + var info = remote.requestAccountInfo(account); + + info.once('success', function(result) { + // The account exists. Fail by returning an error to callback. + callback(new Error("Account " + account + " already exists")); + }); + + info.once('error', function(result) { + if (result.error === "remoteError" && result.remote.error === "actNotFound") { + // rippled indicated the account does not exist. Create it by funding it. + fund_account(remote, src, account, amount, callback); + } else { + // Some other error occurred. Pass it up to the callback. + callback(result); + } + }); + + info.request(); +} + +function create_accounts(remote, src, amount, accounts, callback) { + assert.strictEqual(arguments.length, 5); + + remote.set_account_seq(src, 1); + + async.forEach(accounts, function (account, callback) { + create_account(remote, src, account, amount, callback); }, callback); }; diff --git a/test/websocket-test.js b/test/websocket-test.js index 4f2a710af0..1c39e9614e 100644 --- a/test/websocket-test.js +++ b/test/websocket-test.js @@ -11,11 +11,12 @@ suite('WebSocket connection', function() { setup(function(done) { this.timeout(2000); - var cfg = extend({}, config.default_server_config, config.servers.alpha); + var host = config.server_default; + var cfg = testutils.get_server_config(config, host); if (cfg.no_server) { done(); } else { - server = Server.from_config("alpha", cfg); + server = Server.from_config(host, cfg); server.once('started', done) server.start(); } @@ -24,7 +25,8 @@ suite('WebSocket connection', function() { teardown(function(done) { this.timeout(2000); - if (config.servers.alpha.no_server) { + var cfg = testutils.get_server_config(config); + if (cfg.no_server) { done(); } else { server.on('stopped', done); @@ -39,7 +41,8 @@ suite('WebSocket connection', function() { // push the measured time out this far. this.timeout(3000); - var alpha = Remote.from_config("alpha"); + var host = config.server_default; + var alpha = Remote.from_config(host); alpha.on('connected', function () { alpha.on('disconnected', function () {