1#!/bin/sh
2"exec" "`dirname $0`/py3-cmd" "$0" "-c" "`dirname $0`/config.json" "$@"
3
4import argparse
5import logging
6import os
7from typing import List, Optional
8import sys
9
10
11import qemu
12import qemu_error
13
14__all__ = ["init", "run_test", "shutdown"]
15
16
17TRUSTY_PROJECT_FOLDER = os.path.dirname(os.path.realpath(__file__))
18
19
20def init(*, android=None, instance_dir: os.PathLike, disable_rpmb=False,
21         verbose=False, debug_on_error=False
22) -> qemu.Runner:
23
24    with open(f"{TRUSTY_PROJECT_FOLDER}/config.json", encoding="utf-8") as json:
25        config = qemu.Config(json)
26
27    if android:
28        config.android_image_dir = qemu.find_android_image_dir(android)
29        config.adb = qemu.find_adb_path(android)
30        config.boot_android = True
31
32    runner = qemu.Runner(config,
33                         interactive=False,
34                         instance_dir=instance_dir,
35                         verbose=verbose,
36                         rpmb=not disable_rpmb,
37                         debug=False,
38                         debug_on_error=debug_on_error)
39    return runner
40
41
42def _check_args(args):
43    """Validate arguments passed to run_test."""
44    assert args.headless, args
45    assert not args.linux, args
46    assert not args.atf, args
47    assert not args.qemu, args
48    assert not args.arch, args
49    assert not args.debug, args
50    assert not args.extra_qemu_flags, args
51    assert not args.disable_rpmb, args
52
53
54def _prepare_runner_for_test(runner, args):
55    """Check if the runner is in the correct state (BOOTLOADER, ANDROID)
56    to run a given test and reboot the emulator if it is not.
57
58    TODO: Remove the unconditional reboot after boot tests once the test harness
59          no longers requires it.
60    """
61    if args.boot_test:
62        target_state = qemu.RunnerState.BOOTLOADER
63    elif args.shell_command:
64        target_state = qemu.RunnerState.ANDROID
65    else:
66        raise qemu_error.ConfigError(
67            "Command must request exactly one Android or boot test to run")
68
69    # Due to limitations in the test runner, always reboot between boot tests
70    if (runner.state != target_state or
71            runner.state == qemu.RunnerState.BOOTLOADER):
72        runner.reboot(target_state, factory_reset=True, full_wipe=False)
73
74
75def run_test(runner: qemu.Runner, cmd: List[str]) -> int:
76    args = build_argparser().parse_args(cmd)
77    _check_args(args)
78    _prepare_runner_for_test(runner, args)
79
80    timeout = args.timeout if args.timeout else runner.default_timeout
81    if args.boot_test:
82        return runner.boottest_run(args.boot_test, timeout)
83    if args.shell_command:
84        return runner.androidtest_run(args.shell_command, timeout)
85
86    raise qemu.RunnerGenericError(
87        "Command contained neither a boot test nor an Android test to run")
88
89
90def shutdown(runner: Optional[qemu.Runner],
91             factory_reset: bool = True,
92             full_wipe: bool = False):
93    if runner:
94        runner.shutdown(factory_reset, full_wipe)
95
96
97def build_argparser():
98    argument_parser = argparse.ArgumentParser()
99    argument_parser.add_argument("-c", "--config", type=argparse.FileType("r"))
100    argument_parser.add_argument("--headless", action="store_true")
101    argument_parser.add_argument("-v", "--verbose", action="store_true")
102    argument_parser.add_argument("--debug", action="store_true")
103    argument_parser.add_argument("--debug-on-error", action="store_true")
104    argument_parser.add_argument("--boot-test", action="append")
105    argument_parser.add_argument("--shell-command", action="append")
106    argument_parser.add_argument("--android")
107    argument_parser.add_argument("--linux")
108    argument_parser.add_argument("--instance-dir", type=str,
109                                 default="/tmp/trusty-qemu-generic-arm64")
110    argument_parser.add_argument("--atf")
111    argument_parser.add_argument("--qemu")
112    argument_parser.add_argument("--arch")
113    argument_parser.add_argument("--disable-rpmb", action="store_true")
114    argument_parser.add_argument("--timeout", type=int)
115    argument_parser.add_argument("extra_qemu_flags", nargs="*")
116    return argument_parser
117
118
119def main():
120    args = build_argparser().parse_args()
121    log_level = logging.DEBUG if args.verbose else logging.WARN
122    logging.basicConfig(level=log_level)
123
124    config = qemu.Config(args.config)
125    if args.android:
126        config.android_image_dir = qemu.find_android_image_dir(args.android)
127        config.adb = qemu.find_adb_path(args.android)
128        config.boot_android = True
129    if args.linux:
130        config.linux = args.linux
131    if args.atf:
132        config.atf = args.atf
133    if args.qemu:
134        config.qemu = args.qemu
135    if args.arch:
136        config.arch = args.arch
137    if args.extra_qemu_flags:
138        config.extra_qemu_flags += args.extra_qemu_flags
139
140    runner = qemu.Runner(config,
141                         interactive=not args.headless,
142                         instance_dir=args.instance_dir,
143                         verbose=args.verbose,
144                         rpmb=not args.disable_rpmb,
145                         debug=args.debug,
146                         debug_on_error=args.debug_on_error)
147
148    try:
149        results = runner.run(args.boot_test, args.shell_command, args.timeout)
150        print("Command results: " + repr(results))
151
152        if any(results):
153            sys.exit(1)
154        else:
155            sys.exit(0)
156    except qemu_error.RunnerError as exn:
157        print(exn)
158        sys.exit(2)
159
160
161if __name__ == "__main__":
162    main()
163