summaryrefslogtreecommitdiffstats
path: root/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py
diff options
context:
space:
mode:
Diffstat (limited to 'debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py')
-rw-r--r--debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py197
1 files changed, 197 insertions, 0 deletions
diff --git a/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py b/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py
new file mode 100644
index 00000000000..e6422d14098
--- /dev/null
+++ b/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py
@@ -0,0 +1,197 @@
+# DExTer : Debugging Experience Tester
+# ~~~~~~ ~ ~~ ~ ~~
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+"""DexExpectWatch base class, holds logic for how to build and process expected
+ watch commands.
+"""
+
+import abc
+import difflib
+import os
+
+from dex.command.CommandBase import CommandBase
+from dex.command.StepValueInfo import StepValueInfo
+
+
+class DexExpectWatchBase(CommandBase):
+ def __init__(self, *args, **kwargs):
+ if len(args) < 2:
+ raise TypeError('expected at least two args')
+
+ self.expression = args[0]
+ self.values = [str(arg) for arg in args[1:]]
+ try:
+ on_line = kwargs.pop('on_line')
+ self._from_line = on_line
+ self._to_line = on_line
+ except KeyError:
+ self._from_line = kwargs.pop('from_line', 1)
+ self._to_line = kwargs.pop('to_line', 999999)
+ self._require_in_order = kwargs.pop('require_in_order', True)
+ if kwargs:
+ raise TypeError('unexpected named args: {}'.format(
+ ', '.join(kwargs)))
+
+ # Number of times that this watch has been encountered.
+ self.times_encountered = 0
+
+ # We'll pop from this set as we encounter values so anything left at
+ # the end can be considered as not having been seen.
+ self._missing_values = set(self.values)
+
+ self.misordered_watches = []
+
+ # List of StepValueInfos for any watch that is encountered as invalid.
+ self.invalid_watches = []
+
+ # List of StepValueInfo any any watch where we couldn't retrieve its
+ # data.
+ self.irretrievable_watches = []
+
+ # List of StepValueInfos for any watch that is encountered as having
+ # been optimized out.
+ self.optimized_out_watches = []
+
+ # List of StepValueInfos for any watch that is encountered that has an
+ # expected value.
+ self.expected_watches = []
+
+ # List of StepValueInfos for any watch that is encountered that has an
+ # unexpected value.
+ self.unexpected_watches = []
+
+ super(DexExpectWatchBase, self).__init__()
+
+
+ def get_watches(self):
+ return [self.expression]
+
+ @property
+ def line_range(self):
+ return list(range(self._from_line, self._to_line + 1))
+
+ @property
+ def missing_values(self):
+ return sorted(list(self._missing_values))
+
+ @property
+ def encountered_values(self):
+ return sorted(list(set(self.values) - self._missing_values))
+
+
+ def resolve_label(self, label_line_pair):
+ # from_line and to_line could have the same label.
+ label, lineno = label_line_pair
+ if self._to_line == label:
+ self._to_line = lineno
+ if self._from_line == label:
+ self._from_line = lineno
+
+ def has_labels(self):
+ return len(self.get_label_args()) > 0
+
+ def get_label_args(self):
+ return [label for label in (self._from_line, self._to_line)
+ if isinstance(label, str)]
+
+ @abc.abstractmethod
+ def _get_expected_field(self, watch):
+ """Return a field from watch that this ExpectWatch command is checking.
+ """
+
+ def _handle_watch(self, step_info):
+ self.times_encountered += 1
+
+ if not step_info.watch_info.could_evaluate:
+ self.invalid_watches.append(step_info)
+ return
+
+ if step_info.watch_info.is_optimized_away:
+ self.optimized_out_watches.append(step_info)
+ return
+
+ if step_info.watch_info.is_irretrievable:
+ self.irretrievable_watches.append(step_info)
+ return
+
+ if step_info.expected_value not in self.values:
+ self.unexpected_watches.append(step_info)
+ return
+
+ self.expected_watches.append(step_info)
+ try:
+ self._missing_values.remove(step_info.expected_value)
+ except KeyError:
+ pass
+
+ def _check_watch_order(self, actual_watches, expected_values):
+ """Use difflib to figure out whether the values are in the expected order
+ or not.
+ """
+ differences = []
+ actual_values = [w.expected_value for w in actual_watches]
+ value_differences = list(difflib.Differ().compare(actual_values,
+ expected_values))
+
+ missing_value = False
+ index = 0
+ for vd in value_differences:
+ kind = vd[0]
+ if kind == '+':
+ # A value that is encountered in the expected list but not in the
+ # actual list. We'll keep a note that something is wrong and flag
+ # the next value that matches as misordered.
+ missing_value = True
+ elif kind == ' ':
+ # This value is as expected. It might still be wrong if we've
+ # previously encountered a value that is in the expected list but
+ # not the actual list.
+ if missing_value:
+ missing_value = False
+ differences.append(actual_watches[index])
+ index += 1
+ elif kind == '-':
+ # A value that is encountered in the actual list but not the
+ # expected list.
+ differences.append(actual_watches[index])
+ index += 1
+ else:
+ assert False, 'unexpected diff:{}'.format(vd)
+
+ return differences
+
+ def eval(self, step_collection):
+ for step in step_collection.steps:
+ loc = step.current_location
+
+ if (os.path.exists(loc.path) and os.path.exists(self.path) and
+ os.path.samefile(loc.path, self.path) and
+ loc.lineno in self.line_range):
+ try:
+ watch = step.program_state.frames[0].watches[self.expression]
+ except KeyError:
+ pass
+ else:
+ expected_field = self._get_expected_field(watch)
+ step_info = StepValueInfo(step.step_index, watch,
+ expected_field)
+ self._handle_watch(step_info)
+
+ if self._require_in_order:
+ # A list of all watches where the value has changed.
+ value_change_watches = []
+ prev_value = None
+ for watch in self.expected_watches:
+ if watch.expected_value != prev_value:
+ value_change_watches.append(watch)
+ prev_value = watch.expected_value
+
+ self.misordered_watches = self._check_watch_order(
+ value_change_watches, [
+ v for v in self.values if v in
+ [w.expected_value for w in self.expected_watches]
+ ])
OpenPOWER on IntegriCloud