Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_arduino_build / py / pw_arduino_build / unit_test_server.py
1 #!/usr/bin/env python3
2 # Copyright 2020 The Pigweed Authors
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 # use this file except in compliance with the License. You may obtain a copy of
6 # the License at
7 #
8 #     https://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations under
14 # the License.
15 """Launch a pw_test_server server to use for multi-device testing."""
16
17 import argparse
18 import logging
19 import sys
20 import tempfile
21 from typing import IO, List, Optional
22
23 import pw_cli.process
24
25 import pw_arduino_build.log
26 from pw_arduino_build import teensy_detector
27 from pw_arduino_build.file_operations import decode_file_json
28 from pw_arduino_build.unit_test_runner import ArduinoCoreNotSupported
29
30 _LOG = logging.getLogger('unit_test_server')
31
32 _TEST_RUNNER_COMMAND = 'arduino_unit_test_runner'
33
34 _TEST_SERVER_COMMAND = 'pw_target_runner_server'
35
36
37 class UnknownArduinoCore(Exception):
38     """Exception raised when no Arduino core can be found."""
39
40
41 def parse_args():
42     """Parses command-line arguments."""
43
44     parser = argparse.ArgumentParser(description=__doc__)
45     parser.add_argument('--server-port',
46                         type=int,
47                         default=8081,
48                         help='Port to launch the pw_target_runner_server on')
49     parser.add_argument('--server-config',
50                         type=argparse.FileType('r'),
51                         help='Path to server config file')
52     parser.add_argument('--verbose',
53                         '-v',
54                         dest='verbose',
55                         action="store_true",
56                         help='Output additional logs as the script runs')
57     parser.add_argument("-c",
58                         "--config-file",
59                         required=True,
60                         help="Path to an arduino_builder config file.")
61     # TODO(tonymd): Explicitly split args using "--". See example in:
62     # //pw_unit_test/py/pw_unit_test/test_runner.py:326
63     parser.add_argument('runner_args',
64                         nargs=argparse.REMAINDER,
65                         help='Arguments to forward to the test runner')
66
67     return parser.parse_args()
68
69
70 def generate_runner(command: str, arguments: List[str]) -> str:
71     """Generates a text-proto style pw_target_runner_server configuration."""
72     # TODO(amontanez): Use a real proto library to generate this when we have
73     # one set up.
74     for i, arg in enumerate(arguments):
75         arguments[i] = f'  args: "{arg}"'
76     runner = ['runner {', f'  command:"{command}"']
77     runner.extend(arguments)
78     runner.append('}\n')
79     return '\n'.join(runner)
80
81
82 def generate_server_config(runner_args: Optional[List[str]],
83                            arduino_package_path: str) -> IO[bytes]:
84     """Returns a temporary generated file for use as the server config."""
85
86     if "teensy" not in arduino_package_path:
87         raise ArduinoCoreNotSupported(arduino_package_path)
88
89     boards = teensy_detector.detect_boards(arduino_package_path)
90     if not boards:
91         _LOG.critical('No attached boards detected')
92         sys.exit(1)
93     config_file = tempfile.NamedTemporaryFile()
94     _LOG.debug('Generating test server config at %s', config_file.name)
95     _LOG.debug('Found %d attached devices', len(boards))
96     for board in boards:
97         test_runner_args = []
98         if runner_args:
99             test_runner_args += runner_args
100         test_runner_args += ["-v"] + board.test_runner_args()
101         test_runner_args += ["--port", board.dev_name]
102         test_runner_args += ["--upload-tool", board.arduino_upload_tool_name]
103         config_file.write(
104             generate_runner(_TEST_RUNNER_COMMAND,
105                             test_runner_args).encode('utf-8'))
106     config_file.flush()
107     return config_file
108
109
110 def launch_server(server_config: Optional[IO[bytes]],
111                   server_port: Optional[int], runner_args: Optional[List[str]],
112                   arduino_package_path: str) -> int:
113     """Launch a device test server with the provided arguments."""
114     if server_config is None:
115         # Auto-detect attached boards if no config is provided.
116         server_config = generate_server_config(runner_args,
117                                                arduino_package_path)
118
119     cmd = [_TEST_SERVER_COMMAND, '-config', server_config.name]
120
121     if server_port is not None:
122         cmd.extend(['-port', str(server_port)])
123
124     return pw_cli.process.run(*cmd, log_output=True).returncode
125
126
127 def main():
128     """Launch a device test server with the provided arguments."""
129     args = parse_args()
130
131     if "--" in args.runner_args:
132         args.runner_args.remove("--")
133
134     log_level = logging.DEBUG if args.verbose else logging.INFO
135     pw_arduino_build.log.install(log_level)
136
137     # Get arduino_package_path from either the config file or command line args.
138     arduino_package_path = None
139     if args.config_file:
140         json_file_options, unused_config_path = decode_file_json(
141             args.config_file)
142         arduino_package_path = json_file_options.get("arduino_package_path",
143                                                      None)
144         # Must pass --config-file option in the runner_args.
145         if "--config-file" not in args.runner_args:
146             args.runner_args.append("--config-file")
147             args.runner_args.append(args.config_file)
148
149     # Check for arduino_package_path in the runner_args
150     try:
151         arduino_package_path = args.runner_args[
152             args.runner_args.index("--arduino-package-path") + 1]
153     except (ValueError, IndexError):
154         # Only raise an error if arduino_package_path not set from the json.
155         if arduino_package_path is None:
156             raise UnknownArduinoCore("Test runner arguments: '{}'".format(
157                 " ".join(args.runner_args)))
158
159     exit_code = launch_server(args.server_config, args.server_port,
160                               args.runner_args, arduino_package_path)
161     sys.exit(exit_code)
162
163
164 if __name__ == '__main__':
165     main()