diff options
author | Stephen Tozer <stephen.tozer@sony.com> | 2022-04-28 15:01:28 +0100 |
---|---|---|
committer | Stephen Tozer <stephen.tozer@sony.com> | 2022-06-13 14:44:28 +0100 |
commit | 30bb659c6f992d2dcf03cfc1d19c560f704035f6 (patch) | |
tree | da8e895448db83cae66b38078657393ac9d567b8 /cross-project-tests | |
parent | 3f4a63e5f882eb062da33a5643499597efefe1ba (diff) | |
download | llvm-30bb659c6f992d2dcf03cfc1d19c560f704035f6.zip llvm-30bb659c6f992d2dcf03cfc1d19c560f704035f6.tar.gz llvm-30bb659c6f992d2dcf03cfc1d19c560f704035f6.tar.bz2 |
[Dexter] Allow Dexter watch commands to specify a range of acceptable FP values
This patch adds an optional argument to DexExpectWatchBase, float_range,
which defines a +- acceptance range for expected floating point values.
If passed, this assumes every expected value to be a floating point
value, and an exception will be thrown if this is not the case.
Differential Revision: https://reviews.llvm.org/D124511
Diffstat (limited to 'cross-project-tests')
10 files changed, 159 insertions, 10 deletions
diff --git a/cross-project-tests/debuginfo-tests/dexter/Commands.md b/cross-project-tests/debuginfo-tests/dexter/Commands.md index e151103..d5bd999 100644 --- a/cross-project-tests/debuginfo-tests/dexter/Commands.md +++ b/cross-project-tests/debuginfo-tests/dexter/Commands.md @@ -144,7 +144,7 @@ type checked against the list of `types` --- ## DexExpectWatchValue DexExpectWatchValue(expr, *values [,**from_line=1][,**to_line=Max] - [,**on_line][,**require_in_order=True]) + [,**on_line][,**require_in_order=True][,**float_range]) Args: expr (str): expression to evaluate. @@ -159,6 +159,9 @@ type checked against the list of `types` on_line (int): Only evaluate the expression on this line. If provided, this overrides from_line and to_line. require_in_order (bool): If False the values can appear in any order. + float_range (float): If provided, `values` must be floats, and will + match an actual value if they are within `float_range` of each other. + ### Description Expect the expression `expr` to evaluate to the list of `values` 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 cd93b2b..1027ba0 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/ParseCommand.py @@ -15,7 +15,7 @@ from copy import copy from pathlib import PurePath from collections import defaultdict, OrderedDict -from dex.utils.Exceptions import CommandParseError +from dex.utils.Exceptions import CommandParseError, NonFloatValueInCommand from dex.command.CommandBase import CommandBase from dex.command.commands.DexCommandLine import DexCommandLine @@ -310,6 +310,10 @@ def _find_all_commands_in_file(path, file_lines, valid_commands, source_root_dir err_point = copy(cmd_point) err_point.char += len(command_name) raise format_parse_err(str(e), path, file_lines, err_point) + except NonFloatValueInCommand as e: + err_point = copy(cmd_point) + err_point.char += len(command_name) + raise format_parse_err(str(e), path, file_lines, err_point) else: if type(command) is DexLabel: add_line_label(labels, command, path, cmd_point.get_lineno()) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py index 6dd30ad..e79b1bb 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/command/commands/DexExpectWatchBase.py @@ -18,6 +18,7 @@ from pathlib import PurePath from dex.command.CommandBase import CommandBase, StepExpectInfo from dex.command.StepValueInfo import StepValueInfo +from dex.utils.Exceptions import NonFloatValueInCommand class AddressExpression(object): def __init__(self, name, offset=0): @@ -56,6 +57,13 @@ class DexExpectWatchBase(CommandBase): 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) + self.float_range = kwargs.pop('float_range', None) + if self.float_range is not None: + for value in self.values: + try: + float(value) + except ValueError: + raise NonFloatValueInCommand(f'Non-float value \'{value}\' when float_range arg provided') if kwargs: raise TypeError('unexpected named args: {}'.format( ', '.join(kwargs))) @@ -135,6 +143,33 @@ class DexExpectWatchBase(CommandBase): """Return a field from watch that this ExpectWatch command is checking. """ + def _match_expected_floating_point(self, value): + """Checks to see whether value is a float that falls within the + acceptance range of one of this command's expected float values, and + returns the expected value if so; otherwise returns the original + value.""" + try: + value_as_float = float(value) + except ValueError: + return value + + possible_values = self.values + for expected in possible_values: + try: + expected_as_float = float(expected) + difference = abs(value_as_float - expected_as_float) + if difference <= self.float_range: + return expected + except ValueError: + pass + return value + + def _maybe_fix_float(self, value): + if self.float_range is not None: + return self._match_expected_floating_point(value) + else: + return value + def _handle_watch(self, step_info): self.times_encountered += 1 @@ -150,23 +185,25 @@ class DexExpectWatchBase(CommandBase): self.irretrievable_watches.append(step_info) return + expected_value = self._maybe_fix_float(step_info.expected_value) + # Check to see if this value matches with a resolved address. matching_address = None for v in self.values: if (isinstance(v, AddressExpression) and v.name in self.address_resolutions and - self.resolve_value(v) == step_info.expected_value): + self.resolve_value(v) == expected_value): matching_address = v break # If this is not an expected value, either a direct value or an address, # then this is an unexpected watch. - if step_info.expected_value not in self.values and matching_address is None: + if expected_value not in self.values and matching_address is None: self.unexpected_watches.append(step_info) return self.expected_watches.append(step_info) - value_to_remove = matching_address if matching_address is not None else step_info.expected_value + value_to_remove = matching_address if matching_address is not None else expected_value try: self._missing_values.remove(value_to_remove) except KeyError: @@ -177,7 +214,7 @@ class DexExpectWatchBase(CommandBase): or not. """ differences = [] - actual_values = [w.expected_value for w in actual_watches] + actual_values = [self._maybe_fix_float(w.expected_value) for w in actual_watches] value_differences = list(difflib.Differ().compare(actual_values, expected_values)) @@ -229,14 +266,16 @@ class DexExpectWatchBase(CommandBase): # A list of all watches where the value has changed. value_change_watches = [] prev_value = None + all_expected_values = [] for watch in self.expected_watches: - if watch.expected_value != prev_value: + expected_value = self._maybe_fix_float(watch.expected_value) + all_expected_values.append(expected_value) + if expected_value != prev_value: value_change_watches.append(watch) - prev_value = watch.expected_value + prev_value = expected_value resolved_values = [self.resolve_value(v) for v in self.values] self.misordered_watches = self._check_watch_order( value_change_watches, [ - v for v in resolved_values if v in - [w.expected_value for w in self.expected_watches] + v for v in resolved_values if v in all_expected_values ]) diff --git a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py index 39c0c2f..b636acb 100644 --- a/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py +++ b/cross-project-tests/debuginfo-tests/dexter/dex/utils/Exceptions.py @@ -54,6 +54,14 @@ class CommandParseError(Dexception): self.caret = None +class NonFloatValueInCommand(CommandParseError): + """If a command has the float_range arg but at least one of its expected + values cannot be converted to a float.""" + + def __init__(self, *args, **kwargs): + super(NonFloatValueInCommand, self).__init__(*args, **kwargs) + self.value = None + class ToolArgumentError(Dexception): """If a tool argument is invalid.""" pass diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_out_range.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_out_range.cpp new file mode 100644 index 0000000..519a99a --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_out_range.cpp @@ -0,0 +1,16 @@ +// Purpose: +// Check that a \DexExpectWatchValue float_range that is not large enough +// detects unexpected watch values. +// +// UNSUPPORTED: system-darwin +// +// RUN: not %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_out_range.cpp: + +int main() { + float a = 1.0f; + a = a - 0.5f; + return a; //DexLabel('check') +} + +// DexExpectWatchValue('a', '1.00000', from_line=ref('check1'), to_line=ref('check2'), float_range=0.4) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_zero_nonmatch.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_zero_nonmatch.cpp new file mode 100644 index 0000000..e7e04c9 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/penalty/float_range_zero_nonmatch.cpp @@ -0,0 +1,15 @@ +// Purpose: +// Check that \DexExpectWatchValue float_range=0.0 matches only exact +// values. +// +// UNSUPPORTED: system-darwin +// +// RUN: not %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_zero_nonmatch.cpp: + +int main() { + float a = 1.0f; + return a; //DexLabel('check') +} + +// DexExpectWatchValue('a', '1.0000001', on_line=ref('check'), float_range=0.0) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_multiple.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_multiple.cpp new file mode 100644 index 0000000..5ca1c74 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_multiple.cpp @@ -0,0 +1,18 @@ +// Purpose: +// Check that \DexExpectWatchValue float_range=0.5 considers a range +// difference of 0.49999 to be an expected watch value for multple values. +// +// UNSUPPORTED: system-darwin +// +// RUN: %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_multiple.cpp: + +int main() { + float a = 1.0f; + float b = 100.f; + a = a + 0.4999f; + a = a + b; // DexLabel('check1') + return a; //DexLabel('check2') +} + +// DexExpectWatchValue('a', '1.0', '100.0', from_line=ref('check1'), to_line=ref('check2'), float_range=0.5) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_no_arg.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_no_arg.cpp new file mode 100644 index 0000000..b9e1d22 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_no_arg.cpp @@ -0,0 +1,16 @@ +// Purpose: +// Check that omitted float_range from \DexExpectWatchValue turns off +// the floating point range evalution and defaults back to +// pre-float evalution. +// +// UNSUPPORTED: system-darwin +// +// RUN: %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_no_arg.cpp: + +int main() { + float a = 1.0f; + return a; //DexLabel('check') +} + +// DexExpectWatchValue('a', '1.00000', on_line=ref('check')) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_small.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_small.cpp new file mode 100644 index 0000000..6a844e7 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_small.cpp @@ -0,0 +1,16 @@ +// Purpose: +// Check that \DexExpectWatchValue float_range=0.5 considers a range +// difference of 0.49999 to be an expected watch value. +// +// UNSUPPORTED: system-darwin +// +// RUN: %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_small.cpp: + +int main() { + float a = 1.0f; + a = a - 0.49999f; + return a; //DexLabel('check') +} + +// DexExpectWatchValue('a', '1.0', on_line=ref('check'), float_range=0.5) diff --git a/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_zero_match.cpp b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_zero_match.cpp new file mode 100644 index 0000000..7dbd515 --- /dev/null +++ b/cross-project-tests/debuginfo-tests/dexter/feature_tests/commands/perfect/float_range_watch/float_range_zero_match.cpp @@ -0,0 +1,14 @@ +// Purpose: +// Check that \DexExpectWatchValue float_range=0.0 matches exact values. +// +// UNSUPPORTED: system-darwin +// +// RUN: %dexter_regression_test -- %s | FileCheck %s +// CHECK: float_range_zero_match.cpp: + +int main() { + float a = 1.0f; + return a; //DexLabel('check') +} + +// DexExpectWatchValue('a', '1.0000000', on_line=ref('check'), float_range=0.0) |