match: add tests for visitdir functionality
authorspectral <spectral@google.com>
Sun, 05 Aug 2018 18:31:19 -0700
changeset 38953 987d3a4b989f
parent 38952 0e58c5b20745
child 38954 5a7df82de142
match: add tests for visitdir functionality There are a few cases that we could have done better with some additional logic; I tried to annotate these when I noticed them, but may have missed some. The tests are not exhaustive; there are certainly some patterns that I didn't test well, and many that I didn't test at all. The primary motivation was to get coverage on visitdir so that I can cover identical cases in a similar method I'm working on, to make sure that this new method behaves the same (or better). Differential Revision: https://phab.mercurial-scm.org/D4128
tests/test-match.py
--- a/tests/test-match.py	Mon Jul 23 22:51:53 2018 -0700
+++ b/tests/test-match.py	Sun Aug 05 18:31:19 2018 -0700
@@ -6,8 +6,23 @@
 
 from mercurial import (
     match as matchmod,
+    util,
 )
 
+class BaseMatcherTests(unittest.TestCase):
+
+    def testVisitdir(self):
+        m = matchmod.basematcher('', '')
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+
+class AlwaysMatcherTests(unittest.TestCase):
+
+    def testVisitdir(self):
+        m = matchmod.alwaysmatcher('', '')
+        self.assertEqual(m.visitdir('.'), 'all')
+        self.assertEqual(m.visitdir('dir'), 'all')
+
 class NeverMatcherTests(unittest.TestCase):
 
     def testVisitdir(self):
@@ -15,5 +30,419 @@
         self.assertFalse(m.visitdir('.'))
         self.assertFalse(m.visitdir('dir'))
 
+class PredicateMatcherTests(unittest.TestCase):
+    # predicatematcher does not currently define either of these methods, so
+    # this is equivalent to BaseMatcherTests.
+
+    def testVisitdir(self):
+        m = matchmod.predicatematcher('', '', lambda *a: False)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+
+class PatternMatcherTests(unittest.TestCase):
+
+    def testVisitdirPrefix(self):
+        m = matchmod.match('x', '', patterns=['path:dir/subdir'])
+        assert isinstance(m, matchmod.patternmatcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertEqual(m.visitdir('dir/subdir'), 'all')
+        # OPT: This should probably be 'all' if its parent is?
+        self.assertTrue(m.visitdir('dir/subdir/x'))
+        self.assertFalse(m.visitdir('folder'))
+
+    def testVisitdirRootfilesin(self):
+        m = matchmod.match('x', '', patterns=['rootfilesin:dir/subdir'])
+        assert isinstance(m, matchmod.patternmatcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertFalse(m.visitdir('dir/subdir/x'))
+        self.assertFalse(m.visitdir('folder'))
+        # FIXME: These should probably be True.
+        self.assertFalse(m.visitdir('dir'))
+        self.assertFalse(m.visitdir('dir/subdir'))
+
+    def testVisitdirGlob(self):
+        m = matchmod.match('x', '', patterns=['glob:dir/z*'])
+        assert isinstance(m, matchmod.patternmatcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertFalse(m.visitdir('folder'))
+        # OPT: these should probably be False.
+        self.assertTrue(m.visitdir('dir/subdir'))
+        self.assertTrue(m.visitdir('dir/subdir/x'))
+
+class IncludeMatcherTests(unittest.TestCase):
+
+    def testVisitdirPrefix(self):
+        m = matchmod.match('x', '', include=['path:dir/subdir'])
+        assert isinstance(m, matchmod.includematcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertEqual(m.visitdir('dir/subdir'), 'all')
+        # OPT: This should probably be 'all' if its parent is?
+        self.assertTrue(m.visitdir('dir/subdir/x'))
+        self.assertFalse(m.visitdir('folder'))
+
+    def testVisitdirRootfilesin(self):
+        m = matchmod.match('x', '', include=['rootfilesin:dir/subdir'])
+        assert isinstance(m, matchmod.includematcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertTrue(m.visitdir('dir/subdir'))
+        self.assertFalse(m.visitdir('dir/subdir/x'))
+        self.assertFalse(m.visitdir('folder'))
+
+    def testVisitdirGlob(self):
+        m = matchmod.match('x', '', include=['glob:dir/z*'])
+        assert isinstance(m, matchmod.includematcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertFalse(m.visitdir('folder'))
+        # OPT: these should probably be False.
+        self.assertTrue(m.visitdir('dir/subdir'))
+        self.assertTrue(m.visitdir('dir/subdir/x'))
+
+class ExactMatcherTests(unittest.TestCase):
+
+    def testVisitdir(self):
+        m = matchmod.match('x', '', patterns=['dir/subdir/foo.txt'], exact=True)
+        assert isinstance(m, matchmod.exactmatcher)
+        self.assertTrue(m.visitdir('.'))
+        self.assertTrue(m.visitdir('dir'))
+        self.assertTrue(m.visitdir('dir/subdir'))
+        self.assertFalse(m.visitdir('dir/subdir/foo.txt'))
+        self.assertFalse(m.visitdir('dir/foo'))
+        self.assertFalse(m.visitdir('dir/subdir/x'))
+        self.assertFalse(m.visitdir('folder'))
+
+class DifferenceMatcherTests(unittest.TestCase):
+
+    def testVisitdirM2always(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.alwaysmatcher('', '')
+        dm = matchmod.differencematcher(m1, m2)
+        # dm should be equivalent to a nevermatcher.
+        self.assertFalse(dm.visitdir('.'))
+        self.assertFalse(dm.visitdir('dir'))
+        self.assertFalse(dm.visitdir('dir/subdir'))
+        self.assertFalse(dm.visitdir('dir/subdir/z'))
+        self.assertFalse(dm.visitdir('dir/foo'))
+        self.assertFalse(dm.visitdir('dir/subdir/x'))
+        self.assertFalse(dm.visitdir('folder'))
+
+    def testVisitdirM2never(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.nevermatcher('', '')
+        dm = matchmod.differencematcher(m1, m2)
+        # dm should be equivalent to a alwaysmatcher. OPT: if m2 is a
+        # nevermatcher, we could return 'all' for these.
+        #
+        # We're testing Equal-to-True instead of just 'assertTrue' since
+        # assertTrue does NOT verify that it's a bool, just that it's truthy.
+        # While we may want to eventually make these return 'all', they should
+        # not currently do so.
+        self.assertEqual(dm.visitdir('.'), True)
+        self.assertEqual(dm.visitdir('dir'), True)
+        self.assertEqual(dm.visitdir('dir/subdir'), True)
+        self.assertEqual(dm.visitdir('dir/subdir/z'), True)
+        self.assertEqual(dm.visitdir('dir/foo'), True)
+        self.assertEqual(dm.visitdir('dir/subdir/x'), True)
+        self.assertEqual(dm.visitdir('folder'), True)
+
+    def testVisitdirM2SubdirPrefix(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
+        dm = matchmod.differencematcher(m1, m2)
+        self.assertEqual(dm.visitdir('.'), True)
+        self.assertEqual(dm.visitdir('dir'), True)
+        self.assertFalse(dm.visitdir('dir/subdir'))
+        # OPT: We should probably return False for these; we don't because
+        # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
+        # an 'all' pattern, just True.
+        self.assertEqual(dm.visitdir('dir/subdir/z'), True)
+        self.assertEqual(dm.visitdir('dir/subdir/x'), True)
+        # OPT: We could return 'all' for these.
+        self.assertEqual(dm.visitdir('dir/foo'), True)
+        self.assertEqual(dm.visitdir('folder'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeIncludfe(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir'])
+        m2 = matchmod.match('', '', include=['rootfilesin:dir'])
+        dm = matchmod.differencematcher(m1, m2)
+        self.assertEqual(dm.visitdir('.'), True)
+        self.assertEqual(dm.visitdir('dir'), True)
+        self.assertEqual(dm.visitdir('dir/subdir'), True)
+        self.assertFalse(dm.visitdir('dir/foo'))
+        self.assertFalse(dm.visitdir('folder'))
+        # OPT: We should probably return False for these; we don't because
+        # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
+        # an 'all' pattern, just True.
+        self.assertEqual(dm.visitdir('dir/subdir/z'), True)
+        self.assertEqual(dm.visitdir('dir/subdir/x'), True)
+
+class IntersectionMatcherTests(unittest.TestCase):
+
+    def testVisitdirM2always(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.alwaysmatcher('', '')
+        im = matchmod.intersectmatchers(m1, m2)
+        # im should be equivalent to a alwaysmatcher.
+        self.assertEqual(im.visitdir('.'), 'all')
+        self.assertEqual(im.visitdir('dir'), 'all')
+        self.assertEqual(im.visitdir('dir/subdir'), 'all')
+        self.assertEqual(im.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(im.visitdir('dir/foo'), 'all')
+        self.assertEqual(im.visitdir('dir/subdir/x'), 'all')
+        self.assertEqual(im.visitdir('folder'), 'all')
+
+    def testVisitdirM2never(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.nevermatcher('', '')
+        im = matchmod.intersectmatchers(m1, m2)
+        # im should be equivalent to a nevermatcher.
+        self.assertFalse(im.visitdir('.'))
+        self.assertFalse(im.visitdir('dir'))
+        self.assertFalse(im.visitdir('dir/subdir'))
+        self.assertFalse(im.visitdir('dir/subdir/z'))
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('dir/subdir/x'))
+        self.assertFalse(im.visitdir('folder'))
+
+    def testVisitdirM2SubdirPrefix(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
+        im = matchmod.intersectmatchers(m1, m2)
+        self.assertEqual(im.visitdir('.'), True)
+        self.assertEqual(im.visitdir('dir'), True)
+        self.assertEqual(im.visitdir('dir/subdir'), 'all')
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('folder'))
+        # OPT: We should probably return 'all' for these; we don't because
+        # patternmatcher.visitdir() (our m2) doesn't return 'all' for subdirs of
+        # an 'all' pattern, just True.
+        self.assertEqual(im.visitdir('dir/subdir/z'), True)
+        self.assertEqual(im.visitdir('dir/subdir/x'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeIncludfe(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir'])
+        m2 = matchmod.match('', '', include=['rootfilesin:dir'])
+        im = matchmod.intersectmatchers(m1, m2)
+        self.assertEqual(im.visitdir('.'), True)
+        self.assertEqual(im.visitdir('dir'), True)
+        self.assertFalse(im.visitdir('dir/subdir'))
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('folder'))
+        self.assertFalse(im.visitdir('dir/subdir/z'))
+        self.assertFalse(im.visitdir('dir/subdir/x'))
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude2(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir'])
+        m2 = matchmod.match('', '', include=['path:folder'])
+        im = matchmod.intersectmatchers(m1, m2)
+        # FIXME: is True correct here?
+        self.assertEqual(im.visitdir('.'), True)
+        self.assertFalse(im.visitdir('dir'))
+        self.assertFalse(im.visitdir('dir/subdir'))
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('folder'))
+        self.assertFalse(im.visitdir('dir/subdir/z'))
+        self.assertFalse(im.visitdir('dir/subdir/x'))
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude3(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
+        m2 = matchmod.match('', '', include=['path:dir/subdir'])
+        im = matchmod.intersectmatchers(m1, m2)
+        self.assertEqual(im.visitdir('.'), True)
+        self.assertEqual(im.visitdir('dir'), True)
+        self.assertEqual(im.visitdir('dir/subdir'), True)
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('folder'))
+        self.assertFalse(im.visitdir('dir/subdir/z'))
+        # OPT: this should probably be 'all' not True.
+        self.assertEqual(im.visitdir('dir/subdir/x'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude4(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
+        m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
+        im = matchmod.intersectmatchers(m1, m2)
+        # OPT: these next three could probably be False as well.
+        self.assertEqual(im.visitdir('.'), True)
+        self.assertEqual(im.visitdir('dir'), True)
+        self.assertEqual(im.visitdir('dir/subdir'), True)
+        self.assertFalse(im.visitdir('dir/foo'))
+        self.assertFalse(im.visitdir('folder'))
+        self.assertFalse(im.visitdir('dir/subdir/z'))
+        self.assertFalse(im.visitdir('dir/subdir/x'))
+
+class UnionMatcherTests(unittest.TestCase):
+
+    def testVisitdirM2always(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.alwaysmatcher('', '')
+        um = matchmod.unionmatcher([m1, m2])
+        # um should be equivalent to a alwaysmatcher.
+        self.assertEqual(um.visitdir('.'), 'all')
+        self.assertEqual(um.visitdir('dir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(um.visitdir('dir/foo'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+        self.assertEqual(um.visitdir('folder'), 'all')
+
+    def testVisitdirM1never(self):
+        m1 = matchmod.nevermatcher('', '')
+        m2 = matchmod.alwaysmatcher('', '')
+        um = matchmod.unionmatcher([m1, m2])
+        # um should be equivalent to a alwaysmatcher.
+        self.assertEqual(um.visitdir('.'), 'all')
+        self.assertEqual(um.visitdir('dir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(um.visitdir('dir/foo'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+        self.assertEqual(um.visitdir('folder'), 'all')
+
+    def testVisitdirM2never(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.nevermatcher('', '')
+        um = matchmod.unionmatcher([m1, m2])
+        # um should be equivalent to a alwaysmatcher.
+        self.assertEqual(um.visitdir('.'), 'all')
+        self.assertEqual(um.visitdir('dir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(um.visitdir('dir/foo'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+        self.assertEqual(um.visitdir('folder'), 'all')
+
+    def testVisitdirM2SubdirPrefix(self):
+        m1 = matchmod.alwaysmatcher('', '')
+        m2 = matchmod.match('', '', patterns=['path:dir/subdir'])
+        um = matchmod.unionmatcher([m1, m2])
+        self.assertEqual(um.visitdir('.'), 'all')
+        self.assertEqual(um.visitdir('dir'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertEqual(um.visitdir('dir/foo'), 'all')
+        self.assertEqual(um.visitdir('folder'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeIncludfe(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir'])
+        m2 = matchmod.match('', '', include=['rootfilesin:dir'])
+        um = matchmod.unionmatcher([m1, m2])
+        self.assertEqual(um.visitdir('.'), True)
+        self.assertEqual(um.visitdir('dir'), True)
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertFalse(um.visitdir('dir/foo'))
+        self.assertFalse(um.visitdir('folder'))
+        # OPT: These two should probably be 'all' not True.
+        self.assertEqual(um.visitdir('dir/subdir/z'), True)
+        self.assertEqual(um.visitdir('dir/subdir/x'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude2(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir'])
+        m2 = matchmod.match('', '', include=['path:folder'])
+        um = matchmod.unionmatcher([m1, m2])
+        self.assertEqual(um.visitdir('.'), True)
+        self.assertEqual(um.visitdir('dir'), True)
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertFalse(um.visitdir('dir/foo'))
+        self.assertEqual(um.visitdir('folder'), 'all')
+        # OPT: These should probably be 'all' not True.
+        self.assertEqual(um.visitdir('dir/subdir/z'), True)
+        self.assertEqual(um.visitdir('dir/subdir/x'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude3(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
+        m2 = matchmod.match('', '', include=['path:dir/subdir'])
+        um = matchmod.unionmatcher([m1, m2])
+        self.assertEqual(um.visitdir('.'), True)
+        self.assertEqual(um.visitdir('dir'), True)
+        self.assertEqual(um.visitdir('dir/subdir'), 'all')
+        self.assertFalse(um.visitdir('dir/foo'))
+        self.assertFalse(um.visitdir('folder'))
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+        # OPT: this should probably be 'all' not True.
+        self.assertEqual(um.visitdir('dir/subdir/z'), True)
+
+    # We're using includematcher instead of patterns because it behaves slightly
+    # better (giving narrower results) than patternmatcher.
+    def testVisitdirIncludeInclude4(self):
+        m1 = matchmod.match('', '', include=['path:dir/subdir/x'])
+        m2 = matchmod.match('', '', include=['path:dir/subdir/z'])
+        um = matchmod.unionmatcher([m1, m2])
+        # OPT: these next three could probably be False as well.
+        self.assertEqual(um.visitdir('.'), True)
+        self.assertEqual(um.visitdir('dir'), True)
+        self.assertEqual(um.visitdir('dir/subdir'), True)
+        self.assertFalse(um.visitdir('dir/foo'))
+        self.assertFalse(um.visitdir('folder'))
+        self.assertEqual(um.visitdir('dir/subdir/z'), 'all')
+        self.assertEqual(um.visitdir('dir/subdir/x'), 'all')
+
+class SubdirMatcherTests(unittest.TestCase):
+
+    def testVisitdir(self):
+        m = matchmod.match('', '', include=['path:dir/subdir'])
+        sm = matchmod.subdirmatcher('dir', m)
+
+        self.assertEqual(sm.visitdir('.'), True)
+        self.assertEqual(sm.visitdir('subdir'), 'all')
+        # OPT: These next two should probably be 'all' not True.
+        self.assertEqual(sm.visitdir('subdir/x'), True)
+        self.assertEqual(sm.visitdir('subdir/z'), True)
+        self.assertFalse(sm.visitdir('foo'))
+
+class PrefixdirMatcherTests(unittest.TestCase):
+
+    def testVisitdir(self):
+        m = matchmod.match(util.localpath('root/d'), 'e/f',
+                ['../a.txt', 'b.txt'])
+        pm = matchmod.prefixdirmatcher('root', 'd/e/f', 'd', m)
+
+        # `m` elides 'd' because it's part of the root, and the rest of the
+        # patterns are relative.
+        self.assertEqual(bool(m('a.txt')), False)
+        self.assertEqual(bool(m('b.txt')), False)
+        self.assertEqual(bool(m('e/a.txt')), True)
+        self.assertEqual(bool(m('e/b.txt')), False)
+        self.assertEqual(bool(m('e/f/b.txt')), True)
+
+        # The prefix matcher re-adds 'd' to the paths, so they need to be
+        # specified when using the prefixdirmatcher.
+        self.assertEqual(bool(pm('a.txt')), False)
+        self.assertEqual(bool(pm('b.txt')), False)
+        self.assertEqual(bool(pm('d/e/a.txt')), True)
+        self.assertEqual(bool(pm('d/e/b.txt')), False)
+        self.assertEqual(bool(pm('d/e/f/b.txt')), True)
+
+        self.assertEqual(m.visitdir('.'), True)
+        self.assertEqual(m.visitdir('e'), True)
+        self.assertEqual(m.visitdir('e/f'), True)
+        self.assertEqual(m.visitdir('e/f/g'), False)
+
+        self.assertEqual(pm.visitdir('.'), True)
+        self.assertEqual(pm.visitdir('d'), True)
+        self.assertEqual(pm.visitdir('d/e'), True)
+        self.assertEqual(pm.visitdir('d/e/f'), True)
+        self.assertEqual(pm.visitdir('d/e/f/g'), False)
+
 if __name__ == '__main__':
     silenttestrunner.main(__name__)