[Lldb-commits] [PATCH] D91193: [lldb] Fine tune expect() validation

Dave Lee via Phabricator via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 10 11:37:38 PST 2020


kastiglione created this revision.
kastiglione added reviewers: jingham, teemperor, aprantl, JDevlieghere.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
kastiglione requested review of this revision.

Restore ability to call `expect()` with a message and no matcher.

After the changes in D88792 <https://reviews.llvm.org/D88792>, `expect` can no longer be called as:

  self.expect("some command", "some message")

After speaking to @jingham, I now understand that an uncommon use case is to
check only that a command succeeds, without validating its output. The changes
in D88792 <https://reviews.llvm.org/D88792> prevent such expectations.

This change restores the ability to call `expect` this way, but with the
requirement that the `msg` argument be a keyword argument. Requiring `msg` be a
keyword argument provides the ability to ensure `expect`'s arguments are
explict, and not ambiguous. Example:

  self.expect("some command", msg="some message")

When lldb supports only Python 3, the easy solution will be to change the
signature of `expect` by require all arguments be keyword ags using its `*`
syntax:

  def expect(
          self,
          str,
          *,
          msg=None,
          patterns=None,
          startstr=None,
          endstr=None,
          substrs=None,
          trace=False,
          error=False,
          ordered=True,
          matching=True,
          exe=True,
          inHistory=False):

But the `*` syntax is not supported in Python 2. To work around this, this change renames `expect` to `_expect_impl`, and implements `expect` as a separate function with a signature that separates positional args from keyword args, in order to perform validation.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91193

Files:
  lldb/packages/Python/lldbsuite/test/lldbtest.py
  lldb/test/API/assert_messages_test/TestAssertMessages.py


Index: lldb/test/API/assert_messages_test/TestAssertMessages.py
===================================================================
--- lldb/test/API/assert_messages_test/TestAssertMessages.py
+++ lldb/test/API/assert_messages_test/TestAssertMessages.py
@@ -122,10 +122,6 @@
         self.assert_expect_fails_with("any command",
             dict(substrs="some substring"),
             "substrs must be a collection of strings")
-        # Prevent `self.expect("cmd", "substr")`
-        self.assert_expect_fails_with("any command",
-            dict(msg="some substring"),
-            "expect() missing a matcher argument")
         # Prevent `self.expect("cmd", "msg", "substr")`
         self.assert_expect_fails_with("any command",
             dict(msg="a message", patterns="some substring"),
Index: lldb/packages/Python/lldbsuite/test/lldbtest.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -2388,7 +2388,35 @@
 
         self.assertTrue(cmd_status == 0)
 
-    def expect(
+
+    def expect(self, str, *args, **kwargs):
+        "Validate `expect` arguments before calling `_expect_impl`"
+        # Support success/fail only checks like:
+        #     self.expect("lldb command")
+        if not args and not kwargs:
+            return self._expect(str)
+
+        # Prevent this ambiguous mistake:
+        #     self.expect("lldb command", "some string")
+        # Instead, this invocation must be changed to either:
+        #     self.expect("lldb command", msg="some string")
+        # or use a matcher of some kind, such as:
+        #     self.expect("lldb command", substrs=["some string"])
+        if args:
+            needed = ("patterns", "startstr", "endstr", "substrs", "error")
+            assert any(arg in kwargs for arg in needed), \
+                "expect() requires a keyword argument"
+
+        # Check `patterns` and `substrs` are not accidentally given as strings.
+        assert not isinstance(kwargs.get("patterns"), six.string_types), \
+            "patterns must be a collection of strings"
+        assert not isinstance(kwargs.get("substrs"), six.string_types), \
+            "substrs must be a collection of strings"
+
+        return self._expect(str, **kwargs)
+
+
+    def _expect_impl(
             self,
             str,
             msg=None,
@@ -2429,22 +2457,6 @@
         set to False, the 'str' is treated as a string to be matched/not-matched
         against the golden input.
         """
-        # Catch cases where `expect` has been miscalled. Specifically, prevent
-        # this easy to make mistake:
-        #     self.expect("lldb command", "some substr")
-        # The `msg` parameter is used only when a failed match occurs. A failed
-        # match can only occur when one of `patterns`, `startstr`, `endstr`, or
-        # `substrs` has been given. Thus, if a `msg` is given, it's an error to
-        # not also provide one of the matcher parameters.
-        if msg and not (patterns or startstr or endstr or substrs or error):
-            assert False, "expect() missing a matcher argument"
-
-        # Check `patterns` and `substrs` are not accidentally given as strings.
-        assert not isinstance(patterns, six.string_types), \
-            "patterns must be a collection of strings"
-        assert not isinstance(substrs, six.string_types), \
-            "substrs must be a collection of strings"
-
         trace = (True if traceAlways else trace)
 
         if exe:


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D91193.304274.patch
Type: text/x-patch
Size: 3596 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20201110/398b6d66/attachment.bin>


More information about the lldb-commits mailing list