headless.py adjustments
This commit is contained in:
parent
33f2bb0d79
commit
9799dd2455
@ -43,6 +43,8 @@ def pytest_addoption(parser):
|
||||
default=False, help="fake_txn produces PSBTv2")
|
||||
parser.addoption("--Q", action="store_true", default=False,
|
||||
help="Uses Q simulator when running 'login_settings_tests' module")
|
||||
parser.addoption("--headless", action="store_true", default=False,
|
||||
help="Simulator is running in headless mode")
|
||||
# to make bitcoind produce psbt v2 one currently needs https://github.com/achow101/bitcoin/tree/psbt2
|
||||
# or wait until https://github.com/bitcoin/bitcoin/pull/21283 merged and released
|
||||
|
||||
@ -432,7 +434,7 @@ def cap_story(dev):
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def cap_image(sim_exec, is_q1):
|
||||
def cap_image(request, sim_exec, is_q1):
|
||||
|
||||
def flip(raw):
|
||||
reorg = bytearray(128*64)
|
||||
@ -449,11 +451,13 @@ def cap_image(sim_exec, is_q1):
|
||||
from PIL import Image
|
||||
|
||||
if is_q1:
|
||||
if request.config.getoption('--headless'):
|
||||
raise pytest.skip("headless mode")
|
||||
# trigger simulator to capture a snapshot into a named file, read it.
|
||||
fn = os.path.realpath(f'./debug/snap-{random.randint(1E6, 9E6)}.png')
|
||||
try:
|
||||
sim_exec(f"from glob import dis; dis.dis.save_snapshot({fn!r})")
|
||||
while 1:
|
||||
for _ in range(20):
|
||||
time.sleep(0.010)
|
||||
try:
|
||||
rv = Image.open(fn)
|
||||
|
||||
@ -18,6 +18,7 @@ python run_sim_tests.py -m all --onetime --veryslow # run all tests (
|
||||
python run_sim_tests.py -m test_multisig.py -k cosigning # run only tests that match expression from test_multisig.py
|
||||
python run_sim_tests.py -m test_export.py --pdb # run only export tests and attach debugger
|
||||
python run_sim_tests.py -m test_attended.py --q1 -w 6 --login # run attended test + all login tests
|
||||
python run_sim_tests.py -w 6 --q1 --headless # run in headless mode (skips QR code checks)
|
||||
|
||||
|
||||
Onetime/veryslow tests are completely separated form the rest of the test suite.
|
||||
@ -98,7 +99,7 @@ def is_ok(ec: ExitCode) -> bool:
|
||||
|
||||
|
||||
def _run_pytest_tests(test_module: str, pytest_marks: str, pytest_k: str, pdb: bool,
|
||||
failed_first: bool, psbt2=False, is_Q=False) -> ExitCode:
|
||||
failed_first: bool, psbt2=False, is_Q=False, headless=False) -> ExitCode:
|
||||
cmd_list = [
|
||||
"--cache-clear", "-m", pytest_marks, "--sim",
|
||||
test_module if test_module is not None else ""
|
||||
@ -113,19 +114,21 @@ def _run_pytest_tests(test_module: str, pytest_marks: str, pytest_k: str, pdb: b
|
||||
cmd_list.append("--psbt2")
|
||||
if is_Q:
|
||||
cmd_list.insert(0, "--Q") # only changes behavior in login_settings_test
|
||||
if headless:
|
||||
cmd_list.append("--headless")
|
||||
|
||||
return pytest.main(cmd_list)
|
||||
|
||||
def _run_coldcard_tests(test_module: str, simulator_args: List[str], pytest_marks: str,
|
||||
pytest_k: str, pdb: bool, failed_first: bool, psbt2=False,
|
||||
is_Q=False) -> ExitCode:
|
||||
is_Q=False, headless=False) -> ExitCode:
|
||||
if simulator_args:
|
||||
sim = ColdcardSimulator(args=simulator_args)
|
||||
sim = ColdcardSimulator(args=simulator_args, headless=headless)
|
||||
sim.start()
|
||||
time.sleep(1)
|
||||
|
||||
exit_code = _run_pytest_tests(test_module, pytest_marks, pytest_k, pdb,
|
||||
failed_first, psbt2, is_Q)
|
||||
failed_first, psbt2, is_Q, headless)
|
||||
|
||||
if simulator_args:
|
||||
sim.stop()
|
||||
@ -135,11 +138,11 @@ def _run_coldcard_tests(test_module: str, simulator_args: List[str], pytest_mark
|
||||
|
||||
|
||||
def run_coldcard_tests(test_module=None, simulator_args=None, pytest_k=None, pdb=False,
|
||||
failed_first=False, psbt2=False, is_Q=False,
|
||||
pytest_marks="not onetime and not veryslow and not manual"):
|
||||
failed_first=False, psbt2=False, is_Q=False, headless=False,
|
||||
pytest_marks="not onetime and not veryslow and not manual"):
|
||||
failed = []
|
||||
exit_code = _run_coldcard_tests(test_module, simulator_args, pytest_marks, pytest_k,
|
||||
pdb, failed_first, psbt2, is_Q)
|
||||
pdb, failed_first, psbt2, is_Q, headless)
|
||||
if not is_ok(exit_code):
|
||||
# no success, no nothing - give failed another try, each alone with its own simulator
|
||||
last_failed = get_last_failed()
|
||||
@ -147,7 +150,8 @@ def run_coldcard_tests(test_module=None, simulator_args=None, pytest_k=None, pdb
|
||||
exit_codes = []
|
||||
for failed_test in last_failed:
|
||||
exit_code_2 = _run_coldcard_tests(failed_test, simulator_args, pytest_marks,
|
||||
pytest_k, pdb, failed_first, psbt2, is_Q)
|
||||
pytest_k, pdb, failed_first, psbt2, is_Q,
|
||||
headless)
|
||||
exit_codes.append(exit_code_2)
|
||||
if not is_ok(exit_code_2):
|
||||
failed.append(failed_test)
|
||||
@ -169,16 +173,17 @@ class PytestCollectMarked:
|
||||
|
||||
|
||||
class ColdcardSimulator:
|
||||
def __init__(self, path=None, args=None):
|
||||
def __init__(self, path=None, args=None, headless=False):
|
||||
self.proc = None
|
||||
self.args = args
|
||||
self.path = "/tmp/ckcc-simulator.sock" if path is None else path
|
||||
self.headless = headless
|
||||
|
||||
def start(self, start_wait=None):
|
||||
# here we are in testing directory
|
||||
cmd_list = [
|
||||
"python",
|
||||
"simulator.py"
|
||||
"headless.py" if self.headless else "simulator.py"
|
||||
]
|
||||
if self.args is not None:
|
||||
cmd_list.extend(self.args)
|
||||
@ -223,6 +228,8 @@ def main():
|
||||
help="Collect marked test and print them to stdout")
|
||||
parser.add_argument("-k", "--pytest-k", type=str, metavar="EXPRESSION", default=None,
|
||||
help="only run tests which match the given substring expression")
|
||||
parser.add_argument("--headless", action="store_true", default=False,
|
||||
help="run simulator instance in headless mode")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.sim_init_wait:
|
||||
@ -285,7 +292,8 @@ def main():
|
||||
|
||||
ec, failed_tests = run_coldcard_tests(test_module, simulator_args=test_args,
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
failed_first=args.ff, psbt2=args.psbt2,
|
||||
headless=args.headless)
|
||||
result.append((test_module, ec, failed_tests))
|
||||
print("Done", test_module)
|
||||
print(80 * "=")
|
||||
@ -296,7 +304,8 @@ def main():
|
||||
ec, failed_tests = run_coldcard_tests(test_module=None, pytest_marks="veryslow",
|
||||
pytest_k=args.pytest_k, pdb=args.pdb,
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
failed_first=args.ff, psbt2=args.psbt2)
|
||||
failed_first=args.ff, psbt2=args.psbt2,
|
||||
headless=args.headless)
|
||||
result.append(("veryslow", ec, failed_tests))
|
||||
|
||||
# run onetime is specified (each test against its own simulator)
|
||||
@ -307,20 +316,22 @@ def main():
|
||||
ec, failed_tests = run_coldcard_tests(test_module=onetime_test, pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_marks="onetime",
|
||||
simulator_args=DEFAULT_SIMULATOR_ARGS,
|
||||
psbt2=args.psbt2)
|
||||
psbt2=args.psbt2, headless=args.headless)
|
||||
result.append((f"onetime: {onetime_test}", ec, failed_tests))
|
||||
|
||||
if args.login:
|
||||
print("start login settings tests")
|
||||
ec, failed_tests = run_coldcard_tests(test_module="login_settings_tests.py", pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_k=args.pytest_k,
|
||||
is_Q=True if args.q1 else False)
|
||||
is_Q=True if args.q1 else False,
|
||||
headless=args.headless)
|
||||
result.append((f"login_settings_tests", ec, failed_tests))
|
||||
|
||||
if args.clone:
|
||||
print("start clone tests")
|
||||
ec, failed_tests = run_coldcard_tests(test_module="clone_tests.py", pdb=args.pdb,
|
||||
failed_first=args.ff, pytest_k=args.pytest_k)
|
||||
failed_first=args.ff, pytest_k=args.pytest_k,
|
||||
headless=args.headless)
|
||||
result.append((f"clone_tests", ec, failed_tests))
|
||||
|
||||
print("All done")
|
||||
|
||||
@ -13,7 +13,7 @@ from mnemonic import Mnemonic
|
||||
def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
cap_story, need_keypress, cap_screen_qr, pass_word_quiz,
|
||||
get_setting, seed_story_to_words, press_cancel, is_q1,
|
||||
press_select):
|
||||
press_select, request):
|
||||
def doit(reuse_pw=False, save_pw=False, st=None, ct=False):
|
||||
# st -> seed type
|
||||
# ct -> cleartext backup
|
||||
@ -73,7 +73,7 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
|
||||
print("Passphrase: %s" % ' '.join(words))
|
||||
|
||||
if 'QR Code' in body:
|
||||
if 'QR Code' in body and not request.config.getoption('--headless'):
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
got_qr = cap_screen_qr().decode('ascii').lower().split()
|
||||
assert [w[0:4] for w in words] == got_qr
|
||||
@ -108,11 +108,11 @@ def backup_system(settings_set, settings_remove, goto_home, pick_menu_item,
|
||||
def test_make_backup(multisig, goto_home, pick_menu_item, cap_story, need_keypress, st,
|
||||
open_microsd, microsd_path, unit_test, cap_menu, word_menu_entry,
|
||||
pass_word_quiz, reset_seed_words, import_ms_wallet, get_setting,
|
||||
cap_screen_qr, reuse_pw, save_pw, settings_set, settings_remove,
|
||||
reuse_pw, save_pw, settings_set, settings_remove, press_select,
|
||||
generate_ephemeral_words, set_bip39_pw, verify_backup_file,
|
||||
check_and_decrypt_backup, restore_backup_cs, clear_ms, seedvault,
|
||||
restore_main_seed, import_ephemeral_xprv, backup_system,
|
||||
press_cancel, press_select):
|
||||
press_cancel):
|
||||
# Make an encrypted 7z backup, verify it, and even restore it!
|
||||
clear_ms()
|
||||
reset_seed_words()
|
||||
|
||||
@ -173,7 +173,8 @@ def pass_word_quiz(need_keypress, cap_story, press_select):
|
||||
])
|
||||
def test_import_seed(goto_home, pick_menu_item, cap_story, need_keypress, unit_test,
|
||||
cap_menu, word_menu_entry, seed_words, xfp, get_secrets, is_q1,
|
||||
reset_seed_words, cap_screen_qr, qr_quality_check, expect_ftux):
|
||||
reset_seed_words, cap_screen_qr, qr_quality_check, expect_ftux,
|
||||
request):
|
||||
|
||||
unit_test('devtest/clear_seed.py')
|
||||
|
||||
@ -198,9 +199,10 @@ def test_import_seed(goto_home, pick_menu_item, cap_story, need_keypress, unit_t
|
||||
v = get_secrets()
|
||||
|
||||
assert f'Press {KEY_QR if is_q1 else "(3)"} to show QR code' in body
|
||||
need_keypress(KEY_QR if is_q1 else '3')
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
assert qr == v['xpub']
|
||||
if not request.config.getoption('--headless'):
|
||||
need_keypress(KEY_QR if is_q1 else '3')
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
assert qr == v['xpub']
|
||||
|
||||
assert v['mnemonic'] == seed_words
|
||||
reset_seed_words()
|
||||
@ -259,7 +261,7 @@ def test_all_bip39_words(pos, goto_home, pick_menu_item, cap_story, unit_test,
|
||||
def test_import_from_dice(count, nwords, goto_home, pick_menu_item, cap_story, need_keypress,
|
||||
unit_test, cap_menu, word_menu_entry, get_secrets, reset_seed_words,
|
||||
cap_screen, cap_screen_qr, qr_quality_check, expect_ftux, press_select,
|
||||
press_cancel, is_q1, seed_story_to_words):
|
||||
press_cancel, is_q1, seed_story_to_words, request):
|
||||
import random
|
||||
from hashlib import sha256
|
||||
|
||||
@ -308,11 +310,12 @@ def test_import_from_dice(count, nwords, goto_home, pick_menu_item, cap_story, n
|
||||
else:
|
||||
words = [i[4:4+4].upper() for i in re.findall(r'[ 0-9][0-9]: \w*', body)]
|
||||
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
if not request.config.getoption('--headless'):
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
|
||||
qr = cap_screen_qr()
|
||||
assert qr.decode('ascii').split() == words
|
||||
press_cancel() # close QR
|
||||
qr = cap_screen_qr()
|
||||
assert qr.decode('ascii').split() == words
|
||||
press_cancel() # close QR
|
||||
|
||||
need_keypress('6')
|
||||
time.sleep(0.1)
|
||||
@ -620,7 +623,7 @@ def test_bip39_complex(target, goto_home, pick_menu_item, cap_story,
|
||||
def test_show_seed(mode, b39_word, goto_home, pick_menu_item, cap_story, need_keypress,
|
||||
sim_exec, cap_menu, get_secrets, cap_screen_qr, set_bip39_pw,
|
||||
set_encoded_secret, qr_quality_check, reset_seed_words,
|
||||
press_select, is_q1, seed_story_to_words):
|
||||
press_select, is_q1, seed_story_to_words, request):
|
||||
|
||||
reset_seed_words()
|
||||
if mode == 'words':
|
||||
@ -694,9 +697,11 @@ def test_show_seed(mode, b39_word, goto_home, pick_menu_item, cap_story, need_ke
|
||||
|
||||
if not is_q1:
|
||||
assert '(1) to view as QR Code' in body
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
assert qr == qr_expect
|
||||
|
||||
if not request.config.getoption('--headless'):
|
||||
need_keypress(KEY_QR if is_q1 else '1')
|
||||
qr = cap_screen_qr().decode('ascii')
|
||||
assert qr == qr_expect
|
||||
|
||||
press_select() # clear screen
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ def start():
|
||||
'-X', 'heapsize=9m',
|
||||
'-i', '../sim_boot.py',
|
||||
str(oled_w), '-1', str(led_w)] \
|
||||
+ sys.argv[1:]
|
||||
+ sys.argv[1:] + ["--headless"]
|
||||
|
||||
|
||||
args = dict(env=env, pass_fds=[oled_w, led_w], shell=False)
|
||||
|
||||
@ -23,8 +23,9 @@ genuine_led = True
|
||||
from sim_secel import SEState
|
||||
SE_STATE = SEState()
|
||||
|
||||
# Provide a way to dump few hundred/4k bytes of data from QR or NFC simulated read
|
||||
data_pipe = uasyncio.StreamReader(open(int(sys.argv[4]), 'rb'))
|
||||
if not "--headless" in sys.argv:
|
||||
# Provide a way to dump few hundred/4k bytes of data from QR or NFC simulated read
|
||||
data_pipe = uasyncio.StreamReader(open(int(sys.argv[4]), 'rb'))
|
||||
|
||||
# HACK: reduce size of heap in Unix simulator to be more similar to
|
||||
# actual hardware, so we can enjoy those out-of-memory errors too!
|
||||
|
||||
Loading…
Reference in New Issue
Block a user