Merge bitcoin/bitcoin#27944: test: various USDT functional test cleanups (27831 follow-ups)

9f55773a37 test: refactor: usdt_mempool: store all events (stickies-v)
bc43270450 test: refactor: remove unnecessary nonlocal (stickies-v)
326db63a68 test: log sanity check assertion failures (stickies-v)
f5525ad680 test: store utxocache events (stickies-v)
f1b99ac94f test: refactor: deduplicate handle_utxocache_* logic (stickies-v)
ad90ba36bd test: refactor:  rename inbound to is_inbound (stickies-v)
afc0224cdb test: refactor: remove unnecessary blocks_checked counter (stickies-v)

Pull request description:

  Various cleanups to the USDT functional tests, largely (but not exclusively) follow-ups to https://github.com/bitcoin/bitcoin/pull/27831#pullrequestreview-1491438045. Except for slightly different logging behaviour in "test: store utxocache events" and "test: log sanity check assertion failures", this is a refactor PR, removing unnecessary code and (imo) making it more readable and maintainable.

  The rationale for each change is in the corresponding commit message.

  Note: except for "test: store utxocache events" (which relies on its parent, and I separated into two commits because we may want the parent but not the child), all commits are stand-alone and I'm okay with dropping one/multiple commits if they turn out to be controversial or undesired.

ACKs for top commit:
  0xB10C:
    ACK 9f55773a37. Reviewed the code and ran the USDT interface tests. I stepped through the commits and think all changes are reasonable.

Tree-SHA512: 6c37a0265b6c26d4f9552a056a690b8f86f7304bd33b4419febd8b17369cf6af799cb87c16df35d0c2a1b839ad31de24661d4384eafa88816c2051c522fd3bf5
This commit is contained in:
fanquake 2023-09-10 14:06:17 +01:00
commit c5a63ea56f
No known key found for this signature in database
GPG key ID: 2EEB9F5CC09526C1
4 changed files with 51 additions and 77 deletions

View file

@ -137,9 +137,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
"""Add a transaction to the mempool and make sure the tracepoint returns """Add a transaction to the mempool and make sure the tracepoint returns
the expected txid, vsize, and fee.""" the expected txid, vsize, and fee."""
EXPECTED_ADDED_EVENTS = 1 events = []
handled_added_events = 0
event = None
self.log.info("Hooking into mempool:added tracepoint...") self.log.info("Hooking into mempool:added tracepoint...")
node = self.nodes[0] node = self.nodes[0]
@ -148,9 +146,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_added_event(_, data, __): def handle_added_event(_, data, __):
nonlocal event, handled_added_events events.append(bpf["added_events"].event(data))
event = bpf["added_events"].event(data)
handled_added_events += 1
bpf["added_events"].open_perf_buffer(handle_added_event) bpf["added_events"].open_perf_buffer(handle_added_event)
@ -165,7 +161,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
self.generate(node, 1) self.generate(node, 1)
self.log.info("Ensuring mempool:added event was handled successfully...") self.log.info("Ensuring mempool:added event was handled successfully...")
assert_equal(EXPECTED_ADDED_EVENTS, handled_added_events) assert_equal(1, len(events))
event = events[0]
assert_equal(bytes(event.hash)[::-1].hex(), tx["txid"]) assert_equal(bytes(event.hash)[::-1].hex(), tx["txid"])
assert_equal(event.vsize, tx["tx"].get_vsize()) assert_equal(event.vsize, tx["tx"].get_vsize())
assert_equal(event.fee, fee) assert_equal(event.fee, fee)
@ -177,9 +174,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
"""Expire a transaction from the mempool and make sure the tracepoint returns """Expire a transaction from the mempool and make sure the tracepoint returns
the expected txid, expiry reason, vsize, and fee.""" the expected txid, expiry reason, vsize, and fee."""
EXPECTED_REMOVED_EVENTS = 1 events = []
handled_removed_events = 0
event = None
self.log.info("Hooking into mempool:removed tracepoint...") self.log.info("Hooking into mempool:removed tracepoint...")
node = self.nodes[0] node = self.nodes[0]
@ -188,9 +183,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_removed_event(_, data, __): def handle_removed_event(_, data, __):
nonlocal event, handled_removed_events events.append(bpf["removed_events"].event(data))
event = bpf["removed_events"].event(data)
handled_removed_events += 1
bpf["removed_events"].open_perf_buffer(handle_removed_event) bpf["removed_events"].open_perf_buffer(handle_removed_event)
@ -212,7 +205,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf.perf_buffer_poll(timeout=200) bpf.perf_buffer_poll(timeout=200)
self.log.info("Ensuring mempool:removed event was handled successfully...") self.log.info("Ensuring mempool:removed event was handled successfully...")
assert_equal(EXPECTED_REMOVED_EVENTS, handled_removed_events) assert_equal(1, len(events))
event = events[0]
assert_equal(bytes(event.hash)[::-1].hex(), txid) assert_equal(bytes(event.hash)[::-1].hex(), txid)
assert_equal(event.reason.decode("UTF-8"), "expiry") assert_equal(event.reason.decode("UTF-8"), "expiry")
assert_equal(event.vsize, tx["tx"].get_vsize()) assert_equal(event.vsize, tx["tx"].get_vsize())
@ -226,9 +220,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
"""Replace one and two transactions in the mempool and make sure the tracepoint """Replace one and two transactions in the mempool and make sure the tracepoint
returns the expected txids, vsizes, and fees.""" returns the expected txids, vsizes, and fees."""
EXPECTED_REPLACED_EVENTS = 1 events = []
handled_replaced_events = 0
event = None
self.log.info("Hooking into mempool:replaced tracepoint...") self.log.info("Hooking into mempool:replaced tracepoint...")
node = self.nodes[0] node = self.nodes[0]
@ -237,9 +229,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_replaced_event(_, data, __): def handle_replaced_event(_, data, __):
nonlocal event, handled_replaced_events events.append(bpf["replaced_events"].event(data))
event = bpf["replaced_events"].event(data)
handled_replaced_events += 1
bpf["replaced_events"].open_perf_buffer(handle_replaced_event) bpf["replaced_events"].open_perf_buffer(handle_replaced_event)
@ -261,7 +251,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf.perf_buffer_poll(timeout=200) bpf.perf_buffer_poll(timeout=200)
self.log.info("Ensuring mempool:replaced event was handled successfully...") self.log.info("Ensuring mempool:replaced event was handled successfully...")
assert_equal(EXPECTED_REPLACED_EVENTS, handled_replaced_events) assert_equal(1, len(events))
event = events[0]
assert_equal(bytes(event.replaced_hash)[::-1].hex(), original_tx["txid"]) assert_equal(bytes(event.replaced_hash)[::-1].hex(), original_tx["txid"])
assert_equal(event.replaced_vsize, original_tx["tx"].get_vsize()) assert_equal(event.replaced_vsize, original_tx["tx"].get_vsize())
assert_equal(event.replaced_fee, original_fee) assert_equal(event.replaced_fee, original_fee)
@ -277,9 +268,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
"""Create an invalid transaction and make sure the tracepoint returns """Create an invalid transaction and make sure the tracepoint returns
the expected txid, rejection reason, peer id, and peer address.""" the expected txid, rejection reason, peer id, and peer address."""
EXPECTED_REJECTED_EVENTS = 1 events = []
handled_rejected_events = 0
event = None
self.log.info("Adding P2P connection...") self.log.info("Adding P2P connection...")
node = self.nodes[0] node = self.nodes[0]
@ -291,9 +280,7 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0) bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0)
def handle_rejected_event(_, data, __): def handle_rejected_event(_, data, __):
nonlocal event, handled_rejected_events events.append(bpf["rejected_events"].event(data))
event = bpf["rejected_events"].event(data)
handled_rejected_events += 1
bpf["rejected_events"].open_perf_buffer(handle_rejected_event) bpf["rejected_events"].open_perf_buffer(handle_rejected_event)
@ -305,7 +292,8 @@ class MempoolTracepointTest(BitcoinTestFramework):
bpf.perf_buffer_poll(timeout=200) bpf.perf_buffer_poll(timeout=200)
self.log.info("Ensuring mempool:rejected event was handled successfully...") self.log.info("Ensuring mempool:rejected event was handled successfully...")
assert_equal(EXPECTED_REJECTED_EVENTS, handled_rejected_events) assert_equal(1, len(events))
event = events[0]
assert_equal(bytes(event.hash)[::-1].hex(), tx["tx"].hash) assert_equal(bytes(event.hash)[::-1].hex(), tx["tx"].hash)
# The next test is already known to fail, so disable it to avoid # The next test is already known to fail, so disable it to avoid
# wasting CPU time and developer time. See # wasting CPU time and developer time. See

View file

@ -121,11 +121,11 @@ class NetTracepointTest(BitcoinTestFramework):
checked_outbound_version_msg = 0 checked_outbound_version_msg = 0
events = [] events = []
def check_p2p_message(event, inbound): def check_p2p_message(event, is_inbound):
nonlocal checked_inbound_version_msg, checked_outbound_version_msg nonlocal checked_inbound_version_msg, checked_outbound_version_msg
if event.msg_type.decode("utf-8") == "version": if event.msg_type.decode("utf-8") == "version":
self.log.info( self.log.info(
f"check_p2p_message(): {'inbound' if inbound else 'outbound'} {event}") f"check_p2p_message(): {'inbound' if is_inbound else 'outbound'} {event}")
peer = self.nodes[0].getpeerinfo()[0] peer = self.nodes[0].getpeerinfo()[0]
msg = msg_version() msg = msg_version()
msg.deserialize(BytesIO(bytes(event.msg[:event.msg_size]))) msg.deserialize(BytesIO(bytes(event.msg[:event.msg_size])))
@ -133,13 +133,12 @@ class NetTracepointTest(BitcoinTestFramework):
assert_equal(peer["addr"], event.peer_addr.decode("utf-8")) assert_equal(peer["addr"], event.peer_addr.decode("utf-8"))
assert_equal(peer["connection_type"], assert_equal(peer["connection_type"],
event.peer_conn_type.decode("utf-8")) event.peer_conn_type.decode("utf-8"))
if inbound: if is_inbound:
checked_inbound_version_msg += 1 checked_inbound_version_msg += 1
else: else:
checked_outbound_version_msg += 1 checked_outbound_version_msg += 1
def handle_inbound(_, data, __): def handle_inbound(_, data, __):
nonlocal events
event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents event = ctypes.cast(data, ctypes.POINTER(P2PMessage)).contents
events.append((event, True)) events.append((event, True))
@ -157,8 +156,8 @@ class NetTracepointTest(BitcoinTestFramework):
self.log.info( self.log.info(
"check receipt and content of in- and outbound version messages") "check receipt and content of in- and outbound version messages")
for event, inbound in events: for event, is_inbound in events:
check_p2p_message(event, inbound) check_p2p_message(event, is_inbound)
assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG, assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG,
checked_inbound_version_msg) checked_inbound_version_msg)
assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG, assert_equal(EXPECTED_INOUTBOUND_VERSION_MSG,

View file

@ -252,43 +252,30 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
# that the handle_* functions succeeded. # that the handle_* functions succeeded.
EXPECTED_HANDLE_ADD_SUCCESS = 2 EXPECTED_HANDLE_ADD_SUCCESS = 2
EXPECTED_HANDLE_SPENT_SUCCESS = 1 EXPECTED_HANDLE_SPENT_SUCCESS = 1
handle_add_succeeds = 0
handle_spent_succeeds = 0
expected_utxocache_spents = []
expected_utxocache_adds = [] expected_utxocache_adds = []
expected_utxocache_spents = []
actual_utxocache_adds = []
actual_utxocache_spents = []
def compare_utxo_with_event(utxo, event):
"""Compare a utxo dict to the event produced by BPF"""
assert_equal(utxo["txid"], bytes(event.txid[::-1]).hex())
assert_equal(utxo["index"], event.index)
assert_equal(utxo["height"], event.height)
assert_equal(utxo["value"], event.value)
assert_equal(utxo["is_coinbase"], event.is_coinbase)
def handle_utxocache_add(_, data, __): def handle_utxocache_add(_, data, __):
nonlocal handle_add_succeeds
event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents
self.log.info(f"handle_utxocache_add(): {event}") self.log.info(f"handle_utxocache_add(): {event}")
add = expected_utxocache_adds.pop(0) actual_utxocache_adds.append(event)
try:
assert_equal(add["txid"], bytes(event.txid[::-1]).hex())
assert_equal(add["index"], event.index)
assert_equal(add["height"], event.height)
assert_equal(add["value"], event.value)
assert_equal(add["is_coinbase"], event.is_coinbase)
except AssertionError:
self.log.exception("Assertion failed")
else:
handle_add_succeeds += 1
def handle_utxocache_spent(_, data, __): def handle_utxocache_spent(_, data, __):
nonlocal handle_spent_succeeds
event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents event = ctypes.cast(data, ctypes.POINTER(UTXOCacheChange)).contents
self.log.info(f"handle_utxocache_spent(): {event}") self.log.info(f"handle_utxocache_spent(): {event}")
spent = expected_utxocache_spents.pop(0) actual_utxocache_spents.append(event)
try:
assert_equal(spent["txid"], bytes(event.txid[::-1]).hex())
assert_equal(spent["index"], event.index)
assert_equal(spent["height"], event.height)
assert_equal(spent["value"], event.value)
assert_equal(spent["is_coinbase"], event.is_coinbase)
except AssertionError:
self.log.exception("Assertion failed")
else:
handle_spent_succeeds += 1
bpf["utxocache_add"].open_perf_buffer(handle_utxocache_add) bpf["utxocache_add"].open_perf_buffer(handle_utxocache_add)
bpf["utxocache_spent"].open_perf_buffer(handle_utxocache_spent) bpf["utxocache_spent"].open_perf_buffer(handle_utxocache_spent)
@ -324,19 +311,18 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
"is_coinbase": block_index == 0, "is_coinbase": block_index == 0,
}) })
assert_equal(EXPECTED_HANDLE_ADD_SUCCESS, len(expected_utxocache_adds))
assert_equal(EXPECTED_HANDLE_SPENT_SUCCESS,
len(expected_utxocache_spents))
bpf.perf_buffer_poll(timeout=200) bpf.perf_buffer_poll(timeout=200)
bpf.cleanup()
assert_equal(EXPECTED_HANDLE_ADD_SUCCESS, len(expected_utxocache_adds), len(actual_utxocache_adds))
assert_equal(EXPECTED_HANDLE_SPENT_SUCCESS, len(expected_utxocache_spents), len(actual_utxocache_spents))
self.log.info( self.log.info(
f"check that we successfully traced {EXPECTED_HANDLE_ADD_SUCCESS} adds and {EXPECTED_HANDLE_SPENT_SUCCESS} spent") f"check that we successfully traced {EXPECTED_HANDLE_ADD_SUCCESS} adds and {EXPECTED_HANDLE_SPENT_SUCCESS} spent")
assert_equal(0, len(expected_utxocache_adds)) for expected_utxo, actual_event in zip(expected_utxocache_adds + expected_utxocache_spents,
assert_equal(0, len(expected_utxocache_spents)) actual_utxocache_adds + actual_utxocache_spents):
assert_equal(EXPECTED_HANDLE_ADD_SUCCESS, handle_add_succeeds) compare_utxo_with_event(expected_utxo, actual_event)
assert_equal(EXPECTED_HANDLE_SPENT_SUCCESS, handle_spent_succeeds)
bpf.cleanup()
def test_flush(self): def test_flush(self):
""" Tests the utxocache:flush tracepoint API. """ Tests the utxocache:flush tracepoint API.
@ -367,9 +353,13 @@ class UTXOCacheTracepointTest(BitcoinTestFramework):
"size": event.size "size": event.size
}) })
# sanity checks only # sanity checks only
assert event.memory > 0 try:
assert event.duration > 0 assert event.memory > 0
handle_flush_succeeds += 1 assert event.duration > 0
except AssertionError:
self.log.exception("Assertion error")
else:
handle_flush_succeeds += 1
bpf["utxocache_flush"].open_perf_buffer(handle_utxocache_flush) bpf["utxocache_flush"].open_perf_buffer(handle_utxocache_flush)

View file

@ -86,7 +86,6 @@ class ValidationTracepointTest(BitcoinTestFramework):
self.duration) self.duration)
BLOCKS_EXPECTED = 2 BLOCKS_EXPECTED = 2
blocks_checked = 0
expected_blocks = dict() expected_blocks = dict()
events = [] events = []
@ -98,11 +97,9 @@ class ValidationTracepointTest(BitcoinTestFramework):
usdt_contexts=[ctx], debug=0) usdt_contexts=[ctx], debug=0)
def handle_blockconnected(_, data, __): def handle_blockconnected(_, data, __):
nonlocal events, blocks_checked
event = ctypes.cast(data, ctypes.POINTER(Block)).contents event = ctypes.cast(data, ctypes.POINTER(Block)).contents
self.log.info(f"handle_blockconnected(): {event}") self.log.info(f"handle_blockconnected(): {event}")
events.append(event) events.append(event)
blocks_checked += 1
bpf["block_connected"].open_perf_buffer( bpf["block_connected"].open_perf_buffer(
handle_blockconnected) handle_blockconnected)
@ -127,7 +124,7 @@ class ValidationTracepointTest(BitcoinTestFramework):
# only plausibility checks # only plausibility checks
assert event.duration > 0 assert event.duration > 0
del expected_blocks[block_hash] del expected_blocks[block_hash]
assert_equal(BLOCKS_EXPECTED, blocks_checked) assert_equal(BLOCKS_EXPECTED, len(events))
assert_equal(0, len(expected_blocks)) assert_equal(0, len(expected_blocks))
bpf.cleanup() bpf.cleanup()