| #!/usr/bin/env python3 |
| # Copyright 2023 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Launch a pw_target_runner server to use for multi-device testing.""" |
| |
| import argparse |
| import logging |
| import sys |
| import tempfile |
| from typing import IO, List, Optional |
| |
| import pw_cli.process |
| import pw_cli.log |
| |
| from stm32f769i_disc0_utils import stm32f769i_detector |
| |
| _LOG = logging.getLogger('unit_test_server') |
| |
| _TEST_RUNNER_COMMAND = 'stm32f769i_disc0_unit_test_runner' |
| |
| _TEST_SERVER_COMMAND = 'pw_target_runner_server' |
| |
| |
| def parse_args(): |
| """Parses command-line arguments.""" |
| |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument( |
| '--server-port', |
| type=int, |
| default=8080, |
| help='Port to launch the pw_target_runner_server on', |
| ) |
| parser.add_argument( |
| '--server-config', |
| type=argparse.FileType('r'), |
| help='Path to server config file', |
| ) |
| parser.add_argument( |
| '--verbose', |
| '-v', |
| dest='verbose', |
| action="store_true", |
| help='Output additional logs as the script runs', |
| ) |
| |
| return parser.parse_args() |
| |
| |
| def generate_runner(command: str, arguments: List[str]) -> str: |
| """Generates a text-proto style pw_target_runner_server configuration.""" |
| # TODO(amontanez): Use a real proto library to generate this when we have |
| # one set up. |
| for i, arg in enumerate(arguments): |
| arguments[i] = f' args: "{arg}"' |
| runner = ['runner {', f' command:"{command}"'] |
| runner.extend(arguments) |
| runner.append('}\n') |
| return '\n'.join(runner) |
| |
| |
| def generate_server_config() -> IO[bytes]: |
| """Returns a temporary generated file for use as the server config.""" |
| boards = stm32f769i_detector.detect_boards() |
| if not boards: |
| _LOG.critical('No attached boards detected') |
| sys.exit(1) |
| config_file = tempfile.NamedTemporaryFile() |
| _LOG.debug('Generating test server config at %s', config_file.name) |
| _LOG.debug('Found %d attached devices', len(boards)) |
| for board in boards: |
| test_runner_args = [ |
| '--stlink-serial', |
| board.serial_number, |
| '--port', |
| board.dev_name, |
| ] |
| config_file.write( |
| generate_runner(_TEST_RUNNER_COMMAND, test_runner_args).encode( |
| 'utf-8' |
| ) |
| ) |
| config_file.flush() |
| return config_file |
| |
| |
| def launch_server( |
| server_config: Optional[IO[bytes]], server_port: Optional[int] |
| ) -> int: |
| """Launch a device test server with the provided arguments.""" |
| if server_config is None: |
| # Auto-detect attached boards if no config is provided. |
| server_config = generate_server_config() |
| |
| cmd = [_TEST_SERVER_COMMAND, '-config', server_config.name] |
| |
| if server_port is not None: |
| cmd.extend(['-port', str(server_port)]) |
| |
| return pw_cli.process.run(*cmd, log_output=True).returncode |
| |
| |
| def main(): |
| """Launch a device test server with the provided arguments.""" |
| args = parse_args() |
| |
| # Try to use pw_cli logs, else default to something reasonable. |
| pw_cli.log.install() |
| if args.verbose: |
| _LOG.setLevel(logging.DEBUG) |
| |
| exit_code = launch_server(args.server_config, args.server_port) |
| sys.exit(exit_code) |
| |
| |
| if __name__ == '__main__': |
| main() |