aboutsummaryrefslogtreecommitdiff
path: root/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py
diff options
context:
space:
mode:
Diffstat (limited to 'cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py')
-rw-r--r--cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py241
1 files changed, 135 insertions, 106 deletions
diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py
index 1027ba0..5afefb1 100644
--- a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py
+++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py
@@ -26,7 +26,10 @@ from dex.command.commands.DexExpectStepKind import DexExpectStepKind
from dex.command.commands.DexExpectStepOrder import DexExpectStepOrder
from dex.command.commands.DexExpectWatchType import DexExpectWatchType
from dex.command.commands.DexExpectWatchValue import DexExpectWatchValue
-from dex.command.commands.DexExpectWatchBase import AddressExpression, DexExpectWatchBase
+from dex.command.commands.DexExpectWatchBase import (
+ AddressExpression,
+ DexExpectWatchBase,
+)
from dex.command.commands.DexLabel import DexLabel
from dex.command.commands.DexLimitSteps import DexLimitSteps
from dex.command.commands.DexFinishTest import DexFinishTest
@@ -35,6 +38,7 @@ from dex.command.commands.DexWatch import DexWatch
from dex.utils import Timer
from dex.utils.Exceptions import CommandParseError, DebuggerException
+
def _get_valid_commands():
"""Return all top level DExTer test commands.
@@ -42,19 +46,19 @@ def _get_valid_commands():
{ name (str): command (class) }
"""
return {
- DexCommandLine.get_name() : DexCommandLine,
- DexDeclareAddress.get_name() : DexDeclareAddress,
- DexDeclareFile.get_name() : DexDeclareFile,
- DexExpectProgramState.get_name() : DexExpectProgramState,
- DexExpectStepKind.get_name() : DexExpectStepKind,
- DexExpectStepOrder.get_name() : DexExpectStepOrder,
- DexExpectWatchType.get_name() : DexExpectWatchType,
- DexExpectWatchValue.get_name() : DexExpectWatchValue,
- DexLabel.get_name() : DexLabel,
- DexLimitSteps.get_name() : DexLimitSteps,
- DexFinishTest.get_name() : DexFinishTest,
- DexUnreachable.get_name() : DexUnreachable,
- DexWatch.get_name() : DexWatch
+ DexCommandLine.get_name(): DexCommandLine,
+ DexDeclareAddress.get_name(): DexDeclareAddress,
+ DexDeclareFile.get_name(): DexDeclareFile,
+ DexExpectProgramState.get_name(): DexExpectProgramState,
+ DexExpectStepKind.get_name(): DexExpectStepKind,
+ DexExpectStepOrder.get_name(): DexExpectStepOrder,
+ DexExpectWatchType.get_name(): DexExpectWatchType,
+ DexExpectWatchValue.get_name(): DexExpectWatchValue,
+ DexLabel.get_name(): DexLabel,
+ DexLimitSteps.get_name(): DexLimitSteps,
+ DexFinishTest.get_name(): DexFinishTest,
+ DexUnreachable.get_name(): DexUnreachable,
+ DexWatch.get_name(): DexWatch,
}
@@ -63,7 +67,7 @@ def _get_command_name(command_raw: str) -> str:
command_raw on the first opening paranthesis and further stripping
any potential leading or trailing whitespace.
"""
- return command_raw.split('(', 1)[0].rstrip()
+ return command_raw.split("(", 1)[0].rstrip()
def _merge_subcommands(command_name: str, valid_commands: dict) -> dict:
@@ -74,11 +78,13 @@ def _merge_subcommands(command_name: str, valid_commands: dict) -> dict:
"""
subcommands = valid_commands[command_name].get_subcommands()
if subcommands:
- return { **valid_commands, **subcommands }
+ return {**valid_commands, **subcommands}
return valid_commands
-def _build_command(command_type, labels, addresses, raw_text: str, path: str, lineno: str) -> CommandBase:
+def _build_command(
+ command_type, labels, addresses, raw_text: str, path: str, lineno: str
+) -> CommandBase:
"""Build a command object from raw text.
This function will call eval().
@@ -89,23 +95,26 @@ def _build_command(command_type, labels, addresses, raw_text: str, path: str, li
Returns:
A dexter command object.
"""
+
def label_to_line(label_name: str) -> int:
line = labels.get(label_name, None)
if line != None:
return line
raise format_unresolved_label_err(label_name, raw_text, path, lineno)
- def get_address_object(address_name: str, offset: int=0):
+ def get_address_object(address_name: str, offset: int = 0):
if address_name not in addresses:
raise format_undeclared_address_err(address_name, raw_text, path, lineno)
return AddressExpression(address_name, offset)
valid_commands = _merge_subcommands(
- command_type.get_name(), {
- 'ref': label_to_line,
- 'address': get_address_object,
+ command_type.get_name(),
+ {
+ "ref": label_to_line,
+ "address": get_address_object,
command_type.get_name(): command_type,
- })
+ },
+ )
# pylint: disable=eval-used
command = eval(raw_text, valid_commands)
@@ -130,7 +139,7 @@ def _search_line_for_cmd_start(line: str, start: int, valid_commands: dict) -> i
idx = line.find(command, start)
if idx != -1:
# Ignore escaped '\' commands.
- if idx > 0 and line[idx - 1] == '\\':
+ if idx > 0 and line[idx - 1] == "\\":
continue
return idx
return -1
@@ -158,17 +167,17 @@ def _search_line_for_cmd_end(line: str, start: int, paren_balance: int) -> (int,
"""
for end in range(start, len(line)):
ch = line[end]
- if ch == '(':
+ if ch == "(":
paren_balance += 1
- elif ch == ')':
- paren_balance -=1
+ elif ch == ")":
+ paren_balance -= 1
if paren_balance == 0:
break
end += 1
return (end, paren_balance)
-class TextPoint():
+class TextPoint:
def __init__(self, line, char):
self.line = line
self.char = char
@@ -180,37 +189,45 @@ class TextPoint():
return self.char + 1
-def format_unresolved_label_err(label: str, src: str, filename: str, lineno) -> CommandParseError:
+def format_unresolved_label_err(
+ label: str, src: str, filename: str, lineno
+) -> CommandParseError:
err = CommandParseError()
err.src = src
- err.caret = '' # Don't bother trying to point to the bad label.
+ err.caret = "" # Don't bother trying to point to the bad label.
err.filename = filename
err.lineno = lineno
- err.info = f'Unresolved label: \'{label}\''
+ err.info = f"Unresolved label: '{label}'"
return err
-def format_undeclared_address_err(address: str, src: str, filename: str, lineno) -> CommandParseError:
+
+def format_undeclared_address_err(
+ address: str, src: str, filename: str, lineno
+) -> CommandParseError:
err = CommandParseError()
err.src = src
- err.caret = '' # Don't bother trying to point to the bad address.
+ err.caret = "" # Don't bother trying to point to the bad address.
err.filename = filename
err.lineno = lineno
- err.info = f'Undeclared address: \'{address}\''
+ err.info = f"Undeclared address: '{address}'"
return err
-def format_parse_err(msg: str, path: str, lines: list, point: TextPoint) -> CommandParseError:
+
+def format_parse_err(
+ msg: str, path: str, lines: list, point: TextPoint
+) -> CommandParseError:
err = CommandParseError()
err.filename = path
err.src = lines[point.line].rstrip()
err.lineno = point.get_lineno()
err.info = msg
- err.caret = '{}<r>^</>'.format(' ' * (point.char))
+ err.caret = "{}<r>^</>".format(" " * (point.char))
return err
def skip_horizontal_whitespace(line, point):
- for idx, char in enumerate(line[point.char:]):
- if char not in ' \t':
+ for idx, char in enumerate(line[point.char :]):
+ if char not in " \t":
point.char += idx
return
@@ -219,34 +236,36 @@ def add_line_label(labels, label, cmd_path, cmd_lineno):
# Enforce unique line labels.
if label.eval() in labels:
err = CommandParseError()
- err.info = f'Found duplicate line label: \'{label.eval()}\''
+ err.info = f"Found duplicate line label: '{label.eval()}'"
err.lineno = cmd_lineno
err.filename = cmd_path
err.src = label.raw_text
# Don't both trying to point to it since we're only printing the raw
# command, which isn't much text.
- err.caret = ''
+ err.caret = ""
raise err
labels[label.eval()] = label.get_line()
+
def add_address(addresses, address, cmd_path, cmd_lineno):
# Enforce unique address variables.
address_name = address.get_address_name()
if address_name in addresses:
err = CommandParseError()
- err.info = f'Found duplicate address: \'{address_name}\''
+ err.info = f"Found duplicate address: '{address_name}'"
err.lineno = cmd_lineno
err.filename = cmd_path
err.src = address.raw_text
# Don't both trying to point to it since we're only printing the raw
# command, which isn't much text.
- err.caret = ''
+ err.caret = ""
raise err
addresses.append(address_name)
+
def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir):
- labels = {} # dict of {name: line}.
- addresses = [] # list of addresses.
+ labels = {} # dict of {name: line}.
+ addresses = [] # list of addresses.
address_resolutions = {}
cmd_path = path
declared_files = set()
@@ -262,23 +281,30 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir
while True:
# If parens are currently balanced we can look for a new command.
if paren_balance == 0:
- region_start.char = _search_line_for_cmd_start(line, region_start.char, valid_commands)
+ region_start.char = _search_line_for_cmd_start(
+ line, region_start.char, valid_commands
+ )
if region_start.char == -1:
- break # Read next line.
+ break # Read next line.
- command_name = _get_command_name(line[region_start.char:])
+ command_name = _get_command_name(line[region_start.char :])
cmd_point = copy(region_start)
cmd_text_list = [command_name]
- region_start.char += len(command_name) # Start searching for parens after cmd.
+ region_start.char += len(
+ command_name
+ ) # Start searching for parens after cmd.
skip_horizontal_whitespace(line, region_start)
- if region_start.char >= len(line) or line[region_start.char] != '(':
+ if region_start.char >= len(line) or line[region_start.char] != "(":
raise format_parse_err(
- "Missing open parenthesis", path, file_lines, region_start)
+ "Missing open parenthesis", path, file_lines, region_start
+ )
- end, paren_balance = _search_line_for_cmd_end(line, region_start.char, paren_balance)
+ end, paren_balance = _search_line_for_cmd_end(
+ line, region_start.char, paren_balance
+ )
# Add this text blob to the command.
- cmd_text_list.append(line[region_start.char:end])
+ cmd_text_list.append(line[region_start.char : end])
# Move parse ptr to end of line or parens.
region_start.char = end
@@ -302,8 +328,8 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir
# This err should point to the problem line.
err_point = copy(cmd_point)
# To e the command start is the absolute start, so use as offset.
- err_point.line += e.lineno - 1 # e.lineno is a position, not index.
- err_point.char += e.offset - 1 # e.offset is a position, not index.
+ err_point.line += e.lineno - 1 # e.lineno is a position, not index.
+ err_point.char += e.offset - 1 # e.offset is a position, not index.
raise format_parse_err(e.msg, path, file_lines, err_point)
except TypeError as e:
# This err should always point to the end of the command name.
@@ -322,18 +348,23 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir
elif type(command) is DexDeclareFile:
cmd_path = command.declared_file
if not os.path.isabs(cmd_path):
- source_dir = (source_root_dir if source_root_dir else
- os.path.dirname(path))
+ source_dir = (
+ source_root_dir
+ if source_root_dir
+ else os.path.dirname(path)
+ )
cmd_path = os.path.join(source_dir, cmd_path)
# TODO: keep stored paths as PurePaths for 'longer'.
cmd_path = str(PurePath(cmd_path))
declared_files.add(cmd_path)
- elif type(command) is DexCommandLine and 'DexCommandLine' in commands:
+ elif type(command) is DexCommandLine and "DexCommandLine" in commands:
msg = "More than one DexCommandLine in file"
raise format_parse_err(msg, path, file_lines, err_point)
assert (path, cmd_point) not in commands[command_name], (
- command_name, commands[command_name])
+ command_name,
+ commands[command_name],
+ )
commands[command_name][path, cmd_point] = command
if paren_balance != 0:
@@ -344,6 +375,7 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir
raise format_parse_err(msg, path, file_lines, err_point)
return dict(commands), declared_files
+
def _find_all_commands(test_files, source_root_dir):
commands = defaultdict(dict)
valid_commands = _get_valid_commands()
@@ -352,28 +384,32 @@ def _find_all_commands(test_files, source_root_dir):
with open(test_file) as fp:
lines = fp.readlines()
file_commands, declared_files = _find_all_commands_in_file(
- test_file, lines, valid_commands, source_root_dir)
+ test_file, lines, valid_commands, source_root_dir
+ )
for command_name in file_commands:
commands[command_name].update(file_commands[command_name])
new_source_files |= declared_files
return dict(commands), new_source_files
+
def get_command_infos(test_files, source_root_dir):
- with Timer('parsing commands'):
- try:
- commands, new_source_files = _find_all_commands(test_files, source_root_dir)
- command_infos = OrderedDict()
- for command_type in commands:
- for command in commands[command_type].values():
- if command_type not in command_infos:
- command_infos[command_type] = []
- command_infos[command_type].append(command)
- return OrderedDict(command_infos), new_source_files
- except CommandParseError as e:
- msg = 'parser error: <d>{}({}):</> {}\n{}\n{}\n'.format(
- e.filename, e.lineno, e.info, e.src, e.caret)
- raise DebuggerException(msg)
+ with Timer("parsing commands"):
+ try:
+ commands, new_source_files = _find_all_commands(test_files, source_root_dir)
+ command_infos = OrderedDict()
+ for command_type in commands:
+ for command in commands[command_type].values():
+ if command_type not in command_infos:
+ command_infos[command_type] = []
+ command_infos[command_type].append(command)
+ return OrderedDict(command_infos), new_source_files
+ except CommandParseError as e:
+ msg = "parser error: <d>{}({}):</> {}\n{}\n{}\n".format(
+ e.filename, e.lineno, e.info, e.src, e.caret
+ )
+ raise DebuggerException(msg)
+
class TestParseCommand(unittest.TestCase):
class MockCmd(CommandBase):
@@ -384,7 +420,7 @@ class TestParseCommand(unittest.TestCase):
"""
def __init__(self, *args):
- self.value = args[0]
+ self.value = args[0]
def get_name():
return __class__.__name__
@@ -392,25 +428,24 @@ class TestParseCommand(unittest.TestCase):
def eval(this):
pass
-
def __init__(self, *args):
super().__init__(*args)
self.valid_commands = {
- TestParseCommand.MockCmd.get_name() : TestParseCommand.MockCmd
+ TestParseCommand.MockCmd.get_name(): TestParseCommand.MockCmd
}
-
def _find_all_commands_in_lines(self, lines):
"""Use DExTer parsing methods to find all the mock commands in lines.
Returns:
{ cmd_name: { (path, line): command_obj } }
"""
- cmds, declared_files = _find_all_commands_in_file(__file__, lines, self.valid_commands, None)
+ cmds, declared_files = _find_all_commands_in_file(
+ __file__, lines, self.valid_commands, None
+ )
return cmds
-
def _find_all_mock_values_in_lines(self, lines):
"""Use DExTer parsing methods to find all mock command values in lines.
@@ -421,36 +456,34 @@ class TestParseCommand(unittest.TestCase):
mocks = cmds.get(TestParseCommand.MockCmd.get_name(), None)
return [v.value for v in mocks.values()] if mocks else []
-
def test_parse_inline(self):
"""Commands can be embedded in other text."""
lines = [
'MockCmd("START") Lorem ipsum dolor sit amet, consectetur\n',
'adipiscing elit, MockCmd("EMBEDDED") sed doeiusmod tempor,\n',
- 'incididunt ut labore et dolore magna aliqua.\n'
+ "incididunt ut labore et dolore magna aliqua.\n",
]
values = self._find_all_mock_values_in_lines(lines)
- self.assertTrue('START' in values)
- self.assertTrue('EMBEDDED' in values)
-
+ self.assertTrue("START" in values)
+ self.assertTrue("EMBEDDED" in values)
def test_parse_multi_line_comment(self):
"""Multi-line commands can embed comments."""
lines = [
- 'Lorem ipsum dolor sit amet, consectetur\n',
- 'adipiscing elit, sed doeiusmod tempor,\n',
- 'incididunt ut labore et MockCmd(\n',
+ "Lorem ipsum dolor sit amet, consectetur\n",
+ "adipiscing elit, sed doeiusmod tempor,\n",
+ "incididunt ut labore et MockCmd(\n",
' "WITH_COMMENT" # THIS IS A COMMENT\n',
- ') dolore magna aliqua. Ut enim ad minim\n',
+ ") dolore magna aliqua. Ut enim ad minim\n",
]
values = self._find_all_mock_values_in_lines(lines)
- self.assertTrue('WITH_COMMENT' in values)
+ self.assertTrue("WITH_COMMENT" in values)
def test_parse_empty(self):
"""Empty files are silently ignored."""
@@ -462,7 +495,7 @@ class TestParseCommand(unittest.TestCase):
def test_parse_bad_whitespace(self):
"""Throw exception when parsing badly formed whitespace."""
lines = [
- 'MockCmd\n',
+ "MockCmd\n",
'("XFAIL_CMD_LF_PAREN")\n',
]
@@ -478,42 +511,38 @@ class TestParseCommand(unittest.TestCase):
'MockCmd\t\t("TABS")\n',
'MockCmd( "ARG_SPACE" )\n',
'MockCmd(\t\t"ARG_TABS"\t\t)\n',
- 'MockCmd(\n',
+ "MockCmd(\n",
'"CMD_PAREN_LF")\n',
]
values = self._find_all_mock_values_in_lines(lines)
- self.assertTrue('NONE' in values)
- self.assertTrue('SPACE' in values)
- self.assertTrue('TABS' in values)
- self.assertTrue('ARG_SPACE' in values)
- self.assertTrue('ARG_TABS' in values)
- self.assertTrue('CMD_PAREN_LF' in values)
-
+ self.assertTrue("NONE" in values)
+ self.assertTrue("SPACE" in values)
+ self.assertTrue("TABS" in values)
+ self.assertTrue("ARG_SPACE" in values)
+ self.assertTrue("ARG_TABS" in values)
+ self.assertTrue("CMD_PAREN_LF" in values)
def test_parse_share_line(self):
"""More than one command can appear on one line."""
lines = [
'MockCmd("START") MockCmd("CONSECUTIVE") words '
- 'MockCmd("EMBEDDED") more words\n'
+ 'MockCmd("EMBEDDED") more words\n'
]
values = self._find_all_mock_values_in_lines(lines)
- self.assertTrue('START' in values)
- self.assertTrue('CONSECUTIVE' in values)
- self.assertTrue('EMBEDDED' in values)
-
+ self.assertTrue("START" in values)
+ self.assertTrue("CONSECUTIVE" in values)
+ self.assertTrue("EMBEDDED" in values)
def test_parse_escaped(self):
"""Escaped commands are ignored."""
- lines = [
- 'words \MockCmd("IGNORED") words words words\n'
- ]
+ lines = ['words \MockCmd("IGNORED") words words words\n']
values = self._find_all_mock_values_in_lines(lines)
- self.assertFalse('IGNORED' in values)
+ self.assertFalse("IGNORED" in values)