summaryrefslogtreecommitdiffstats
path: root/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py
diff options
context:
space:
mode:
authorJeremy Morse <jeremy.morse@sony.com>2019-10-31 13:41:24 +0000
committerJeremy Morse <jeremy.morse@sony.com>2019-10-31 13:49:47 +0000
commitf78c236efda85af1e526ac35ed535ef4786450e3 (patch)
tree1ff1609178e085f58b46dfcbce21fd6b2ef40025 /debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py
parentefacf2ce55d698e5df8173f0d4dacbc7d3c7fd34 (diff)
downloadbcm5719-llvm-f78c236efda85af1e526ac35ed535ef4786450e3.tar.gz
bcm5719-llvm-f78c236efda85af1e526ac35ed535ef4786450e3.zip
Import Dexter to debuginfo-tests
Dexter (Debug Experience Tester) is a test-driver for our debug info integration tests, reading a set of debug experience expectations and comparing them with the actual behaviour of a program under a debugger. More about Dexter can be found in the RFC: http://lists.llvm.org/pipermail/llvm-dev/2019-October/135773.html and the phab review in D68708. Not all the debuginfo tests have been transformed into Dexter tests, and we look forwards to doing that incrementally. This commit mostly aims to flush out buildbots that are running debuginfo-tests but don't have python 3 installed, possibly green-dragon and some windows bots.
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