aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_assumevalid.py4
-rwxr-xr-xtest/functional/feature_notifications.py6
-rwxr-xr-xtest/functional/feature_pruning.py7
-rwxr-xr-xtest/functional/p2p_compactblocks.py2
-rwxr-xr-xtest/functional/p2p_leak.py6
-rwxr-xr-xtest/functional/p2p_sendheaders.py16
-rwxr-xr-xtest/functional/p2p_timeouts.py18
-rwxr-xr-xtest/functional/rpc_blockchain.py1
-rwxr-xr-xtest/functional/rpc_getblockstats.py4
-rwxr-xr-xtest/functional/test_framework/mininode.py68
-rw-r--r--test/functional/test_framework/util.py2
-rwxr-xr-xtest/functional/test_runner.py6
-rwxr-xr-xtest/functional/wallet_basic.py9
-rwxr-xr-xtest/functional/wallet_fallbackfee.py1
-rwxr-xr-xtest/functional/wallet_labels.py8
-rwxr-xr-xtest/functional/wallet_multiwallet.py29
-rwxr-xr-xtest/lint/check-rpc-mappings.py4
-rwxr-xr-xtest/lint/commit-script-check.sh1
-rwxr-xr-xtest/lint/git-subtree-check.sh1
-rwxr-xr-xtest/lint/lint-all.sh6
-rwxr-xr-xtest/lint/lint-filenames.sh24
-rwxr-xr-xtest/lint/lint-include-guards.sh3
-rwxr-xr-xtest/lint/lint-includes.sh3
-rwxr-xr-xtest/lint/lint-locale-dependence.sh3
-rwxr-xr-xtest/lint/lint-logs.sh4
-rwxr-xr-xtest/lint/lint-python-shebang.sh4
-rwxr-xr-xtest/lint/lint-python-utf8-encoding.sh20
-rwxr-xr-xtest/lint/lint-python.sh2
-rwxr-xr-xtest/lint/lint-shell-locale.sh24
-rwxr-xr-xtest/lint/lint-shell.sh6
-rwxr-xr-xtest/lint/lint-tests.sh3
-rwxr-xr-xtest/lint/lint-whitespace.sh3
-rwxr-xr-xtest/util/bitcoin-util-test.py8
-rwxr-xr-xtest/util/rpcauth-test.py2
34 files changed, 227 insertions, 81 deletions
diff --git a/test/functional/feature_assumevalid.py b/test/functional/feature_assumevalid.py
index 5a09142412..a925c70783 100755
--- a/test/functional/feature_assumevalid.py
+++ b/test/functional/feature_assumevalid.py
@@ -68,12 +68,12 @@ class AssumeValidTest(BitcoinTestFramework):
def send_blocks_until_disconnected(self, p2p_conn):
"""Keep sending blocks to the node until we're disconnected."""
for i in range(len(self.blocks)):
- if p2p_conn.state != "connected":
+ if not p2p_conn.is_connected:
break
try:
p2p_conn.send_message(msg_block(self.blocks[i]))
except IOError as e:
- assert str(e) == 'Not connected, no pushbuf'
+ assert not p2p_conn.is_connected
break
def assert_blockchain_height(self, node, height):
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 8964c8d64b..6d51f31e35 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -36,7 +36,7 @@ class NotificationsTest(BitcoinTestFramework):
wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10)
# file content should equal the generated blocks hashes
- with open(self.block_filename, 'r') as f:
+ with open(self.block_filename, 'r', encoding="utf-8") as f:
assert_equal(sorted(blocks), sorted(l.strip() for l in f.read().splitlines()))
self.log.info("test -walletnotify")
@@ -45,7 +45,7 @@ class NotificationsTest(BitcoinTestFramework):
# file content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
- with open(self.tx_filename, 'r') as f:
+ with open(self.tx_filename, 'r', encoding="ascii") as f:
assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines()))
os.remove(self.tx_filename)
@@ -58,7 +58,7 @@ class NotificationsTest(BitcoinTestFramework):
# file content should equal the generated transaction hashes
txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count)))
- with open(self.tx_filename, 'r') as f:
+ with open(self.tx_filename, 'r', encoding="ascii") as f:
assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines()))
# Mine another 41 up-version blocks. -alertnotify should trigger on the 51st.
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 11a52b9ee2..d400507a66 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -260,10 +260,17 @@ class PruneTest(BitcoinTestFramework):
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
+ # Save block transaction count before pruning, assert value
+ block1_details = node.getblock(node.getblockhash(1))
+ assert_equal(block1_details["nTx"], len(block1_details["tx"]))
+
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
node.generate(6)
assert_equal(node.getblockchaininfo()["blocks"], 1001)
+ # Pruned block should still know the number of transactions
+ assert_equal(node.getblockheader(node.getblockhash(1))["nTx"], block1_details["nTx"])
+
# negative heights should raise an exception
assert_raises_rpc_error(-8, "Negative", node.pruneblockchain, -10)
diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py
index cb4c9867a3..37849e9213 100755
--- a/test/functional/p2p_compactblocks.py
+++ b/test/functional/p2p_compactblocks.py
@@ -87,7 +87,7 @@ class TestP2PConn(P2PInterface):
This is used when we want to send a message into the node that we expect
will get us disconnected, eg an invalid block."""
self.send_message(message)
- wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock)
+ wait_until(lambda: not self.is_connected, timeout=timeout, lock=mininode_lock)
class CompactBlocksTest(BitcoinTestFramework):
def set_test_params(self):
diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py
index 198dcc1490..dca5ea39de 100755
--- a/test/functional/p2p_leak.py
+++ b/test/functional/p2p_leak.py
@@ -118,11 +118,11 @@ class P2PLeakTest(BitcoinTestFramework):
time.sleep(5)
#This node should have been banned
- assert no_version_bannode.state != "connected"
+ assert not no_version_bannode.is_connected
# These nodes should have been disconnected
- assert unsupported_service_bit5_node.state != "connected"
- assert unsupported_service_bit7_node.state != "connected"
+ assert not unsupported_service_bit5_node.is_connected
+ assert not unsupported_service_bit7_node.is_connected
self.nodes[0].disconnect_p2ps()
diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py
index 095cc4b734..7ee8168e2f 100755
--- a/test/functional/p2p_sendheaders.py
+++ b/test/functional/p2p_sendheaders.py
@@ -288,6 +288,7 @@ class SendHeadersTest(BitcoinTestFramework):
# 1. Mine a block; expect inv announcements each time
self.log.info("Part 1: headers don't start before sendheaders message...")
for i in range(4):
+ self.log.debug("Part 1.{}: starting...".format(i))
old_tip = tip
tip = self.mine_blocks(1)
inv_node.check_last_inv_announcement(inv=[tip])
@@ -335,11 +336,13 @@ class SendHeadersTest(BitcoinTestFramework):
height = self.nodes[0].getblockcount() + 1
block_time += 10 # Advance far enough ahead
for i in range(10):
+ self.log.debug("Part 2.{}: starting...".format(i))
# Mine i blocks, and alternate announcing either via
# inv (of tip) or via headers. After each, new blocks
# mined by the node should successfully be announced
# with block header, even though the blocks are never requested
for j in range(2):
+ self.log.debug("Part 2.{}.{}: starting...".format(i, j))
blocks = []
for b in range(i + 1):
blocks.append(create_block(tip, create_coinbase(height), block_time))
@@ -386,6 +389,7 @@ class SendHeadersTest(BitcoinTestFramework):
# PART 3. Headers announcements can stop after large reorg, and resume after
# getheaders or inv from peer.
for j in range(2):
+ self.log.debug("Part 3.{}: starting...".format(j))
# First try mining a reorg that can propagate with header announcement
new_block_hashes = self.mine_reorg(length=7)
tip = new_block_hashes[-1]
@@ -412,23 +416,28 @@ class SendHeadersTest(BitcoinTestFramework):
test_node.wait_for_block(new_block_hashes[-1])
for i in range(3):
+ self.log.debug("Part 3.{}.{}: starting...".format(j, i))
+
# Mine another block, still should get only an inv
tip = self.mine_blocks(1)
inv_node.check_last_inv_announcement(inv=[tip])
test_node.check_last_inv_announcement(inv=[tip])
if i == 0:
- self.log.debug("Just get the data -- shouldn't cause headers announcements to resume")
+ # Just get the data -- shouldn't cause headers announcements to resume
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 1:
- self.log.debug("Send a getheaders message that shouldn't trigger headers announcements to resume (best header sent will be too old)")
+ # Send a getheaders message that shouldn't trigger headers announcements
+ # to resume (best header sent will be too old)
test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1])
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
elif i == 2:
+ # This time, try sending either a getheaders to trigger resumption
+ # of headers announcements, or mine a new block and inv it, also
+ # triggering resumption of headers announcements.
test_node.send_get_data([tip])
test_node.wait_for_block(tip)
- self.log.debug("This time, try sending either a getheaders to trigger resumption of headers announcements, or mine a new block and inv it, also triggering resumption of headers announcements.")
if j == 0:
test_node.send_get_headers(locator=[tip], hashstop=0)
test_node.sync_with_ping()
@@ -532,6 +541,7 @@ class SendHeadersTest(BitcoinTestFramework):
# First we test that receipt of an unconnecting header doesn't prevent
# chain sync.
for i in range(10):
+ self.log.debug("Part 5.{}: starting...".format(i))
test_node.last_message.pop("getdata", None)
blocks = []
# Create two more blocks.
diff --git a/test/functional/p2p_timeouts.py b/test/functional/p2p_timeouts.py
index 6a21b693b4..e958536cf9 100755
--- a/test/functional/p2p_timeouts.py
+++ b/test/functional/p2p_timeouts.py
@@ -47,9 +47,9 @@ class TimeoutsTest(BitcoinTestFramework):
sleep(1)
- assert no_verack_node.connected
- assert no_version_node.connected
- assert no_send_node.connected
+ assert no_verack_node.is_connected
+ assert no_version_node.is_connected
+ assert no_send_node.is_connected
no_verack_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
@@ -58,18 +58,18 @@ class TimeoutsTest(BitcoinTestFramework):
assert "version" in no_verack_node.last_message
- assert no_verack_node.connected
- assert no_version_node.connected
- assert no_send_node.connected
+ assert no_verack_node.is_connected
+ assert no_version_node.is_connected
+ assert no_send_node.is_connected
no_verack_node.send_message(msg_ping())
no_version_node.send_message(msg_ping())
sleep(31)
- assert not no_verack_node.connected
- assert not no_version_node.connected
- assert not no_send_node.connected
+ assert not no_verack_node.is_connected
+ assert not no_version_node.is_connected
+ assert not no_send_node.is_connected
if __name__ == '__main__':
TimeoutsTest().main()
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 17e24453e5..7acc59c2c6 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -217,6 +217,7 @@ class BlockchainTest(BitcoinTestFramework):
assert_equal(header['confirmations'], 1)
assert_equal(header['previousblockhash'], secondbesthash)
assert_is_hex_string(header['chainwork'])
+ assert_equal(header['nTx'], 1)
assert_is_hash_string(header['hash'])
assert_is_hash_string(header['previousblockhash'])
assert_is_hash_string(header['merkleroot'])
diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py
index 060a2373b1..f573faaf17 100755
--- a/test/functional/rpc_getblockstats.py
+++ b/test/functional/rpc_getblockstats.py
@@ -81,11 +81,11 @@ class GetblockstatsTest(BitcoinTestFramework):
'mocktime': int(mocktime),
'stats': self.expected_stats,
}
- with open(filename, 'w') as f:
+ with open(filename, 'w', encoding="utf8") as f:
json.dump(to_dump, f, sort_keys=True, indent=2)
def load_test_data(self, filename):
- with open(filename, 'r') as f:
+ with open(filename, 'r', encoding="utf8") as f:
d = json.load(f)
blocks = d['blocks']
mocktime = d['mocktime']
diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py
index 7c2125a177..5859f108a4 100755
--- a/test/functional/test_framework/mininode.py
+++ b/test/functional/test_framework/mininode.py
@@ -77,6 +77,12 @@ class P2PConnection(asyncore.dispatcher):
super().__init__(map=mininode_socket_map)
+ self._conn_open = False
+
+ @property
+ def is_connected(self):
+ return self._conn_open
+
def peer_connect(self, dstaddr, dstport, net="regtest"):
self.dstaddr = dstaddr
self.dstport = dstport
@@ -84,7 +90,7 @@ class P2PConnection(asyncore.dispatcher):
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.sendbuf = b""
self.recvbuf = b""
- self.state = "connecting"
+ self._asyncore_pre_connection = True
self.network = net
self.disconnect = False
@@ -97,22 +103,23 @@ class P2PConnection(asyncore.dispatcher):
def peer_disconnect(self):
# Connection could have already been closed by other end.
- if self.state == "connected":
- self.disconnect_node()
+ if self.is_connected:
+ self.disconnect = True # Signal asyncore to disconnect
# Connection and disconnection methods
def handle_connect(self):
"""asyncore callback when a connection is opened."""
- if self.state != "connected":
+ if not self.is_connected:
logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "connected"
+ self._conn_open = True
+ self._asyncore_pre_connection = False
self.on_open()
def handle_close(self):
"""asyncore callback when a connection is closed."""
logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport))
- self.state = "closed"
+ self._conn_open = False
self.recvbuf = b""
self.sendbuf = b""
try:
@@ -121,13 +128,6 @@ class P2PConnection(asyncore.dispatcher):
pass
self.on_close()
- def disconnect_node(self):
- """Disconnect the p2p connection.
-
- Called by the test logic thread. Causes the p2p connection
- to be disconnected on the next iteration of the asyncore loop."""
- self.disconnect = True
-
# Socket read methods
def handle_read(self):
@@ -182,9 +182,8 @@ class P2PConnection(asyncore.dispatcher):
def writable(self):
"""asyncore method to determine whether the handle_write() callback should be called on the next loop."""
with mininode_lock:
- pre_connection = self.state == "connecting"
length = len(self.sendbuf)
- return (length > 0 or pre_connection)
+ return length > 0 or self._asyncore_pre_connection
def handle_write(self):
"""asyncore callback when data should be written to the socket."""
@@ -192,7 +191,7 @@ class P2PConnection(asyncore.dispatcher):
# asyncore does not expose socket connection, only the first read/write
# event, thus we must check connection manually here to know when we
# actually connect
- if self.state == "connecting":
+ if self._asyncore_pre_connection:
self.handle_connect()
if not self.writable():
return
@@ -204,26 +203,17 @@ class P2PConnection(asyncore.dispatcher):
return
self.sendbuf = self.sendbuf[sent:]
- def send_message(self, message, pushbuf=False):
+ def send_message(self, message):
"""Send a P2P message over the socket.
This method takes a P2P payload, builds the P2P header and adds
the message to the send buffer to be sent over the socket."""
- if self.state != "connected" and not pushbuf:
- raise IOError('Not connected, no pushbuf')
+ if not self.is_connected:
+ raise IOError('Not connected')
self._log_message("send", message)
- command = message.command
- data = message.serialize()
- tmsg = MAGIC_BYTES[self.network]
- tmsg += command
- tmsg += b"\x00" * (12 - len(command))
- tmsg += struct.pack("<I", len(data))
- th = sha256(data)
- h = sha256(th)
- tmsg += h[:4]
- tmsg += data
+ tmsg = self._build_message(message)
with mininode_lock:
- if (len(self.sendbuf) == 0 and not pushbuf):
+ if len(self.sendbuf) == 0:
try:
sent = self.send(tmsg)
self.sendbuf = tmsg[sent:]
@@ -234,6 +224,20 @@ class P2PConnection(asyncore.dispatcher):
# Class utility methods
+ def _build_message(self, message):
+ """Build a serialized P2P message"""
+ command = message.command
+ data = message.serialize()
+ tmsg = MAGIC_BYTES[self.network]
+ tmsg += command
+ tmsg += b"\x00" * (12 - len(command))
+ tmsg += struct.pack("<I", len(data))
+ th = sha256(data)
+ h = sha256(th)
+ tmsg += h[:4]
+ tmsg += data
+ return tmsg
+
def _log_message(self, direction, msg):
"""Logs a message being sent or received over the connection."""
if direction == "send":
@@ -280,7 +284,7 @@ class P2PInterface(P2PConnection):
vt.addrTo.port = self.dstport
vt.addrFrom.ip = "0.0.0.0"
vt.addrFrom.port = 0
- self.send_message(vt, True)
+ self.sendbuf = self._build_message(vt) # Will be sent right after handle_connect
# Message receiving methods
@@ -348,7 +352,7 @@ class P2PInterface(P2PConnection):
# Connection helper methods
def wait_for_disconnect(self, timeout=60):
- test_function = lambda: self.state != "connected"
+ test_function = lambda: not self.is_connected
wait_until(test_function, timeout=timeout, lock=mininode_lock)
# Message receiving helper methods
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index e016148f70..5e0b61b5e7 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -327,7 +327,7 @@ def get_auth_cookie(datadir):
assert password is None # Ensure that there is only one rpcpassword line
password = line.split("=")[1].strip("\n")
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
- with open(os.path.join(datadir, "regtest", ".cookie"), 'r') as f:
+ with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="ascii") as f:
userpass = f.read()
split_userpass = userpass.split(':')
user = split_userpass[0]
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 5b3a4df0f9..36101d9f57 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -213,7 +213,7 @@ def main():
# Read config generated by configure.
config = configparser.ConfigParser()
configfile = os.path.abspath(os.path.dirname(__file__)) + "/../config.ini"
- config.read_file(open(configfile))
+ config.read_file(open(configfile, encoding="utf8"))
passon_args.append("--configfile=%s" % configfile)
@@ -590,7 +590,7 @@ class RPCCoverage():
if not os.path.isfile(coverage_ref_filename):
raise RuntimeError("No coverage reference found")
- with open(coverage_ref_filename, 'r') as coverage_ref_file:
+ with open(coverage_ref_filename, 'r', encoding="utf8") as coverage_ref_file:
all_cmds.update([line.strip() for line in coverage_ref_file.readlines()])
for root, dirs, files in os.walk(self.dir):
@@ -599,7 +599,7 @@ class RPCCoverage():
coverage_filenames.add(os.path.join(root, filename))
for filename in coverage_filenames:
- with open(filename, 'r') as coverage_file:
+ with open(filename, 'r', encoding="utf8") as coverage_file:
covered_cmds.update([line.strip() for line in coverage_file.readlines()])
return all_cmds - covered_cmds
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 9c58b84819..d6a64eaefb 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -129,6 +129,15 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].lockunspent, False,
[{"txid": unspent_0["txid"], "vout": 999}])
+ # An output should be unlocked when spent
+ unspent_0 = self.nodes[1].listunspent()[0]
+ self.nodes[1].lockunspent(False, [unspent_0])
+ tx = self.nodes[1].createrawtransaction([unspent_0], { self.nodes[1].getnewaddress() : 1 })
+ tx = self.nodes[1].fundrawtransaction(tx)['hex']
+ tx = self.nodes[1].signrawtransactionwithwallet(tx)["hex"]
+ self.nodes[1].sendrawtransaction(tx)
+ assert_equal(len(self.nodes[1].listlockunspent()), 0)
+
# Have node1 generate 100 blocks (so node0 can recover the fee)
self.nodes[1].generate(100)
self.sync_all([self.nodes[0:3]])
diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py
index e9cd052344..cc0a5175d5 100755
--- a/test/functional/wallet_fallbackfee.py
+++ b/test/functional/wallet_fallbackfee.py
@@ -21,7 +21,6 @@ class WalletRBFTest(BitcoinTestFramework):
self.restart_node(0, extra_args=["-fallbackfee=0"])
assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1))
assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].fundrawtransaction(self.nodes[0].createrawtransaction([], {self.nodes[0].getnewaddress(): 1})))
- assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].sendfrom("", self.nodes[0].getnewaddress(), 1))
assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendmany("", {self.nodes[0].getnewaddress(): 1}))
if __name__ == '__main__':
diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py
index f5d7830fca..8d961fb34a 100755
--- a/test/functional/wallet_labels.py
+++ b/test/functional/wallet_labels.py
@@ -85,7 +85,8 @@ class WalletLabelsTest(BitcoinTestFramework):
# we want to reset so that the "" label has what's expected.
# otherwise we're off by exactly the fee amount as that's mined
# and matures in the next 100 blocks
- node.sendfrom("", common_address, fee)
+ if accounts_api:
+ node.sendfrom("", common_address, fee)
amount_to_send = 1.0
# Create labels and make sure subsequent label API calls
@@ -125,7 +126,7 @@ class WalletLabelsTest(BitcoinTestFramework):
if accounts_api:
node.sendfrom(label.name, to_label.receive_address, amount_to_send)
else:
- node.sendfrom(label.name, to_label.addresses[0], amount_to_send)
+ node.sendtoaddress(to_label.addresses[0], amount_to_send)
node.generate(1)
for label in labels:
if accounts_api:
@@ -166,7 +167,8 @@ class WalletLabelsTest(BitcoinTestFramework):
label.add_address(multisig_address)
label.purpose[multisig_address] = "send"
label.verify(node)
- node.sendfrom("", multisig_address, 50)
+ if accounts_api:
+ node.sendfrom("", multisig_address, 50)
node.generate(101)
if accounts_api:
for label in labels:
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index 53638615f6..3cefd83459 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -88,7 +88,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
# should not initialize if the specified walletdir is not a directory
not_a_dir = wallet_dir('notadir')
- open(not_a_dir, 'a').close()
+ open(not_a_dir, 'a', encoding="utf8").close()
self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
self.log.info("Do not allow -zapwallettxes with multiwallet")
@@ -234,5 +234,32 @@ class MultiWalletTest(BitcoinTestFramework):
assert new_wallet_name in self.nodes[0].listwallets()
+ self.log.info("Test dynamic wallet unloading")
+
+ # Test `unloadwallet` errors
+ assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].unloadwallet)
+ assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", self.nodes[0].unloadwallet, "dummy")
+ assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.get_wallet_rpc("dummy").unloadwallet)
+ assert_raises_rpc_error(-8, "Cannot unload the requested wallet", w1.unloadwallet, "w2"),
+
+ # Successfully unload the specified wallet name
+ self.nodes[0].unloadwallet("w1")
+ assert 'w1' not in self.nodes[0].listwallets()
+
+ # Successfully unload the wallet referenced by the request endpoint
+ w2.unloadwallet()
+ assert 'w2' not in self.nodes[0].listwallets()
+
+ # Successfully unload all wallets
+ for wallet_name in self.nodes[0].listwallets():
+ self.nodes[0].unloadwallet(wallet_name)
+ assert_equal(self.nodes[0].listwallets(), [])
+ assert_raises_rpc_error(-32601, "Method not found (wallet method is disabled because no wallet is loaded)", self.nodes[0].getwalletinfo)
+
+ # Successfully load a previously unloaded wallet
+ self.nodes[0].loadwallet('w1')
+ assert_equal(self.nodes[0].listwallets(), ['w1'])
+ assert_equal(w1.getwalletinfo()['walletname'], 'w1')
+
if __name__ == '__main__':
MultiWalletTest().main()
diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py
index 7e96852c5c..c3cdeef580 100755
--- a/test/lint/check-rpc-mappings.py
+++ b/test/lint/check-rpc-mappings.py
@@ -44,7 +44,7 @@ def process_commands(fname):
"""Find and parse dispatch table in implementation file `fname`."""
cmds = []
in_rpcs = False
- with open(fname, "r") as f:
+ with open(fname, "r", encoding="utf8") as f:
for line in f:
line = line.rstrip()
if not in_rpcs:
@@ -70,7 +70,7 @@ def process_mapping(fname):
"""Find and parse conversion table in implementation file `fname`."""
cmds = []
in_rpcs = False
- with open(fname, "r") as f:
+ with open(fname, "r", encoding="utf8") as f:
for line in f:
line = line.rstrip()
if not in_rpcs:
diff --git a/test/lint/commit-script-check.sh b/test/lint/commit-script-check.sh
index 1c9dbc7f68..f1327469f3 100755
--- a/test/lint/commit-script-check.sh
+++ b/test/lint/commit-script-check.sh
@@ -11,6 +11,7 @@
# The resulting script should exactly transform the previous commit into the current
# one. Any remaining diff signals an error.
+export LC_ALL=C
if test "x$1" = "x"; then
echo "Usage: $0 <commit>..."
exit 1
diff --git a/test/lint/git-subtree-check.sh b/test/lint/git-subtree-check.sh
index 184951715e..85e8b841b6 100755
--- a/test/lint/git-subtree-check.sh
+++ b/test/lint/git-subtree-check.sh
@@ -3,6 +3,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+export LC_ALL=C
DIR="$1"
COMMIT="$2"
if [ -z "$COMMIT" ]; then
diff --git a/test/lint/lint-all.sh b/test/lint/lint-all.sh
index b6d86959c6..7c4f96cb3b 100755
--- a/test/lint/lint-all.sh
+++ b/test/lint/lint-all.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -7,6 +7,10 @@
# This script runs all contrib/devtools/lint-*.sh files, and fails if any exit
# with a non-zero status code.
+# This script is intentionally locale dependent by not setting "export LC_ALL=C"
+# in order to allow for the executed lint scripts to opt in or opt out of locale
+# dependence themselves.
+
set -u
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
diff --git a/test/lint/lint-filenames.sh b/test/lint/lint-filenames.sh
new file mode 100755
index 0000000000..5391e43d91
--- /dev/null
+++ b/test/lint/lint-filenames.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Make sure only lowercase alphanumerics (a-z0-9), underscores (_),
+# hyphens (-) and dots (.) are used in source code filenames.
+
+export LC_ALL=C
+
+EXIT_CODE=0
+OUTPUT=$(git ls-files --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]" | \
+ grep -vE '^[a-z0-9_./-]+$' | \
+ grep -vE '^src/(secp256k1|univalue)/')
+
+if [[ ${OUTPUT} != "" ]]; then
+ echo "Use only lowercase alphanumerics (a-z0-9), underscores (_), hyphens (-) and dots (.)"
+ echo "in source code filenames:"
+ echo
+ echo "${OUTPUT}"
+ EXIT_CODE=1
+fi
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-include-guards.sh b/test/lint/lint-include-guards.sh
index 6a0dd556bb..464969794b 100755
--- a/test/lint/lint-include-guards.sh
+++ b/test/lint/lint-include-guards.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -6,6 +6,7 @@
#
# Check include guards.
+export LC_ALL=C
HEADER_ID_PREFIX="BITCOIN_"
HEADER_ID_SUFFIX="_H"
diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh
index f5daf4f9ac..fdc3f5bd35 100755
--- a/test/lint/lint-includes.sh
+++ b/test/lint/lint-includes.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -8,6 +8,7 @@
# Guard against accidental introduction of new Boost dependencies.
# Check includes: Check for duplicate includes. Enforce bracket syntax includes.
+export LC_ALL=C
IGNORE_REGEXP="/(leveldb|secp256k1|univalue)/"
filter_suffix() {
diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh
index 3144f2c841..cbe1143bd0 100755
--- a/test/lint/lint-locale-dependence.sh
+++ b/test/lint/lint-locale-dependence.sh
@@ -1,5 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
+export LC_ALL=C
KNOWN_VIOLATIONS=(
"src/base58.cpp:.*isspace"
"src/bitcoin-tx.cpp.*stoul"
diff --git a/test/lint/lint-logs.sh b/test/lint/lint-logs.sh
index 35be13ec19..1afd4cfc1a 100755
--- a/test/lint/lint-logs.sh
+++ b/test/lint/lint-logs.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -12,7 +12,7 @@
# There are some instances of LogPrintf() in comments. Those can be
# ignored
-
+export LC_ALL=C
UNTERMINATED_LOGS=$(git grep --extended-regexp "LogPrintf?\(" -- "*.cpp" | \
grep -v '\\n"' | \
grep -v "/\* Continued \*/" | \
diff --git a/test/lint/lint-python-shebang.sh b/test/lint/lint-python-shebang.sh
index f5c5971c03..4ff87f0bf7 100755
--- a/test/lint/lint-python-shebang.sh
+++ b/test/lint/lint-python-shebang.sh
@@ -1,5 +1,7 @@
-#!/bin/bash
+#!/usr/bin/env bash
# Shebang must use python3 (not python or python2)
+
+export LC_ALL=C
EXIT_CODE=0
for PYTHON_FILE in $(git ls-files -- "*.py"); do
if [[ $(head -c 2 "${PYTHON_FILE}") == "#!" &&
diff --git a/test/lint/lint-python-utf8-encoding.sh b/test/lint/lint-python-utf8-encoding.sh
new file mode 100755
index 0000000000..14183a5ccf
--- /dev/null
+++ b/test/lint/lint-python-utf8-encoding.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Make sure we explicitly open all text files using UTF-8 (or ASCII) encoding to
+# avoid potential issues on the BSDs where the locale is not always set.
+
+export LC_ALL=C
+EXIT_CODE=0
+OUTPUT=$(git grep " open(" -- "*.py" | grep -vE "encoding=.(ascii|utf8|utf-8)." | grep -vE "open\([^,]*, ['\"][^'\"]*b[^'\"]*['\"]")
+if [[ ${OUTPUT} != "" ]]; then
+ echo "Python's open(...) seems to be used to open text files without explicitly"
+ echo "specifying encoding=\"utf8\":"
+ echo
+ echo "${OUTPUT}"
+ EXIT_CODE=1
+fi
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh
index 7d3555b6d4..d9d46d86d5 100755
--- a/test/lint/lint-python.sh
+++ b/test/lint/lint-python.sh
@@ -6,6 +6,8 @@
#
# Check for specified flake8 warnings in python files.
+export LC_ALL=C
+
# E101 indentation contains mixed spaces and tabs
# E112 expected an indented block
# E113 unexpected indentation
diff --git a/test/lint/lint-shell-locale.sh b/test/lint/lint-shell-locale.sh
new file mode 100755
index 0000000000..242b27c763
--- /dev/null
+++ b/test/lint/lint-shell-locale.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+# Make sure all shell scripts:
+# a.) explicitly opt out of locale dependence using "export LC_ALL=C", or
+# b.) explicitly opt in to locale dependence using the annotation below.
+
+export LC_ALL=C
+
+EXIT_CODE=0
+for SHELL_SCRIPT in $(git ls-files -- "*.sh" | grep -vE "src/(secp256k1|univalue)/"); do
+ if grep -q "# This script is intentionally locale dependent by not setting \"export LC_ALL=C\"" "${SHELL_SCRIPT}"; then
+ continue
+ fi
+ FIRST_NON_COMMENT_LINE=$(grep -vE '^(#.*|)$' "${SHELL_SCRIPT}" | head -1)
+ if [[ ${FIRST_NON_COMMENT_LINE} != "export LC_ALL=C" ]]; then
+ echo "Missing \"export LC_ALL=C\" (to avoid locale dependence) as first non-comment non-empty line in ${SHELL_SCRIPT}"
+ EXIT_CODE=1
+ fi
+done
+exit ${EXIT_CODE}
diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh
index 5f5fa9a925..e2ccdb5165 100755
--- a/test/lint/lint-shell.sh
+++ b/test/lint/lint-shell.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -6,6 +6,10 @@
#
# Check for shellcheck warnings in shell scripts.
+# This script is intentionally locale dependent by not setting "export LC_ALL=C"
+# to allow running certain versions of shellcheck that core dump when LC_ALL=C
+# is set.
+
# Disabled warnings:
# SC2001: See if you can use ${variable//search/replace} instead.
# SC2004: $/${} is unnecessary on arithmetic variables.
diff --git a/test/lint/lint-tests.sh b/test/lint/lint-tests.sh
index ffc0660551..35d11023eb 100755
--- a/test/lint/lint-tests.sh
+++ b/test/lint/lint-tests.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -6,6 +6,7 @@
#
# Check the test suite naming conventions
+export LC_ALL=C
EXIT_CODE=0
NAMING_INCONSISTENCIES=$(git grep -E '^BOOST_FIXTURE_TEST_SUITE\(' -- \
diff --git a/test/lint/lint-whitespace.sh b/test/lint/lint-whitespace.sh
index c5d43043d5..beb7ec42f4 100755
--- a/test/lint/lint-whitespace.sh
+++ b/test/lint/lint-whitespace.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
#
# Copyright (c) 2017 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
@@ -8,6 +8,7 @@
# We can't run this check unless we know the commit range for the PR.
+export LC_ALL=C
while getopts "?" opt; do
case $opt in
?)
diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py
index 30bd13d0dc..16371a6234 100755
--- a/test/util/bitcoin-util-test.py
+++ b/test/util/bitcoin-util-test.py
@@ -28,7 +28,7 @@ import sys
def main():
config = configparser.ConfigParser()
config.optionxform = str
- config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini")))
+ config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"), encoding="utf8"))
env_conf = dict(config.items('environment'))
parser = argparse.ArgumentParser(description=__doc__)
@@ -49,7 +49,7 @@ def main():
def bctester(testDir, input_basename, buildenv):
""" Loads and parses the input file, runs all tests and reports results"""
input_filename = os.path.join(testDir, input_basename)
- raw_data = open(input_filename).read()
+ raw_data = open(input_filename, encoding="utf8").read()
input_data = json.loads(raw_data)
failed_testcases = []
@@ -86,7 +86,7 @@ def bctest(testDir, testObj, buildenv):
inputData = None
if "input" in testObj:
filename = os.path.join(testDir, testObj["input"])
- inputData = open(filename).read()
+ inputData = open(filename, encoding="utf8").read()
stdinCfg = subprocess.PIPE
# Read the expected output data (if there is any)
@@ -97,7 +97,7 @@ def bctest(testDir, testObj, buildenv):
outputFn = testObj['output_cmp']
outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
try:
- outputData = open(os.path.join(testDir, outputFn)).read()
+ outputData = open(os.path.join(testDir, outputFn), encoding="utf8").read()
except:
logging.error("Output file " + outputFn + " can not be opened")
raise
diff --git a/test/util/rpcauth-test.py b/test/util/rpcauth-test.py
index 2456feb102..46e9fbc739 100755
--- a/test/util/rpcauth-test.py
+++ b/test/util/rpcauth-test.py
@@ -18,7 +18,7 @@ class TestRPCAuth(unittest.TestCase):
config_path = os.path.abspath(
os.path.join(os.sep, os.path.abspath(os.path.dirname(__file__)),
"../config.ini"))
- with open(config_path) as config_file:
+ with open(config_path, encoding="utf8") as config_file:
config.read_file(config_file)
sys.path.insert(0, os.path.dirname(config['environment']['RPCAUTH']))
self.rpcauth = importlib.import_module('rpcauth')