mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-03-29 16:48:41 +08:00
allow tests to state that certain fields' values change at certain
blocks (i.e. value hash) in ways that are inconsistent with prior releases, but still correct
This commit is contained in:
@@ -74,9 +74,12 @@ def find_serialization_outputs( test_output_path ):
|
||||
Find the SERIALIZE outputs from the given test log output.
|
||||
Parse them and return the parsed list.
|
||||
Add '__block_height__' to each item (the height at which the serialization was found)
|
||||
Add '__ignore_fields__' list to each item for fields we can ignore at that height (as well as the versions for which it applies)
|
||||
"""
|
||||
ret = []
|
||||
curr_height = 0
|
||||
ignore_fields = {}
|
||||
|
||||
with open(test_output_path, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
@@ -99,19 +102,55 @@ def find_serialization_outputs( test_output_path ):
|
||||
pass
|
||||
|
||||
line = line.strip()
|
||||
if " SERIALIZE: " not in line:
|
||||
continue
|
||||
|
||||
payload_bin = extract_serialize_payload( line )
|
||||
payload = parse_serialize_payload( payload_bin )
|
||||
if line.startswith("BLOCKSTACK_SERIALIZATION_CHECK_IGNORE"):
|
||||
# format: BLOCKSTACK_SERIALIZATION_CHECK_IGNORE field
|
||||
# NOTE: since this gets printed *before* the "Snapshotting block", we'll have to use curr_height+1
|
||||
parts = line.split()
|
||||
assert len(parts) == 2
|
||||
field = parts[1]
|
||||
|
||||
payload['__block_height__'] = curr_height
|
||||
ret.append( payload )
|
||||
if not ignore_fields.has_key(curr_height+1):
|
||||
ignore_fields[curr_height+1] = []
|
||||
|
||||
ignore_fields[curr_height+1].append(field)
|
||||
|
||||
|
||||
if " SERIALIZE: " in line:
|
||||
# finished this block's data
|
||||
payload_bin = extract_serialize_payload( line )
|
||||
payload = parse_serialize_payload( payload_bin )
|
||||
|
||||
payload['__block_height__'] = curr_height
|
||||
payload['__ignore_fields__'] = ignore_fields.get(curr_height, [])[:]
|
||||
ret.append( payload )
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def serialization_eq( height, serialization_1, serialization_2, ignore=['txid','consensus_hash','name_consensus_hash','name_hash', 'vtxindex'] ):
|
||||
def find_ignore_serialization_fields( test_output_path ):
|
||||
"""
|
||||
Find any occurrences of "SERIALIZATION_FIELD_IGNORE ..."
|
||||
and return them as a CSV
|
||||
"""
|
||||
ret = []
|
||||
with open(test_output_path, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if len(line) == 0:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
if line.startswith("SERIALIZATION_CHECK_IGNORE"):
|
||||
parts = line.split(" ")
|
||||
assert len(parts) == 2
|
||||
ignore = parts[1]
|
||||
ret.append(ignore)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def serialization_eq( height, serialization_1, serialization_2, ignore=[] ):
|
||||
"""
|
||||
Given two parsed serialization payloads, verify that they represent the same data
|
||||
(ignoring fields given in @ignore)
|
||||
@@ -164,58 +203,66 @@ def group_by_block_height( serializations ):
|
||||
return ret
|
||||
|
||||
|
||||
def compare_serializations( test_output_1, test_output_2 ):
|
||||
def compare_serializations( test_output_old, test_output_new ):
|
||||
"""
|
||||
Given the paths to two different test outputs, verify
|
||||
that their sequence of SERIALIZEs match (up to txid).
|
||||
"""
|
||||
serializations_1 = find_serialization_outputs( test_output_1 )
|
||||
serializations_2 = find_serialization_outputs( test_output_2 )
|
||||
serializations_old = find_serialization_outputs( test_output_old )
|
||||
serializations_new = find_serialization_outputs( test_output_new )
|
||||
serialization_ignore = ['txid','consensus_hash','name_consensus_hash','name_hash','vtxindex']
|
||||
rc = True
|
||||
|
||||
if len(serializations_1) != len(serializations_2):
|
||||
print >> sys.stderr, " Mismatched number of serializations (%s != %s)" % (len(serializations_1), len(serializations_2))
|
||||
if len(serializations_old) != len(serializations_new):
|
||||
print >> sys.stderr, " Mismatched number of serializations (%s != %s)" % (len(serializations_old), len(serializations_new))
|
||||
return False
|
||||
|
||||
# group by block height (since bitcoind can re-order transactions)
|
||||
block_serializations_1 = group_by_block_height( serializations_1 )
|
||||
block_serializations_2 = group_by_block_height( serializations_2 )
|
||||
block_serializations_old = group_by_block_height( serializations_old )
|
||||
block_serializations_new = group_by_block_height( serializations_new )
|
||||
|
||||
for height in sorted(block_serializations_1.keys()):
|
||||
if not block_serializations_2.has_key(height):
|
||||
for height in sorted(block_serializations_old.keys()):
|
||||
if not block_serializations_new.has_key(height):
|
||||
print >> sys.stderr, " Missing block height %s in second log" % height
|
||||
return False
|
||||
|
||||
for height in sorted(block_serializations_2.keys()):
|
||||
if not block_serializations_1.has_key(height):
|
||||
for height in sorted(block_serializations_new.keys()):
|
||||
if not block_serializations_old.has_key(height):
|
||||
print >> sys.stderr, " Missing block height %s in first log" % height
|
||||
return False
|
||||
|
||||
for height in sorted(block_serializations_1.keys()):
|
||||
sh1 = block_serializations_1[height]
|
||||
sh2 = block_serializations_2[height]
|
||||
for height in sorted(block_serializations_old.keys()):
|
||||
s_old = block_serializations_old[height]
|
||||
s_new = block_serializations_new[height]
|
||||
|
||||
if len(sh1) != len(sh2):
|
||||
print >> sys.stderr, " Mismatched number of serializations at block %s (%s != %s)" % (height, len(sh1), len(sh2))
|
||||
if len(s_old) != len(s_new):
|
||||
print >> sys.stderr, " Mismatched number of serializations at block %s (%s != %s)" % (height, len(s_old), len(s_new))
|
||||
return False
|
||||
|
||||
matched = False
|
||||
err = None
|
||||
for s1 in sh1:
|
||||
for s1 in s_old:
|
||||
# has to match one serialization in the second listing
|
||||
# order doesn't matter, since bitcoind reorders them anyway
|
||||
for s2 in sh2:
|
||||
res, err = serialization_eq( height, s1, s2 )
|
||||
for s2 in s_new:
|
||||
|
||||
# serializations to ignore (use the fields from the new log to ignore fields in the old log)
|
||||
ignore = serialization_ignore[:]
|
||||
ignore += s2['__ignore_fields__']
|
||||
|
||||
res, err = serialization_eq( height, s1, s2, ignore=ignore )
|
||||
if res:
|
||||
matched = True
|
||||
sh2.remove(s2)
|
||||
s_new.remove(s2)
|
||||
break
|
||||
|
||||
if not matched:
|
||||
# soldier on here so we can print all mismatches
|
||||
print >> sys.stderr, " Mismatched serializations in block %s" % height
|
||||
print >> sys.stderr, err['error']
|
||||
return False
|
||||
rc = False
|
||||
|
||||
return True
|
||||
return rc
|
||||
|
||||
|
||||
def is_test_successful( test_output ):
|
||||
@@ -235,23 +282,46 @@ def is_test_successful( test_output ):
|
||||
return False
|
||||
|
||||
|
||||
def skip_check( test_output ):
|
||||
"""
|
||||
Should we skip the serialization test?
|
||||
i.e. is it expected to fail?
|
||||
"""
|
||||
with open(test_output, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if len(line) == 0:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
if line == "BLOCKSTACK_SERIALIZATION_CHANGE_BEHAVIOR":
|
||||
# this is a known breaking change
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
test_output_1 = sys.argv[1]
|
||||
test_output_2 = sys.argv[2]
|
||||
test_output_old = sys.argv[1]
|
||||
test_output_new = sys.argv[2]
|
||||
except:
|
||||
print >> sys.stderr, "Usage: %s TEST_OUTPUT_1 TEST_OUTPUT_2" % sys.argv[0]
|
||||
print >> sys.stderr, "Usage: %s TEST_OUTPUT_OLD TEST_OUTPUT_NEW" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
if not is_test_successful( test_output_1 ):
|
||||
print >> sys.stderr, " %s is a failed test" % test_output_1
|
||||
if not is_test_successful( test_output_old ):
|
||||
print >> sys.stderr, " %s is a failed old test" % test_output_old
|
||||
sys.exit(2)
|
||||
|
||||
if not is_test_successful( test_output_new ):
|
||||
print >> sys.stderr, " %s is a failed new test" % test_output_new
|
||||
sys.exit(1)
|
||||
|
||||
if not is_test_successful( test_output_2 ):
|
||||
print >> sys.stderr, " %s is a failed test" % test_output_2
|
||||
sys.exit(1)
|
||||
if skip_check( test_output_new ):
|
||||
print >> sys.stderr, " %s is a breaking chnage" % test_output_new
|
||||
sys.exit(2)
|
||||
|
||||
res = compare_serializations( test_output_1, test_output_2 )
|
||||
res = compare_serializations( test_output_old, test_output_new )
|
||||
if res:
|
||||
sys.exit(0)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user