Browse Source

tests/test_lightningd.py: incorporate everything from old test-basic shell test.

This moves all the non-legacy blackbox testing into python.

Before:
	real	10m18.385s

After:
	real	9m54.877s

Note that this doesn't valgrind the subdaemons: that patch seems to cause
some issues in the python framework which I am still chasing.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 8 years ago
committed by Christian Decker
parent
commit
f61da7eb64
  1. 3
      Makefile
  2. 5
      lightningd/pay.c
  3. 20
      lightningd/test/Makefile
  4. 123
      lightningd/test/test-basic
  5. 108
      tests/test_lightningd.py
  6. 12
      tests/utils.py

3
Makefile

@ -234,10 +234,7 @@ $(HELPER_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(CORE_PROTOBUF_OBJS) $(BITCOIN_OBJS
test-protocol: test/test_protocol test-protocol: test/test_protocol
set -e; TMP=`mktemp`; for f in test/commits/*.script; do if ! $(VALGRIND) test/test_protocol < $$f > $$TMP; then echo "test/test_protocol < $$f FAILED" >&2; exit 1; fi; diff -u $$TMP $$f.expected; done; rm $$TMP set -e; TMP=`mktemp`; for f in test/commits/*.script; do if ! $(VALGRIND) test/test_protocol < $$f > $$TMP; then echo "test/test_protocol < $$f FAILED" >&2; exit 1; fi; diff -u $$TMP $$f.expected; done; rm $$TMP
# FIXME: check doesn't depend on lightningd-blackbox-tests, since they
# can't run in parallel with daemon blackbox tests.
check: test-protocol check: test-protocol
$(MAKE) lightningd-blackbox-tests
$(MAKE) pytest $(MAKE) pytest
pytest: daemon/lightningd daemon/lightning-cli lightningd-all pytest: daemon/lightningd daemon/lightning-cli lightningd-all

5
lightningd/pay.c

@ -59,8 +59,11 @@ void payment_succeeded(struct lightningd *ld, struct htlc_end *dst,
const struct preimage *rval) const struct preimage *rval)
{ {
/* FIXME: dev_htlc will do this! */ /* FIXME: dev_htlc will do this! */
if (!dst->pay_command) if (!dst->pay_command) {
log_debug(ld->log, "Payment succeeded on HTLC %"PRIu64,
dst->htlc_id);
return; return;
}
assert(!dst->pay_command->rval); assert(!dst->pay_command->rval);
dst->pay_command->rval = tal_dup(dst->pay_command, dst->pay_command->rval = tal_dup(dst->pay_command,

20
lightningd/test/Makefile

@ -14,25 +14,5 @@ $(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_SRC) $(LIGHTNINGD_LI
lightningd/tests: $(LIGHTNINGD_TEST_PROGRAMS:%=unittest/%) lightningd/tests: $(LIGHTNINGD_TEST_PROGRAMS:%=unittest/%)
# So far, only basic test.
lightningd-test-0-basic: lightningd-test-setup-0
lightningd-test-0-%:
NO_VALGRIND=$(NO_VALGRIND) VG_TRACE_CHILDREN="--trace-children=yes" VARIANT=0 lightningd/test/test-$*
# We shutdown first in case something is left over.
lightningd-test-setup-%: lightningd-all daemon/lightning-cli
VARIANT=$* daemon/test/scripts/shutdown.sh 2>/dev/null || true
VARIANT=$* daemon/test/scripts/setup.sh
lightningd-test-shutdown-0: lightningd-test-0-basic
VARIANT=0 daemon/test/scripts/shutdown.sh
lightningd-test-shutdown-1: lightningd-test-1-basic\ --restart
VARIANT=1 daemon/test/scripts/shutdown.sh
lightningd-test-shutdown-2: lightningd-test-2-basic\ --reconnect
VARIANT=2 daemon/test/scripts/shutdown.sh
lightningd-blackbox-tests: lightningd-test-shutdown-0 #lightningd-test-shutdown-1 lightningd-test-shutdown-2
check-source-bolt: $(LIGHTNINGD_TEST_SRC:%=bolt-check/%) check-source-bolt: $(LIGHTNINGD_TEST_SRC:%=bolt-check/%)
check-whitespace: $(LIGHTNINGD_TEST_SRC:%=check-whitespace/%) check-whitespace: $(LIGHTNINGD_TEST_SRC:%=check-whitespace/%)

123
lightningd/test/test-basic

@ -1,123 +0,0 @@
#! /bin/sh -e
# Wherever we are, we want to be in daemon/test dir.
cd `git rev-parse --show-toplevel`/daemon/test
. scripts/vars.sh
. scripts/helpers.sh
parse_cmdline 2 "$@"
setup_lightning 2
start_lightningd 2 lightningd/lightningd
if lcli1 connect localhost $PORT2 $ID1; then
echo Connected OK with wrong ID? >&2
exit 1
fi
lcli1 connect localhost $PORT2 $ID2
# Should now be exchanging gossip
lcli1 getpeers | $FGREP '"condition" : "Exchanging gossip"'
# It should have gone through these steps
lcli1 getpeers info | $FGREP "Starting handshake as initiator"
lcli1 getpeers info | $FGREP "Beginning gossip"
lcli1 getpeers info | $FGREP "Exchanging gossip"
# Both should still be owned by gossip
lcli1 getpeers | $FGREP '"owner" : "lightningd_gossip"'
lcli2 getpeers | $FGREP '"owner" : "lightningd_gossip"'
# Add some funds.
NEWADDR=`lcli1 newaddr | get_field address`
FUND_INPUT_TXID=`$CLI sendtoaddress $NEWADDR 0.10000002`
FUND_INPUT_TX=`$CLI getrawtransaction $FUND_INPUT_TXID`
lcli1 addfunds $FUND_INPUT_TX | $FGREP '"satoshis" : 10000002'
# Now fund a channel.
lcli1 fundchannel $ID2 1000000
# Now wait for it to reach depth
lcli1 getpeers info | $FGREP "Waiting for our funding tx"
[ `lcli1 getpeers | get_field msatoshi_to_us` = 1000000000 ]
[ `lcli1 getpeers | get_field msatoshi_to_them` = 0 ]
[ `lcli2 getpeers | get_field msatoshi_to_them` = 1000000000 ]
[ `lcli2 getpeers | get_field msatoshi_to_us` = 0 ]
$CLI generate 10
check "lcli1 getpeers info | $FGREP 'Funding tx reached depth'"
check "lcli2 getpeers info | $FGREP 'Funding tx reached depth'"
check "lcli1 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'condition : Normal operation'"
check "lcli2 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'condition : Normal operation'"
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'`
# This is actually dust
lcli1 dev-newhtlc $ID2 100000 $(( $(blockheight) + 10 )) $RHASH
check "lcli1 getlog debug | $FGREP 'Sending commit_sig with 0 htlc sigs'"
check "lcli2 getlog debug | $FGREP 'their htlc 0 locked'"
check "lcli2 getpeers info | $FGREP 'failed htlc 0 code 0x400f'"
check "lcli1 getpeers info | $FGREP 'htlc 0 failed with code 0x400f'"
# This one isn't dust.
RHASH=`lcli2 invoice 100000000 testpayment1 | get_field rhash`
[ `lcli2 listinvoice testpayment1 | get_field complete` = false ]
lcli1 dev-newhtlc $ID2 100000000 $(( $(blockheight) + 10 )) $RHASH
check "lcli1 getlog debug | $FGREP 'Sending commit_sig with 1 htlc sigs'"
check "lcli2 getlog debug | $FGREP 'Resolving invoice '\'testpayment1\'' with HTLC 1'"
[ `lcli2 listinvoice testpayment1 | get_field complete` = true ]
check "lcli1 getpeers | get_field msatoshi_to_us | $FGREP -w 900000000"
[ `lcli1 getpeers | get_field msatoshi_to_us` = 900000000 ]
[ `lcli1 getpeers | get_field msatoshi_to_them` = 100000000 ]
[ `lcli2 getpeers | get_field msatoshi_to_them` = 900000000 ]
[ `lcli2 getpeers | get_field msatoshi_to_us` = 100000000 ]
RHASH=`lcli2 invoice 200000000 testpayment2 | get_field rhash`
[ `lcli2 listinvoice testpayment2 | get_field complete` = false ]
# Not enough delay.
if lcli1 sendpay '[ { "msatoshi" : 200000000, "id" : "'$ID2'", "delay" : 3 } ]' $RHASH; then
echo Infufficient delay succeeded? >&2
fi
# Insufficient funds.
if lcli1 sendpay '[ { "msatoshi" : 199999999, "id" : "'$ID2'", "delay" : 5 } ]' $RHASH; then
echo Infufficient funds succeeded? >&2
fi
# Bad ID.
if lcli1 sendpay '[ { "msatoshi" : 200000000,, "id" : "'$ID2'", "delay" : 5 } ]' 00000000000000000000000000000000; then
echo Bad ID succeeded? >&2
fi
# This works
lcli1 sendpay '[ { "msatoshi" : 200000000, "id" : "'$ID2'", "delay" : 5 } ]' $RHASH
[ `lcli2 listinvoice testpayment2 | get_field complete` = true ]
# This will "succeed", but won't actually send anything (duplicate)
if lcli1 getlog debug | $FGREP 'json_sendpay: found previous... succeeded'; then
echo Already had found previous? >&2
exit 1
fi
lcli1 sendpay '[ { "msatoshi" : 200000000, "id" : "'$ID2'", "delay" : 5 } ]' $RHASH
lcli1 getlog debug | $FGREP 'json_sendpay: found previous... succeeded'
# FIXME: test paying via another node, should fail to pay twice.
# Overpay.
RHASH=`lcli2 invoice 200000000 testpayment3 | get_field rhash`
lcli1 sendpay '[ { "msatoshi" : 200000001, "id" : "'$ID2'", "delay" : 5 } ]' $RHASH
lcli1 stop
lcli2 stop
all_ok

108
tests/test_lightningd.py

@ -3,6 +3,8 @@ from concurrent import futures
from hashlib import sha256 from hashlib import sha256
from lightning import LightningRpc, LegacyLightningRpc from lightning import LightningRpc, LegacyLightningRpc
import copy
import json
import logging import logging
import os import os
import sys import sys
@ -19,6 +21,8 @@ if os.getenv("TEST_DEBUG", None) != None:
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout) logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
logging.info("Tests running in '%s'", TEST_DIR) logging.info("Tests running in '%s'", TEST_DIR)
def to_json(arg):
return json.loads(json.dumps(arg))
def setupBitcoind(): def setupBitcoind():
global bitcoind global bitcoind
@ -138,12 +142,112 @@ class LightningDTests(BaseLightningDTests):
def test_connect(self): def test_connect(self):
l1,l2 = self.connect() l1,l2 = self.connect()
p1 = l1.rpc.getpeer(l2.info['id']) p1 = l1.rpc.getpeer(l2.info['id'], 'info')
p2 = l2.rpc.getpeer(l1.info['id']) p2 = l2.rpc.getpeer(l1.info['id'], 'info')
assert p1['condition'] == 'Exchanging gossip' assert p1['condition'] == 'Exchanging gossip'
assert p2['condition'] == 'Exchanging gossip' assert p2['condition'] == 'Exchanging gossip'
# It should have gone through these steps
assert 'condition: Starting handshake as initiator' in p1['log']
assert 'condition: Beginning gossip' in p1['log']
assert 'condition: Exchanging gossip' in p1['log']
# Both should still be owned by gossip
assert p1['owner'] == 'lightningd_gossip'
assert p2['owner'] == 'lightningd_gossip'
def test_htlc(self):
l1,l2 = self.connect()
self.fund_channel(l1, l2, 10**6)
l1.daemon.wait_for_log('condition: Funding tx reached depth 6')
l2.daemon.wait_for_log('condition: Funding tx reached depth 6')
secret = '1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd'
rhash = l1.rpc.dev_rhash(secret)['rhash']
# This is actually dust, and uncalled for.
l1.rpc.dev_newhtlc(l2.info['id'], 100000, l1.bitcoin.rpc.getblockcount() + 10, rhash)
l1.daemon.wait_for_log('Sending commit_sig with 0 htlc sigs')
l2.daemon.wait_for_log('their htlc 0 locked')
l2.daemon.wait_for_log('failed htlc 0 code 0x400f')
l1.daemon.wait_for_log('htlc 0 failed with code 0x400f')
# Set up invoice (non-dust, just to test), and pay it.
# This one isn't dust.
rhash = l2.rpc.invoice(100000000, 'testpayment1')['rhash']
assert l2.rpc.listinvoice('testpayment1')[0]['complete'] == False
l1.rpc.dev_newhtlc(l2.info['id'], 100000000, l1.bitcoin.rpc.getblockcount() + 10, rhash)
l1.daemon.wait_for_log('Sending commit_sig with 1 htlc sigs')
l2.daemon.wait_for_log('their htlc 1 locked')
l2.daemon.wait_for_log("Resolving invoice 'testpayment1' with HTLC 1")
assert l2.rpc.listinvoice('testpayment1')[0]['complete'] == True
l1.daemon.wait_for_log('Payment succeeded on HTLC 1')
# Balances should have changed.
p1 = l1.rpc.getpeer(l2.info['id'])
p2 = l2.rpc.getpeer(l1.info['id'])
assert p1['msatoshi_to_us'] == 900000000
assert p1['msatoshi_to_them'] == 100000000
assert p2['msatoshi_to_us'] == 100000000
assert p2['msatoshi_to_them'] == 900000000
def test_sendpay(self):
l1,l2 = self.connect()
self.fund_channel(l1, l2, 10**6)
amt = 200000000
rhash = l2.rpc.invoice(amt, 'testpayment2')['rhash']
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
routestep = { 'msatoshi' : amt, 'id' : l2.info['id'], 'delay' : 5}
# Insufficient funds.
rs = copy.deepcopy(routestep)
rs['msatoshi'] = rs['msatoshi'] - 1
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
# Gross overpayment (more than factor of 2)
rs = copy.deepcopy(routestep)
rs['msatoshi'] = rs['msatoshi'] * 2 + 1
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
# Insufficient delay.
rs = copy.deepcopy(routestep)
rs['delay'] = rs['delay'] - 2
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
# Bad ID.
rs = copy.deepcopy(routestep)
rs['id'] = '00000000000000000000000000000000'
self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False
# This works.
l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
# Repeat will "succeed", but won't actually send anything (duplicate)
assert not l1.daemon.is_in_log('... succeeded')
l1.rpc.sendpay(to_json([routestep]), rhash)
l1.daemon.wait_for_log('... succeeded')
assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True
# Overpaying by "only" a factor of 2 succeeds.
rhash = l2.rpc.invoice(amt, 'testpayment3')['rhash']
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == False
routestep = { 'msatoshi' : amt * 2, 'id' : l2.info['id'], 'delay' : 5}
l1.rpc.sendpay(to_json([routestep]), rhash)
assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True
# FIXME: test paying via another node, should fail to pay twice.
def test_gossip_jsonrpc(self): def test_gossip_jsonrpc(self):
l1,l2 = self.connect() l1,l2 = self.connect()

12
tests/utils.py

@ -75,6 +75,18 @@ class TailableProc(object):
self.logs_cond.notifyAll() self.logs_cond.notifyAll()
self.running = False self.running = False
def is_in_log(self, regex):
"""Look for `regex` in the logs."""
ex = re.compile(regex)
for l in self.logs:
if ex.search(l):
logging.debug("Found '%s' in logs", regex)
return True
logging.debug("Did not find '%s' in logs", regex)
return False
def wait_for_log(self, regex, offset=1000, timeout=60): def wait_for_log(self, regex, offset=1000, timeout=60):
"""Look for `regex` in the logs. """Look for `regex` in the logs.

Loading…
Cancel
Save