From 9c103c945ccbb4143129cd3118ab9642487cf3a2 Mon Sep 17 00:00:00 2001 From: Chris Reed Date: Sun, 4 Oct 2020 12:49:36 -0500 Subject: [PATCH] Merge pull request #971 from flit/bugfix/commander_elf Commander fix for passed in elves --- pyocd/commands/commander.py | 3 +- pyocd/commands/execution_context.py | 13 +-- test/automated_test.py | 2 + test/commander_test.py | 143 ++++++++++++++++++++++++++++ test/test_util.py | 12 ++- 5 files changed, 165 insertions(+), 8 deletions(-) create mode 100644 test/commander_test.py diff --git a/pyocd/commands/commander.py b/pyocd/commands/commander.py index e87520e40..45657a047 100755 --- a/pyocd/commands/commander.py +++ b/pyocd/commands/commander.py @@ -16,6 +16,7 @@ from __future__ import print_function import logging +import os import traceback from ..core.helpers import ConnectHelper @@ -215,7 +216,7 @@ def _post_connect(self): # Set elf file if provided. if self.args.elf: - self.target.elf = os.path.expanduser(self.args.elf) + self.session.target.elf = os.path.expanduser(self.args.elf) # Handle a device with flash security enabled. if not self.args.no_init and self.session.target.is_locked(): diff --git a/pyocd/commands/execution_context.py b/pyocd/commands/execution_context.py index 6f01d016f..6d8061092 100755 --- a/pyocd/commands/execution_context.py +++ b/pyocd/commands/execution_context.py @@ -302,10 +302,10 @@ def process_command(self, cmd): if matched_command is None: all_matches = self._command_set.command_matcher.find_all(cmd) if len(all_matches) > 1: - self.writei("Error: command '%s' is ambiguous; matches are %s", cmd, - ", ".join("'%s'" % c for c in all_matches)) + raise exceptions.CommandError("command '%s' is ambiguous; matches are %s" % (cmd, + ", ".join("'%s'" % c for c in all_matches))) else: - self.writei("Error: unrecognized command '%s'", cmd) + raise exceptions.CommandError("unrecognized command '%s'" % cmd) return # Run command. @@ -345,6 +345,7 @@ def handle_python(self, cmd): w, h = get_terminal_size() self.write(pprint.pformat(result, indent=2, width=w, depth=10)) except Exception as e: - self.writei("Exception while executing expression: %s", e) - LOG.error("Exception while executing expression: %s", e, - exc_info=self.session.log_tracebacks) + # Log the traceback before raising the exception. + if self.session.log_tracebacks: + LOG.error("Exception while executing expression: %s", e, exc_info=True) + raise exceptions.CommandError("exception while executing expression: %s" % e) diff --git a/test/automated_test.py b/test/automated_test.py index 0bb01f62f..55fb765d6 100755 --- a/test/automated_test.py +++ b/test/automated_test.py @@ -50,6 +50,7 @@ from debug_context_test import DebugContextTest from concurrency_test import ConcurrencyTest from commands_test import CommandsTest +from commander_test import CommanderTest XML_RESULTS_TEMPLATE = "test_results{}.xml" LOG_FILE_TEMPLATE = "automated_test_result{}.txt" @@ -72,6 +73,7 @@ DebugContextTest(), GdbTest(), CommandsTest(), + CommanderTest(), ] # Actual list used at runtime, filted by command line args. diff --git a/test/commander_test.py b/test/commander_test.py new file mode 100644 index 000000000..11ecb63de --- /dev/null +++ b/test/commander_test.py @@ -0,0 +1,143 @@ +# pyOCD debugger +# Copyright (c) 2020 Arm Limited +# SPDX-License-Identifier: Apache-2.0 +# +# 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 +# +# http://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. +from __future__ import print_function + +import argparse +import sys +import traceback +import logging +import six +import os + +from pyocd.core.helpers import ConnectHelper +from pyocd.probe.pydapaccess import DAPAccess +from pyocd.utility.mask import round_up_div +from pyocd.utility import conversion +from pyocd.core.memory_map import MemoryType +from pyocd.commands.commander import PyOCDCommander +from pyocd.commands import commands +from test_util import ( + Test, + TestResult, + get_session_options, + get_target_test_params, + binary_to_elf_file, + get_test_binary_path, + PYOCD_DIR, + ) + +GDB_TEST_ELF = os.path.join(PYOCD_DIR, "src/gdb_test_program/gdb_test.elf") + +class CommanderTestResult(TestResult): + def __init__(self): + super(CommanderTestResult, self).__init__(None, None, None) + self.name = "commander" + +class CommanderTest(Test): + def __init__(self): + super(CommanderTest, self).__init__("Commander Test", commander_test) + + def run(self, board): + try: + result = self.test_function(board.unique_id) + except Exception as e: + result = CommanderTestResult() + result.passed = False + print("Exception %s when testing board %s" % (e, board.unique_id)) + traceback.print_exc(file=sys.stdout) + result.board = board + result.test = self + return result + +def commander_test(board_id): + test_pass_count = 0 + test_count = 0 + failed_commands = [] + result = CommanderTestResult() + + COMMANDS_TO_TEST = [ + # general commands + ["continue"], + ["status"], + ["halt"], + ["status"], + + # commander command group - these are not tested by commands_test.py. + ["list"], + ["exit"], # Must be last command! + ] + + print("\n------ Testing commander ------\n") + + # Set up commander args. + args = six.moves.UserDict() + args.no_init = False + args.frequency = 1000000 + args.options = {} #get_session_options() + args.halt = True + args.no_wait = True + args.project_dir = None + args.config = None + args.script = None + args.no_config = False + args.pack = None + args.unique_id = board_id + args.target_override = None + args.elf = GDB_TEST_ELF + + test_count += 1 + try: + cmdr = PyOCDCommander(args, COMMANDS_TO_TEST) + cmdr.run() + test_pass_count += 1 + print("TEST PASSED") + except Exception: + print("TEST FAILED") + traceback.print_exc() + + test_count += 1 + print("Testing exit code") + print("Exit code:", cmdr.exit_code) + if cmdr.exit_code == 0: + test_pass_count += 1 + print("TEST PASSED") + else: + print("TEST FAILED") + + print("\n\nTest Summary:") + print("Pass count %i of %i tests" % (test_pass_count, test_count)) + if failed_commands: + for c in failed_commands: + print(" - '" + c + "'") + if test_pass_count == test_count: + print("COMMANDER TEST SCRIPT PASSED") + else: + print("COMMANDER TEST SCRIPT FAILED") + + result.passed = test_count == test_pass_count + return result + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='pyOCD commander test') + parser.add_argument('-d', '--debug', action="store_true", help='Enable debug logging') + parser.add_argument('-u', '--uid', help='Debug probe unique ID') + parser.add_argument("-da", "--daparg", dest="daparg", nargs='+', help="Send setting to DAPAccess layer.") + args = parser.parse_args() + level = logging.DEBUG if args.debug else logging.INFO + logging.basicConfig(level=level) + DAPAccess.set_args(args.daparg) + commander_test(args.uid) + diff --git a/test/test_util.py b/test/test_util.py index 34f8378bd..b4899ee7a 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -201,8 +201,18 @@ def get_test_case(self): type="failure" ) system_out = ElementTree.SubElement(case, 'system-out') - system_out.text = self.output + system_out.text = self.filter_output(self.output) return case + + def filter_output(self, output): + """! @brief Hex-encode null byte and control characters.""" + result = six.text_type() + for c in output: + if (c not in ('\n', '\r', '\t')) and (0 <= ord(c) <= 31): + result += u"\\x{:02x}".format(ord(c)) + else: + result += c + return result class Test(object):