Skip to content

Commit 4db16ec

Browse files
committed
Merge #11796: [tests] Functional test naming convention
5fecd84 [tests] Remove redundant import in blocktools.py test (Anthony Towns) 9b20bb4 [tests] Check tests conform to naming convention (Anthony Towns) 7250b4e [tests] README.md nit fixes (Anthony Towns) 82b2712 [tests] move witness util functions to blocktools.py (John Newbery) 1e10854 [tests] [docs] update README for new test naming scheme (John Newbery) Pull request description: Splitting #11774 into two parts -- this part updates the README with the proposed naming convention, and adds some checks to test_runner.py that the number of tests violating the naming convention doesn't increase too much. Idea is this part of the change should not introduce merge conflicts or require much rebasing, so reviews of the complicated bits won't become invalidated too often; while the second part will just be file renames, which will require regular rebasing and will introduce merge conflicts with pending PRs, but can be merged later, and should also be much easier to review, since it will only include relatively trivial changes. Tree-SHA512: b96557d41714addbbfe2aed62fb5a48639eaeb1eb3aba30ac1b3a86bb3cb8d796c6247f9c414c4695c4bf54c0ec9968ac88e2f88fb62483bc1a2f89368f7fc80
2 parents 9501dc2 + 5fecd84 commit 4db16ec

File tree

5 files changed

+111
-50
lines changed

5 files changed

+111
-50
lines changed

test/functional/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ don't have test cases for.
2727
`set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of
2828
the subclass, then locally-defined helper methods, then the `run_test()` method.
2929

30+
#### Naming guidelines
31+
32+
- Name the test `<area>_test.py`, where area can be one of the following:
33+
- `feature` for tests for full features that aren't wallet/mining/mempool, eg `feature_rbf.py`
34+
- `interface` for tests for other interfaces (REST, ZMQ, etc), eg `interface_rest.py`
35+
- `mempool` for tests for mempool behaviour, eg `mempool_reorg.py`
36+
- `mining` for tests for mining features, eg `mining_prioritisetransaction.py`
37+
- `p2p` for tests that explicitly test the p2p interface, eg `p2p_disconnect_ban.py`
38+
- `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py`
39+
- `wallet` for tests for wallet features, eg `wallet_keypool.py`
40+
- use an underscore to separate words
41+
- exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py`
42+
- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py`
43+
3044
#### General test-writing advice
3145

3246
- Set `self.num_nodes` to the minimum number of nodes necessary for the test.
@@ -73,7 +87,7 @@ start the networking thread. (Continue with the test logic in your existing
7387
thread.)
7488

7589
- Can be used to write tests where specific P2P protocol behavior is tested.
76-
Examples tests are `p2p-accept-block.py`, `p2p-compactblocks.py`.
90+
Examples tests are `p2p-acceptblock.py`, `p2p-compactblocks.py`.
7791

7892
#### Comptool
7993

test/functional/bumpfee.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
make assumptions about execution order.
1515
"""
1616

17-
from segwit import send_to_witness
17+
from test_framework.blocktools import send_to_witness
1818
from test_framework.test_framework import BitcoinTestFramework
1919
from test_framework import blocktools
2020
from test_framework.mininode import CTransaction

test/functional/segwit.py

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test the SegWit changeover logic."""
66

7+
from test_framework.address import (
8+
key_to_p2sh_p2wpkh,
9+
key_to_p2wpkh,
10+
program_to_witness,
11+
script_to_p2sh_p2wsh,
12+
script_to_p2wsh,
13+
)
14+
from test_framework.blocktools import witness_script, send_to_witness
715
from test_framework.test_framework import BitcoinTestFramework
816
from test_framework.util import *
917
from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex
10-
from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness
18+
from test_framework.address import script_to_p2sh, key_to_p2pkh
1119
from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE
1220
from io import BytesIO
1321

@@ -16,52 +24,6 @@
1624
WIT_V0 = 0
1725
WIT_V1 = 1
1826

19-
# Create a scriptPubKey corresponding to either a P2WPKH output for the
20-
# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given
21-
# pubkey. Returns the hex encoding of the scriptPubKey.
22-
def witness_script(use_p2wsh, pubkey):
23-
if (use_p2wsh == False):
24-
# P2WPKH instead
25-
pubkeyhash = hash160(hex_str_to_bytes(pubkey))
26-
pkscript = CScript([OP_0, pubkeyhash])
27-
else:
28-
# 1-of-1 multisig
29-
witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
30-
scripthash = sha256(witness_program)
31-
pkscript = CScript([OP_0, scripthash])
32-
return bytes_to_hex_str(pkscript)
33-
34-
# Return a transaction (in hex) that spends the given utxo to a segwit output,
35-
# optionally wrapping the segwit output using P2SH.
36-
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
37-
if use_p2wsh:
38-
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
39-
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
40-
else:
41-
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
42-
if not encode_p2sh:
43-
assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
44-
return node.createrawtransaction([utxo], {addr: amount})
45-
46-
# Create a transaction spending a given utxo to a segwit output corresponding
47-
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
48-
# encode_p2sh determines whether to wrap in P2SH.
49-
# sign=True will have the given node sign the transaction.
50-
# insert_redeem_script will be added to the scriptSig, if given.
51-
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
52-
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
53-
if (sign):
54-
signed = node.signrawtransaction(tx_to_witness)
55-
assert("errors" not in signed or len(["errors"]) == 0)
56-
return node.sendrawtransaction(signed["hex"])
57-
else:
58-
if (insert_redeem_script):
59-
tx = FromHex(CTransaction(), tx_to_witness)
60-
tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
61-
tx_to_witness = ToHex(tx)
62-
63-
return node.sendrawtransaction(tx_to_witness)
64-
6527
def getutxo(txid):
6628
utxo = {}
6729
utxo["vout"] = 0

test/functional/test_framework/blocktools.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,24 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Utilities for manipulating blocks and transactions."""
66

7+
from .address import (
8+
key_to_p2sh_p2wpkh,
9+
key_to_p2wpkh,
10+
script_to_p2sh_p2wsh,
11+
script_to_p2wsh,
12+
)
713
from .mininode import *
8-
from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN
14+
from .script import (
15+
CScript,
16+
OP_0,
17+
OP_1,
18+
OP_CHECKMULTISIG,
19+
OP_CHECKSIG,
20+
OP_RETURN,
21+
OP_TRUE,
22+
hash160,
23+
)
24+
from .util import assert_equal
925

1026
# Create a block (with regtest difficulty)
1127
def create_block(hashprev, coinbase, nTime=None):
@@ -108,3 +124,49 @@ def get_legacy_sigopcount_tx(tx, fAccurate=True):
108124
# scriptSig might be of type bytes, so convert to CScript for the moment
109125
count += CScript(j.scriptSig).GetSigOpCount(fAccurate)
110126
return count
127+
128+
# Create a scriptPubKey corresponding to either a P2WPKH output for the
129+
# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given
130+
# pubkey. Returns the hex encoding of the scriptPubKey.
131+
def witness_script(use_p2wsh, pubkey):
132+
if (use_p2wsh == False):
133+
# P2WPKH instead
134+
pubkeyhash = hash160(hex_str_to_bytes(pubkey))
135+
pkscript = CScript([OP_0, pubkeyhash])
136+
else:
137+
# 1-of-1 multisig
138+
witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
139+
scripthash = sha256(witness_program)
140+
pkscript = CScript([OP_0, scripthash])
141+
return bytes_to_hex_str(pkscript)
142+
143+
# Return a transaction (in hex) that spends the given utxo to a segwit output,
144+
# optionally wrapping the segwit output using P2SH.
145+
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
146+
if use_p2wsh:
147+
program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
148+
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
149+
else:
150+
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
151+
if not encode_p2sh:
152+
assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey))
153+
return node.createrawtransaction([utxo], {addr: amount})
154+
155+
# Create a transaction spending a given utxo to a segwit output corresponding
156+
# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH;
157+
# encode_p2sh determines whether to wrap in P2SH.
158+
# sign=True will have the given node sign the transaction.
159+
# insert_redeem_script will be added to the scriptSig, if given.
160+
def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""):
161+
tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount)
162+
if (sign):
163+
signed = node.signrawtransaction(tx_to_witness)
164+
assert("errors" not in signed or len(["errors"]) == 0)
165+
return node.sendrawtransaction(signed["hex"])
166+
else:
167+
if (insert_redeem_script):
168+
tx = FromHex(CTransaction(), tx_to_witness)
169+
tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
170+
tx_to_witness = ToHex(tx)
171+
172+
return node.sendrawtransaction(tx_to_witness)

test/functional/test_runner.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def main():
272272
sys.exit(0)
273273

274274
check_script_list(config["environment"]["SRCDIR"])
275+
check_script_prefixes()
275276

276277
if not args.keepcache:
277278
shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True)
@@ -470,6 +471,28 @@ def was_successful(self):
470471
return self.status != "Failed"
471472

472473

474+
def check_script_prefixes():
475+
"""Check that no more than `EXPECTED_VIOLATION_COUNT` of the
476+
test scripts don't start with one of the allowed name prefixes."""
477+
EXPECTED_VIOLATION_COUNT = 77
478+
479+
# LEEWAY is provided as a transition measure, so that pull-requests
480+
# that introduce new tests that don't conform with the naming
481+
# convention don't immediately cause the tests to fail.
482+
LEEWAY = 10
483+
484+
good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet)_")
485+
bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]
486+
487+
if len(bad_script_names) < EXPECTED_VIOLATION_COUNT:
488+
print("{}HURRAY!{} Number of functional tests violating naming convention reduced!".format(BOLD[1], BOLD[0]))
489+
print("Consider reducing EXPECTED_VIOLATION_COUNT from %d to %d" % (EXPECTED_VIOLATION_COUNT, len(bad_script_names)))
490+
elif len(bad_script_names) > EXPECTED_VIOLATION_COUNT:
491+
print("INFO: %d tests not meeting naming conventions (expected %d):" % (len(bad_script_names), EXPECTED_VIOLATION_COUNT))
492+
print(" %s" % ("\n ".join(sorted(bad_script_names))))
493+
assert len(bad_script_names) <= EXPECTED_VIOLATION_COUNT + LEEWAY, "Too many tests not following naming convention! (%d found, expected: <= %d)" % (len(bad_script_names), EXPECTED_VIOLATION_COUNT)
494+
495+
473496
def check_script_list(src_dir):
474497
"""Check scripts directory.
475498

0 commit comments

Comments
 (0)