Patchwork [4,of,5,V6] testing: allow Hypothesis to enable extensions

login
register
mail settings
Submitter David MacIver
Date Feb. 25, 2016, 1:58 p.m.
Message ID <058757b1826ff2b918ed.1456408727@laser-shark>
Download mbox | patch
Permalink /patch/13384/
State Superseded
Headers show

Comments

David MacIver - Feb. 25, 2016, 1:58 p.m.
# HG changeset patch
# User David R. MacIver <david@drmaciver.com>
# Date 1456319490 0
#      Wed Feb 24 13:11:30 2016 +0000
# Node ID 058757b1826ff2b918edffc6d308b27272dd08d0
# Parent  fc8271f8949ff95932c74c4003a1790af7bca0be
testing: allow Hypothesis to enable extensions

This adds support for testing extensions, including both tests
that extensions don't change behaviour and test for specific
commands.

We use the precondition system to determine what commands are
available to us. If we never use any commands enabled by an
extension then that extension is *skippable* and should not
have changed the behaviour of the test. We thus rerun the test
with an environment variable which is designed to turn off the
extension.
timeless - Feb. 25, 2016, 4:34 p.m.
David R. MacIver wrote:
>  from hypothesis.errors import HypothesisException
> -from hypothesis.stateful import rule, RuleBasedStateMachine, Bundle
> +from hypothesis.stateful import (
> +    rule, RuleBasedStateMachine, Bundle, precondition)

Since you're not done iterating over this, can you make the initial
commit use the parens and only one import per line:

from hypothesis.stateful import (
    rule,
    RuleBasedStateMachine,
    Bundle,
)

And have this patch just add precondition?

(I may have mentioned this on irc, but here's a great example of why.)

Patch

diff -r fc8271f8949f -r 058757b1826f tests/test-verify-repo-operations.py
--- a/tests/test-verify-repo-operations.py	Wed Feb 24 18:36:49 2016 +0000
+++ b/tests/test-verify-repo-operations.py	Wed Feb 24 13:11:30 2016 +0000
@@ -27,7 +27,8 @@ 
 import subprocess
 
 from hypothesis.errors import HypothesisException
-from hypothesis.stateful import rule, RuleBasedStateMachine, Bundle
+from hypothesis.stateful import (
+    rule, RuleBasedStateMachine, Bundle, precondition)
 from hypothesis import settings, note, strategies as st
 from hypothesis.configuration import set_hypothesis_home_dir
 
@@ -144,6 +145,9 @@ 
         self.mkdirp("repo1")
         self.cd("repo1")
         self.hg("init")
+        self.extensions = {}
+        self.all_extensions = set()
+        self.non_skippable_extensions = set()
 
     def teardown(self):
         """On teardown we clean up after ourselves as usual, but we also
@@ -174,6 +178,17 @@ 
         e = None
         if not self.failed:
             try:
+                for ext in (
+                    self.all_extensions - self.non_skippable_extensions
+                ):
+                    try:
+                        os.environ["SKIP_EXTENSION"] = ext
+                        output = subprocess.check_output([
+                            runtests, path, "--local",
+                        ], stderr=subprocess.STDOUT)
+                        assert "Ran 1 test" in output, output
+                    finally:
+                        del os.environ["SKIP_EXTENSION"]
                 output = subprocess.check_output([
                     runtests, path, "--local", "--pure"
                 ], stderr=subprocess.STDOUT)
@@ -457,6 +472,56 @@ 
             else:
                 self.hg("update", "--", branch)
 
+    # Section: Extension management
+    def hasextension(self, extension):
+        repo = self.currentrepo
+        return repo in self.extensions and extension in self.extensions[repo]
+
+    def commandused(self, extension):
+        assert extension in self.all_extensions
+        self.non_skippable_extensions.add(extension)
+
+    @rule(extension=st.sampled_from((
+        'shelve', 'mq', 'blackbox', 'evolve'
+    )))
+    def addextension(self, extension):
+        self.all_extensions.add(extension)
+        extensions = self.extensions.setdefault(self.currentrepo, set())
+        if extension in extensions:
+            return
+        extensions.add(extension)
+        if not os.path.exists(hgrc):
+            self.command("touch", hgrc)
+        with open(hgrc, "r") as i:
+            existing = i.read()
+        if "[extensions]" not in existing:
+            with open(hgrc, "a") as o:
+                o.write("[extensions]\n")
+            self.log.append("$ echo '[extensions]' >> %s" % (hgrc,))
+        with open(hgrc, 'a') as o:
+            line = "%s=" % (extension,)
+            o.write(line)
+            o.write("\n")
+        self.log.append((
+            '$ if [[ "$SKIP_EXTENSION" == "%s" ]; '
+            'then  echo %s >> %s; fi') % (
+                extension, line, hgrc,))
+
+    # Section: Commands from the shelve extension
+    @rule()
+    @precondition(lambda self: self.hasextension("shelve"))
+    def shelve(self):
+        self.commandused("shelve")
+        with acceptableerrors("nothing changed"):
+            self.hg("shelve")
+
+    @rule()
+    @precondition(lambda self: self.hasextension("shelve"))
+    def unshelve(self):
+        self.commandused("shelve")
+        with acceptableerrors("no shelved changes to apply"):
+            self.hg("unshelve")
+
 settings.register_profile(
     'default',  settings(
         timeout=300,