bug 1478613, refactor tests for compare_locales.paths, r?stas draft
authorAxel Hecht <axel@pike.org>
Thu, 26 Jul 2018 14:50:44 +0200
changeset 642 35f0161682711d9b91febd695796ac31d7b85805
parent 641 53d8552ac04726cd42658770f70413ffd8690695
push id197
push useraxel@mozilla.com
push dateThu, 26 Jul 2018 16:18:26 +0000
reviewersstas
bugs1478613
bug 1478613, refactor tests for compare_locales.paths, r?stas MozReview-Commit-ID: 3GTDg0gBtY
compare_locales/tests/paths/__init__.py
compare_locales/tests/paths/test_files.py
compare_locales/tests/paths/test_ini.py
compare_locales/tests/paths/test_matcher.py
compare_locales/tests/paths/test_paths.py
compare_locales/tests/paths/test_project.py
compare_locales/tests/test_paths.py
rename from compare_locales/tests/test_paths.py
rename to compare_locales/tests/paths/__init__.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/__init__.py
@@ -1,627 +1,53 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
-import unittest
 
 from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
+    ProjectConfig, File, ProjectFiles, TOMLParser
 )
 from compare_locales import mozpath
 import pytoml as toml
 
 
-class TestMatcher(unittest.TestCase):
-
-    def test_matcher(self):
-        one = Matcher('foo/*')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertFalse(one.match('foo/baz/qux'))
-        other = Matcher('bar/*')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertFalse(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertIsNone(one.sub(other, 'bar/baz'))
-        one = Matcher('foo/**')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertTrue(one.match('foo/baz/qux'))
-        other = Matcher('bar/**')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertTrue(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertEqual(one.sub(other, 'foo/baz/qux'), 'bar/baz/qux')
-        one = Matcher('foo/*/one/**')
-        self.assertTrue(one.match('foo/baz/one/qux'))
-        self.assertFalse(one.match('foo/baz/bez/one/qux'))
-        other = Matcher('bar/*/other/**')
-        self.assertTrue(other.match('bar/baz/other/qux'))
-        self.assertFalse(other.match('bar/baz/bez/other/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux'),
-                         'bar/baz/other/qux')
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux/zzz'),
-                         'bar/baz/other/qux/zzz')
-        self.assertIsNone(one.sub(other, 'foo/baz/bez/one/qux'))
-        one = Matcher('foo/**/bar/**')
-        self.assertTrue(one.match('foo/bar/baz.qux'))
-        self.assertTrue(one.match('foo/tender/bar/baz.qux'))
-        self.assertFalse(one.match('foo/nobar/baz.qux'))
-        self.assertFalse(one.match('foo/tender/bar'))
-
-    def test_prefix(self):
-        self.assertEqual(
-            Matcher('foo/bar.file').prefix, 'foo/bar.file'
-        )
-        self.assertEqual(
-            Matcher('foo/*').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**').prefix, 'foo'
-        )
-        self.assertEqual(
-            Matcher('foo/*/bar').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**/bar').prefix, 'foo'
-        )
-
-    def test_variables(self):
-        self.assertDictEqual(
-            Matcher('foo/bar.file').match('foo/bar.file').groupdict(),
-            {}
-        )
-        self.assertDictEqual(
-            Matcher('{path}/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-        self.assertDictEqual(
-            Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-
-
 class SetupMixin(object):
     def setUp(self):
         self.cfg = ProjectConfig()
         self.file = File(
             '/tmp/somedir/de/browser/one/two/file.ftl',
             'file.ftl',
             module='browser', locale='de')
         self.other_file = File(
             '/tmp/somedir/de/toolkit/two/one/file.ftl',
             'file.ftl',
             module='toolkit', locale='de')
 
 
-class TestConfigLegacy(SetupMixin, unittest.TestCase):
-
-    def test_filter_py_true(self):
-        'Test filter.py just return bool(True)'
-        def filter(mod, path, entity=None):
-            return True
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_false(self):
-        'Test filter.py just return bool(False)'
-        def filter(mod, path, entity=None):
-            return False
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_error(self):
-        'Test filter.py just return str("error")'
-        def filter(mod, path, entity=None):
-            return 'error'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_ignore(self):
-        'Test filter.py just return str("ignore")'
-        def filter(mod, path, entity=None):
-            return 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_report(self):
-        'Test filter.py just return str("report") and match to "warning"'
-        def filter(mod, path, entity=None):
-            return 'report'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'warning')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'warning')
-
-    def test_filter_py_module(self):
-        'Test filter.py to return str("error") for browser or "ignore"'
-        def filter(mod, path, entity=None):
-            return 'error' if mod == 'browser' else 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-
-class TestConfigRules(SetupMixin, unittest.TestCase):
-
-    def test_filter_empty(self):
-        'Test that an empty config works'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_file_rule(self):
-        'Test a single rule for just a single file, no key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_key_rule(self):
-        'Test a single rule with file and key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 'one_entity',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_non_matching_key_rule(self):
-        'Test a single key rule with regex special chars that should not match'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': '.ne_entit.',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_single_matching_re_key_rule(self):
-        'Test a single key with regular expression'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 're:.ne_entit.$',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_rule(self):
-        'Test path shortcut, one for each of our files'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_key_rule(self):
-        'Test path and key shortcut, one key matching, one not'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'key': [
-                'one_entity',
-                'other_entity',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_wildcard_rule(self):
-        'Test single wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/*/*',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_wildcard_rule(self):
-        'Test double wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/**',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-
 class MockProjectFiles(ProjectFiles):
     def __init__(self, mocks, locale, projects, mergebase=None):
         (super(MockProjectFiles, self)
             .__init__(locale, projects, mergebase=mergebase))
         self.mocks = mocks
 
     def _files(self, matcher):
         base = matcher.prefix
         for path in self.mocks.get(base, []):
             p = mozpath.join(base, path)
             if matcher.match(p):
                 yield p
 
 
-class TestProjectPaths(unittest.TestCase):
-    def test_l10n_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*'
-        })
-        mocks = {
-            '/tmp/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files), [('/tmp/de/good.ftl', None, None, set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/fr/something.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [('/tmp/de/good.ftl', None, 'merging/de/good.ftl', set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, 'merging/de/something.ftl', set()))
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_reference_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertIsNone(files.match('/tmp/l10n/de/subdir/bad.ftl'))
-        self.assertIsNone(files.match('/tmp/reference/subdir/bad.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 'merging/de/good.ftl', set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 'merging/de/ref.ftl', set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_partial_l10n(self):
-        cfg = ProjectConfig()
-        cfg.locales.extend(['de', 'fr'])
-        cfg.add_paths({
-            'l10n': '/tmp/{locale}/major/*'
-        }, {
-            'l10n': '/tmp/{locale}/minor/*',
-            'locales': ['de']
-        })
-        mocks = {
-            '/tmp/de/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/de/minor/': [
-                'good.ftl',
-            ],
-            '/tmp/fr/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/minor/': [
-                'good.ftl',
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/de/major/good.ftl', None, None, set()),
-                ('/tmp/de/minor/good.ftl', None, None, set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/de/major/some.ftl'),
-            ('/tmp/de/major/some.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/de/other/some.ftl'))
-        # 'fr' is not in the locale list of minor, should only return major
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/fr/major/good.ftl', None, None, set()),
-            ])
-        self.assertIsNone(files.match('/tmp/fr/minor/some.ftl'))
-
-    def test_validation_mode(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        # `None` switches on validation mode
-        files = MockProjectFiles(mocks, None, [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-
-
 class MockTOMLParser(TOMLParser):
     def __init__(self, path_data, env=None, ignore_missing_includes=False):
         # mock, use the path as data. Yeah, not nice
         super(MockTOMLParser, self).__init__(
             '/tmp/base.toml',
             env=env, ignore_missing_includes=ignore_missing_includes
         )
         self.data = toml.loads(path_data)
 
     def load(self):
         # we mocked this
         pass
-
-
-class TestL10nMerge(unittest.TestCase):
-    # need to go through TOMLParser, as that's handling most of the
-    # environment
-    def test_merge_paths(self):
-        cfg = MockTOMLParser.parse(
-            '''\
-basepath = "."
-locales = [
-    "de",
-]
-[env]
-    l = "{l10n_base}/{locale}/"
-[[paths]]
-    reference = "reference/*"
-    l10n = "{l}*"
-''',
-            env={'l10n_base': '/tmp/l10n'}
-        )
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        cfg.add_global_environment(l10n_base='/tmp/l10n')
-        files = MockProjectFiles(mocks, 'de', [cfg], '/tmp/mergers')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 '/tmp/mergers/de/good.ftl',
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 '/tmp/mergers/de/ref.ftl',
-                 set()),
-            ])
-
-
-class TestProjectConfig(unittest.TestCase):
-    def test_expand_paths(self):
-        pc = ProjectConfig()
-        pc.add_environment(one="first_path")
-        self.assertEqual(pc.expand('foo'), 'foo')
-        self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
-        pc.add_environment(l10n_base='../tmp/localizations')
-        self.assertEqual(
-            pc.expand('{l}dir', {'l': '{l10n_base}/{locale}/'}),
-            '../tmp/localizations/{locale}/dir')
-        self.assertEqual(
-            pc.expand('{l}dir', {
-                'l': '{l10n_base}/{locale}/',
-                'l10n_base': '../merge-base'
-            }),
-            '../merge-base/{locale}/dir')
-
-    def test_children(self):
-        pc = ProjectConfig()
-        child = ProjectConfig()
-        pc.add_child(child)
-        self.assertListEqual([pc, child], list(pc.configs))
-
-
-class TestFile(unittest.TestCase):
-    def test_hash_and_equality(self):
-        f1 = File('/tmp/full/path/to/file', 'path/to/file')
-        d = {}
-        d[f1] = True
-        self.assertIn(f1, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file')
-        self.assertIn(f2, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file', locale='en')
-        self.assertNotIn(f2, d)
-        # trigger hash collisions between File and non-File objects
-        self.assertEqual(hash(f1), hash(f1.localpath))
-        self.assertNotIn(f1.localpath, d)
-        f1 = File('/tmp/full/other/path', 'other/path')
-        d[f1.localpath] = True
-        self.assertIn(f1.localpath, d)
-        self.assertNotIn(f1, d)
copy from compare_locales/tests/test_paths.py
copy to compare_locales/tests/paths/test_files.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/test_files.py
@@ -2,352 +2,22 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 import unittest
 
 from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
+    ProjectConfig
 )
-from compare_locales import mozpath
-import pytoml as toml
-
-
-class TestMatcher(unittest.TestCase):
-
-    def test_matcher(self):
-        one = Matcher('foo/*')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertFalse(one.match('foo/baz/qux'))
-        other = Matcher('bar/*')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertFalse(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertIsNone(one.sub(other, 'bar/baz'))
-        one = Matcher('foo/**')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertTrue(one.match('foo/baz/qux'))
-        other = Matcher('bar/**')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertTrue(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertEqual(one.sub(other, 'foo/baz/qux'), 'bar/baz/qux')
-        one = Matcher('foo/*/one/**')
-        self.assertTrue(one.match('foo/baz/one/qux'))
-        self.assertFalse(one.match('foo/baz/bez/one/qux'))
-        other = Matcher('bar/*/other/**')
-        self.assertTrue(other.match('bar/baz/other/qux'))
-        self.assertFalse(other.match('bar/baz/bez/other/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux'),
-                         'bar/baz/other/qux')
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux/zzz'),
-                         'bar/baz/other/qux/zzz')
-        self.assertIsNone(one.sub(other, 'foo/baz/bez/one/qux'))
-        one = Matcher('foo/**/bar/**')
-        self.assertTrue(one.match('foo/bar/baz.qux'))
-        self.assertTrue(one.match('foo/tender/bar/baz.qux'))
-        self.assertFalse(one.match('foo/nobar/baz.qux'))
-        self.assertFalse(one.match('foo/tender/bar'))
-
-    def test_prefix(self):
-        self.assertEqual(
-            Matcher('foo/bar.file').prefix, 'foo/bar.file'
-        )
-        self.assertEqual(
-            Matcher('foo/*').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**').prefix, 'foo'
-        )
-        self.assertEqual(
-            Matcher('foo/*/bar').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**/bar').prefix, 'foo'
-        )
-
-    def test_variables(self):
-        self.assertDictEqual(
-            Matcher('foo/bar.file').match('foo/bar.file').groupdict(),
-            {}
-        )
-        self.assertDictEqual(
-            Matcher('{path}/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-        self.assertDictEqual(
-            Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-
-
-class SetupMixin(object):
-    def setUp(self):
-        self.cfg = ProjectConfig()
-        self.file = File(
-            '/tmp/somedir/de/browser/one/two/file.ftl',
-            'file.ftl',
-            module='browser', locale='de')
-        self.other_file = File(
-            '/tmp/somedir/de/toolkit/two/one/file.ftl',
-            'file.ftl',
-            module='toolkit', locale='de')
-
-
-class TestConfigLegacy(SetupMixin, unittest.TestCase):
-
-    def test_filter_py_true(self):
-        'Test filter.py just return bool(True)'
-        def filter(mod, path, entity=None):
-            return True
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_false(self):
-        'Test filter.py just return bool(False)'
-        def filter(mod, path, entity=None):
-            return False
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_error(self):
-        'Test filter.py just return str("error")'
-        def filter(mod, path, entity=None):
-            return 'error'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_ignore(self):
-        'Test filter.py just return str("ignore")'
-        def filter(mod, path, entity=None):
-            return 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_report(self):
-        'Test filter.py just return str("report") and match to "warning"'
-        def filter(mod, path, entity=None):
-            return 'report'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'warning')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'warning')
-
-    def test_filter_py_module(self):
-        'Test filter.py to return str("error") for browser or "ignore"'
-        def filter(mod, path, entity=None):
-            return 'error' if mod == 'browser' else 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-
-class TestConfigRules(SetupMixin, unittest.TestCase):
-
-    def test_filter_empty(self):
-        'Test that an empty config works'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_file_rule(self):
-        'Test a single rule for just a single file, no key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_key_rule(self):
-        'Test a single rule with file and key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 'one_entity',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_non_matching_key_rule(self):
-        'Test a single key rule with regex special chars that should not match'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': '.ne_entit.',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_single_matching_re_key_rule(self):
-        'Test a single key with regular expression'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 're:.ne_entit.$',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_rule(self):
-        'Test path shortcut, one for each of our files'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_key_rule(self):
-        'Test path and key shortcut, one key matching, one not'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'key': [
-                'one_entity',
-                'other_entity',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_wildcard_rule(self):
-        'Test single wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/*/*',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_wildcard_rule(self):
-        'Test double wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/**',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-
-class MockProjectFiles(ProjectFiles):
-    def __init__(self, mocks, locale, projects, mergebase=None):
-        (super(MockProjectFiles, self)
-            .__init__(locale, projects, mergebase=mergebase))
-        self.mocks = mocks
-
-    def _files(self, matcher):
-        base = matcher.prefix
-        for path in self.mocks.get(base, []):
-            p = mozpath.join(base, path)
-            if matcher.match(p):
-                yield p
+from . import (
+    MockProjectFiles,
+    MockTOMLParser,
+)
 
 
 class TestProjectPaths(unittest.TestCase):
     def test_l10n_path(self):
         cfg = ProjectConfig()
         cfg.add_environment(l10n_base='/tmp')
         cfg.locales.append('de')
         cfg.add_paths({
@@ -519,30 +189,16 @@ class TestProjectPaths(unittest.TestCase
         self.assertListEqual(
             list(files),
             [
                 ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
                  set()),
             ])
 
 
-class MockTOMLParser(TOMLParser):
-    def __init__(self, path_data, env=None, ignore_missing_includes=False):
-        # mock, use the path as data. Yeah, not nice
-        super(MockTOMLParser, self).__init__(
-            '/tmp/base.toml',
-            env=env, ignore_missing_includes=ignore_missing_includes
-        )
-        self.data = toml.loads(path_data)
-
-    def load(self):
-        # we mocked this
-        pass
-
-
 class TestL10nMerge(unittest.TestCase):
     # need to go through TOMLParser, as that's handling most of the
     # environment
     def test_merge_paths(self):
         cfg = MockTOMLParser.parse(
             '''\
 basepath = "."
 locales = [
@@ -577,51 +233,8 @@ locales = [
             [
                 ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
                  '/tmp/mergers/de/good.ftl',
                  set()),
                 ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
                  '/tmp/mergers/de/ref.ftl',
                  set()),
             ])
-
-
-class TestProjectConfig(unittest.TestCase):
-    def test_expand_paths(self):
-        pc = ProjectConfig()
-        pc.add_environment(one="first_path")
-        self.assertEqual(pc.expand('foo'), 'foo')
-        self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
-        pc.add_environment(l10n_base='../tmp/localizations')
-        self.assertEqual(
-            pc.expand('{l}dir', {'l': '{l10n_base}/{locale}/'}),
-            '../tmp/localizations/{locale}/dir')
-        self.assertEqual(
-            pc.expand('{l}dir', {
-                'l': '{l10n_base}/{locale}/',
-                'l10n_base': '../merge-base'
-            }),
-            '../merge-base/{locale}/dir')
-
-    def test_children(self):
-        pc = ProjectConfig()
-        child = ProjectConfig()
-        pc.add_child(child)
-        self.assertListEqual([pc, child], list(pc.configs))
-
-
-class TestFile(unittest.TestCase):
-    def test_hash_and_equality(self):
-        f1 = File('/tmp/full/path/to/file', 'path/to/file')
-        d = {}
-        d[f1] = True
-        self.assertIn(f1, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file')
-        self.assertIn(f2, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file', locale='en')
-        self.assertNotIn(f2, d)
-        # trigger hash collisions between File and non-File objects
-        self.assertEqual(hash(f1), hash(f1.localpath))
-        self.assertNotIn(f1.localpath, d)
-        f1 = File('/tmp/full/other/path', 'other/path')
-        d[f1.localpath] = True
-        self.assertIn(f1.localpath, d)
-        self.assertNotIn(f1, d)
copy from compare_locales/tests/test_paths.py
copy to compare_locales/tests/paths/test_ini.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/test_ini.py
@@ -1,106 +1,19 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 import unittest
 
-from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
+from . import (
+    SetupMixin,
 )
-from compare_locales import mozpath
-import pytoml as toml
-
-
-class TestMatcher(unittest.TestCase):
-
-    def test_matcher(self):
-        one = Matcher('foo/*')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertFalse(one.match('foo/baz/qux'))
-        other = Matcher('bar/*')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertFalse(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertIsNone(one.sub(other, 'bar/baz'))
-        one = Matcher('foo/**')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertTrue(one.match('foo/baz/qux'))
-        other = Matcher('bar/**')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertTrue(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertEqual(one.sub(other, 'foo/baz/qux'), 'bar/baz/qux')
-        one = Matcher('foo/*/one/**')
-        self.assertTrue(one.match('foo/baz/one/qux'))
-        self.assertFalse(one.match('foo/baz/bez/one/qux'))
-        other = Matcher('bar/*/other/**')
-        self.assertTrue(other.match('bar/baz/other/qux'))
-        self.assertFalse(other.match('bar/baz/bez/other/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux'),
-                         'bar/baz/other/qux')
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux/zzz'),
-                         'bar/baz/other/qux/zzz')
-        self.assertIsNone(one.sub(other, 'foo/baz/bez/one/qux'))
-        one = Matcher('foo/**/bar/**')
-        self.assertTrue(one.match('foo/bar/baz.qux'))
-        self.assertTrue(one.match('foo/tender/bar/baz.qux'))
-        self.assertFalse(one.match('foo/nobar/baz.qux'))
-        self.assertFalse(one.match('foo/tender/bar'))
-
-    def test_prefix(self):
-        self.assertEqual(
-            Matcher('foo/bar.file').prefix, 'foo/bar.file'
-        )
-        self.assertEqual(
-            Matcher('foo/*').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**').prefix, 'foo'
-        )
-        self.assertEqual(
-            Matcher('foo/*/bar').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**/bar').prefix, 'foo'
-        )
-
-    def test_variables(self):
-        self.assertDictEqual(
-            Matcher('foo/bar.file').match('foo/bar.file').groupdict(),
-            {}
-        )
-        self.assertDictEqual(
-            Matcher('{path}/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-        self.assertDictEqual(
-            Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-
-
-class SetupMixin(object):
-    def setUp(self):
-        self.cfg = ProjectConfig()
-        self.file = File(
-            '/tmp/somedir/de/browser/one/two/file.ftl',
-            'file.ftl',
-            module='browser', locale='de')
-        self.other_file = File(
-            '/tmp/somedir/de/toolkit/two/one/file.ftl',
-            'file.ftl',
-            module='toolkit', locale='de')
 
 
 class TestConfigLegacy(SetupMixin, unittest.TestCase):
 
     def test_filter_py_true(self):
         'Test filter.py just return bool(True)'
         def filter(mod, path, entity=None):
             return True
@@ -170,458 +83,8 @@ class TestConfigLegacy(SetupMixin, unitt
         rv = self.cfg.filter(self.file)
         self.assertEqual(rv, 'error')
         rv = self.cfg.filter(self.file, entity='one_entity')
         self.assertEqual(rv, 'error')
         rv = self.cfg.filter(self.other_file)
         self.assertEqual(rv, 'ignore')
         rv = self.cfg.filter(self.other_file, entity='one_entity')
         self.assertEqual(rv, 'ignore')
-
-
-class TestConfigRules(SetupMixin, unittest.TestCase):
-
-    def test_filter_empty(self):
-        'Test that an empty config works'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_file_rule(self):
-        'Test a single rule for just a single file, no key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_key_rule(self):
-        'Test a single rule with file and key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 'one_entity',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_non_matching_key_rule(self):
-        'Test a single key rule with regex special chars that should not match'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': '.ne_entit.',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_single_matching_re_key_rule(self):
-        'Test a single key with regular expression'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 're:.ne_entit.$',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_rule(self):
-        'Test path shortcut, one for each of our files'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_key_rule(self):
-        'Test path and key shortcut, one key matching, one not'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'key': [
-                'one_entity',
-                'other_entity',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_wildcard_rule(self):
-        'Test single wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/*/*',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_wildcard_rule(self):
-        'Test double wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/**',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-
-class MockProjectFiles(ProjectFiles):
-    def __init__(self, mocks, locale, projects, mergebase=None):
-        (super(MockProjectFiles, self)
-            .__init__(locale, projects, mergebase=mergebase))
-        self.mocks = mocks
-
-    def _files(self, matcher):
-        base = matcher.prefix
-        for path in self.mocks.get(base, []):
-            p = mozpath.join(base, path)
-            if matcher.match(p):
-                yield p
-
-
-class TestProjectPaths(unittest.TestCase):
-    def test_l10n_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*'
-        })
-        mocks = {
-            '/tmp/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files), [('/tmp/de/good.ftl', None, None, set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/fr/something.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [('/tmp/de/good.ftl', None, 'merging/de/good.ftl', set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, 'merging/de/something.ftl', set()))
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_reference_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertIsNone(files.match('/tmp/l10n/de/subdir/bad.ftl'))
-        self.assertIsNone(files.match('/tmp/reference/subdir/bad.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 'merging/de/good.ftl', set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 'merging/de/ref.ftl', set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_partial_l10n(self):
-        cfg = ProjectConfig()
-        cfg.locales.extend(['de', 'fr'])
-        cfg.add_paths({
-            'l10n': '/tmp/{locale}/major/*'
-        }, {
-            'l10n': '/tmp/{locale}/minor/*',
-            'locales': ['de']
-        })
-        mocks = {
-            '/tmp/de/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/de/minor/': [
-                'good.ftl',
-            ],
-            '/tmp/fr/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/minor/': [
-                'good.ftl',
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/de/major/good.ftl', None, None, set()),
-                ('/tmp/de/minor/good.ftl', None, None, set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/de/major/some.ftl'),
-            ('/tmp/de/major/some.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/de/other/some.ftl'))
-        # 'fr' is not in the locale list of minor, should only return major
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/fr/major/good.ftl', None, None, set()),
-            ])
-        self.assertIsNone(files.match('/tmp/fr/minor/some.ftl'))
-
-    def test_validation_mode(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        # `None` switches on validation mode
-        files = MockProjectFiles(mocks, None, [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-
-
-class MockTOMLParser(TOMLParser):
-    def __init__(self, path_data, env=None, ignore_missing_includes=False):
-        # mock, use the path as data. Yeah, not nice
-        super(MockTOMLParser, self).__init__(
-            '/tmp/base.toml',
-            env=env, ignore_missing_includes=ignore_missing_includes
-        )
-        self.data = toml.loads(path_data)
-
-    def load(self):
-        # we mocked this
-        pass
-
-
-class TestL10nMerge(unittest.TestCase):
-    # need to go through TOMLParser, as that's handling most of the
-    # environment
-    def test_merge_paths(self):
-        cfg = MockTOMLParser.parse(
-            '''\
-basepath = "."
-locales = [
-    "de",
-]
-[env]
-    l = "{l10n_base}/{locale}/"
-[[paths]]
-    reference = "reference/*"
-    l10n = "{l}*"
-''',
-            env={'l10n_base': '/tmp/l10n'}
-        )
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        cfg.add_global_environment(l10n_base='/tmp/l10n')
-        files = MockProjectFiles(mocks, 'de', [cfg], '/tmp/mergers')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 '/tmp/mergers/de/good.ftl',
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 '/tmp/mergers/de/ref.ftl',
-                 set()),
-            ])
-
-
-class TestProjectConfig(unittest.TestCase):
-    def test_expand_paths(self):
-        pc = ProjectConfig()
-        pc.add_environment(one="first_path")
-        self.assertEqual(pc.expand('foo'), 'foo')
-        self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
-        pc.add_environment(l10n_base='../tmp/localizations')
-        self.assertEqual(
-            pc.expand('{l}dir', {'l': '{l10n_base}/{locale}/'}),
-            '../tmp/localizations/{locale}/dir')
-        self.assertEqual(
-            pc.expand('{l}dir', {
-                'l': '{l10n_base}/{locale}/',
-                'l10n_base': '../merge-base'
-            }),
-            '../merge-base/{locale}/dir')
-
-    def test_children(self):
-        pc = ProjectConfig()
-        child = ProjectConfig()
-        pc.add_child(child)
-        self.assertListEqual([pc, child], list(pc.configs))
-
-
-class TestFile(unittest.TestCase):
-    def test_hash_and_equality(self):
-        f1 = File('/tmp/full/path/to/file', 'path/to/file')
-        d = {}
-        d[f1] = True
-        self.assertIn(f1, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file')
-        self.assertIn(f2, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file', locale='en')
-        self.assertNotIn(f2, d)
-        # trigger hash collisions between File and non-File objects
-        self.assertEqual(hash(f1), hash(f1.localpath))
-        self.assertNotIn(f1.localpath, d)
-        f1 = File('/tmp/full/other/path', 'other/path')
-        d[f1.localpath] = True
-        self.assertIn(f1.localpath, d)
-        self.assertNotIn(f1, d)
copy from compare_locales/tests/test_paths.py
copy to compare_locales/tests/paths/test_matcher.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/test_matcher.py
@@ -1,21 +1,17 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 import unittest
 
-from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
-)
-from compare_locales import mozpath
-import pytoml as toml
+from compare_locales.paths import Matcher
 
 
 class TestMatcher(unittest.TestCase):
 
     def test_matcher(self):
         one = Matcher('foo/*')
         self.assertTrue(one.match('foo/baz'))
         self.assertFalse(one.match('foo/baz/qux'))
@@ -78,550 +74,8 @@ class TestMatcher(unittest.TestCase):
             }
         )
         self.assertDictEqual(
             Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
             {
                 'path': 'foo'
             }
         )
-
-
-class SetupMixin(object):
-    def setUp(self):
-        self.cfg = ProjectConfig()
-        self.file = File(
-            '/tmp/somedir/de/browser/one/two/file.ftl',
-            'file.ftl',
-            module='browser', locale='de')
-        self.other_file = File(
-            '/tmp/somedir/de/toolkit/two/one/file.ftl',
-            'file.ftl',
-            module='toolkit', locale='de')
-
-
-class TestConfigLegacy(SetupMixin, unittest.TestCase):
-
-    def test_filter_py_true(self):
-        'Test filter.py just return bool(True)'
-        def filter(mod, path, entity=None):
-            return True
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_false(self):
-        'Test filter.py just return bool(False)'
-        def filter(mod, path, entity=None):
-            return False
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_error(self):
-        'Test filter.py just return str("error")'
-        def filter(mod, path, entity=None):
-            return 'error'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_ignore(self):
-        'Test filter.py just return str("ignore")'
-        def filter(mod, path, entity=None):
-            return 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_report(self):
-        'Test filter.py just return str("report") and match to "warning"'
-        def filter(mod, path, entity=None):
-            return 'report'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'warning')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'warning')
-
-    def test_filter_py_module(self):
-        'Test filter.py to return str("error") for browser or "ignore"'
-        def filter(mod, path, entity=None):
-            return 'error' if mod == 'browser' else 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-
-class TestConfigRules(SetupMixin, unittest.TestCase):
-
-    def test_filter_empty(self):
-        'Test that an empty config works'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_file_rule(self):
-        'Test a single rule for just a single file, no key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_key_rule(self):
-        'Test a single rule with file and key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 'one_entity',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_non_matching_key_rule(self):
-        'Test a single key rule with regex special chars that should not match'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': '.ne_entit.',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_single_matching_re_key_rule(self):
-        'Test a single key with regular expression'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 're:.ne_entit.$',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_rule(self):
-        'Test path shortcut, one for each of our files'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_key_rule(self):
-        'Test path and key shortcut, one key matching, one not'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'key': [
-                'one_entity',
-                'other_entity',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_wildcard_rule(self):
-        'Test single wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/*/*',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_wildcard_rule(self):
-        'Test double wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/**',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-
-class MockProjectFiles(ProjectFiles):
-    def __init__(self, mocks, locale, projects, mergebase=None):
-        (super(MockProjectFiles, self)
-            .__init__(locale, projects, mergebase=mergebase))
-        self.mocks = mocks
-
-    def _files(self, matcher):
-        base = matcher.prefix
-        for path in self.mocks.get(base, []):
-            p = mozpath.join(base, path)
-            if matcher.match(p):
-                yield p
-
-
-class TestProjectPaths(unittest.TestCase):
-    def test_l10n_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*'
-        })
-        mocks = {
-            '/tmp/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files), [('/tmp/de/good.ftl', None, None, set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/fr/something.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [('/tmp/de/good.ftl', None, 'merging/de/good.ftl', set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, 'merging/de/something.ftl', set()))
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_reference_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertIsNone(files.match('/tmp/l10n/de/subdir/bad.ftl'))
-        self.assertIsNone(files.match('/tmp/reference/subdir/bad.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 'merging/de/good.ftl', set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 'merging/de/ref.ftl', set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_partial_l10n(self):
-        cfg = ProjectConfig()
-        cfg.locales.extend(['de', 'fr'])
-        cfg.add_paths({
-            'l10n': '/tmp/{locale}/major/*'
-        }, {
-            'l10n': '/tmp/{locale}/minor/*',
-            'locales': ['de']
-        })
-        mocks = {
-            '/tmp/de/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/de/minor/': [
-                'good.ftl',
-            ],
-            '/tmp/fr/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/minor/': [
-                'good.ftl',
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/de/major/good.ftl', None, None, set()),
-                ('/tmp/de/minor/good.ftl', None, None, set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/de/major/some.ftl'),
-            ('/tmp/de/major/some.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/de/other/some.ftl'))
-        # 'fr' is not in the locale list of minor, should only return major
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/fr/major/good.ftl', None, None, set()),
-            ])
-        self.assertIsNone(files.match('/tmp/fr/minor/some.ftl'))
-
-    def test_validation_mode(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        # `None` switches on validation mode
-        files = MockProjectFiles(mocks, None, [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-
-
-class MockTOMLParser(TOMLParser):
-    def __init__(self, path_data, env=None, ignore_missing_includes=False):
-        # mock, use the path as data. Yeah, not nice
-        super(MockTOMLParser, self).__init__(
-            '/tmp/base.toml',
-            env=env, ignore_missing_includes=ignore_missing_includes
-        )
-        self.data = toml.loads(path_data)
-
-    def load(self):
-        # we mocked this
-        pass
-
-
-class TestL10nMerge(unittest.TestCase):
-    # need to go through TOMLParser, as that's handling most of the
-    # environment
-    def test_merge_paths(self):
-        cfg = MockTOMLParser.parse(
-            '''\
-basepath = "."
-locales = [
-    "de",
-]
-[env]
-    l = "{l10n_base}/{locale}/"
-[[paths]]
-    reference = "reference/*"
-    l10n = "{l}*"
-''',
-            env={'l10n_base': '/tmp/l10n'}
-        )
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        cfg.add_global_environment(l10n_base='/tmp/l10n')
-        files = MockProjectFiles(mocks, 'de', [cfg], '/tmp/mergers')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 '/tmp/mergers/de/good.ftl',
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 '/tmp/mergers/de/ref.ftl',
-                 set()),
-            ])
-
-
-class TestProjectConfig(unittest.TestCase):
-    def test_expand_paths(self):
-        pc = ProjectConfig()
-        pc.add_environment(one="first_path")
-        self.assertEqual(pc.expand('foo'), 'foo')
-        self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
-        pc.add_environment(l10n_base='../tmp/localizations')
-        self.assertEqual(
-            pc.expand('{l}dir', {'l': '{l10n_base}/{locale}/'}),
-            '../tmp/localizations/{locale}/dir')
-        self.assertEqual(
-            pc.expand('{l}dir', {
-                'l': '{l10n_base}/{locale}/',
-                'l10n_base': '../merge-base'
-            }),
-            '../merge-base/{locale}/dir')
-
-    def test_children(self):
-        pc = ProjectConfig()
-        child = ProjectConfig()
-        pc.add_child(child)
-        self.assertListEqual([pc, child], list(pc.configs))
-
-
-class TestFile(unittest.TestCase):
-    def test_hash_and_equality(self):
-        f1 = File('/tmp/full/path/to/file', 'path/to/file')
-        d = {}
-        d[f1] = True
-        self.assertIn(f1, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file')
-        self.assertIn(f2, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file', locale='en')
-        self.assertNotIn(f2, d)
-        # trigger hash collisions between File and non-File objects
-        self.assertEqual(hash(f1), hash(f1.localpath))
-        self.assertNotIn(f1.localpath, d)
-        f1 = File('/tmp/full/other/path', 'other/path')
-        d[f1.localpath] = True
-        self.assertIn(f1.localpath, d)
-        self.assertNotIn(f1, d)
copy from compare_locales/tests/test_paths.py
copy to compare_locales/tests/paths/test_paths.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/test_paths.py
@@ -1,616 +1,17 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 import unittest
 
-from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
-)
-from compare_locales import mozpath
-import pytoml as toml
-
-
-class TestMatcher(unittest.TestCase):
-
-    def test_matcher(self):
-        one = Matcher('foo/*')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertFalse(one.match('foo/baz/qux'))
-        other = Matcher('bar/*')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertFalse(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertIsNone(one.sub(other, 'bar/baz'))
-        one = Matcher('foo/**')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertTrue(one.match('foo/baz/qux'))
-        other = Matcher('bar/**')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertTrue(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertEqual(one.sub(other, 'foo/baz/qux'), 'bar/baz/qux')
-        one = Matcher('foo/*/one/**')
-        self.assertTrue(one.match('foo/baz/one/qux'))
-        self.assertFalse(one.match('foo/baz/bez/one/qux'))
-        other = Matcher('bar/*/other/**')
-        self.assertTrue(other.match('bar/baz/other/qux'))
-        self.assertFalse(other.match('bar/baz/bez/other/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux'),
-                         'bar/baz/other/qux')
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux/zzz'),
-                         'bar/baz/other/qux/zzz')
-        self.assertIsNone(one.sub(other, 'foo/baz/bez/one/qux'))
-        one = Matcher('foo/**/bar/**')
-        self.assertTrue(one.match('foo/bar/baz.qux'))
-        self.assertTrue(one.match('foo/tender/bar/baz.qux'))
-        self.assertFalse(one.match('foo/nobar/baz.qux'))
-        self.assertFalse(one.match('foo/tender/bar'))
-
-    def test_prefix(self):
-        self.assertEqual(
-            Matcher('foo/bar.file').prefix, 'foo/bar.file'
-        )
-        self.assertEqual(
-            Matcher('foo/*').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**').prefix, 'foo'
-        )
-        self.assertEqual(
-            Matcher('foo/*/bar').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**/bar').prefix, 'foo'
-        )
-
-    def test_variables(self):
-        self.assertDictEqual(
-            Matcher('foo/bar.file').match('foo/bar.file').groupdict(),
-            {}
-        )
-        self.assertDictEqual(
-            Matcher('{path}/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-        self.assertDictEqual(
-            Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-
-
-class SetupMixin(object):
-    def setUp(self):
-        self.cfg = ProjectConfig()
-        self.file = File(
-            '/tmp/somedir/de/browser/one/two/file.ftl',
-            'file.ftl',
-            module='browser', locale='de')
-        self.other_file = File(
-            '/tmp/somedir/de/toolkit/two/one/file.ftl',
-            'file.ftl',
-            module='toolkit', locale='de')
-
-
-class TestConfigLegacy(SetupMixin, unittest.TestCase):
-
-    def test_filter_py_true(self):
-        'Test filter.py just return bool(True)'
-        def filter(mod, path, entity=None):
-            return True
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_false(self):
-        'Test filter.py just return bool(False)'
-        def filter(mod, path, entity=None):
-            return False
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_error(self):
-        'Test filter.py just return str("error")'
-        def filter(mod, path, entity=None):
-            return 'error'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_ignore(self):
-        'Test filter.py just return str("ignore")'
-        def filter(mod, path, entity=None):
-            return 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_report(self):
-        'Test filter.py just return str("report") and match to "warning"'
-        def filter(mod, path, entity=None):
-            return 'report'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'warning')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'warning')
-
-    def test_filter_py_module(self):
-        'Test filter.py to return str("error") for browser or "ignore"'
-        def filter(mod, path, entity=None):
-            return 'error' if mod == 'browser' else 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-
-class TestConfigRules(SetupMixin, unittest.TestCase):
-
-    def test_filter_empty(self):
-        'Test that an empty config works'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_file_rule(self):
-        'Test a single rule for just a single file, no key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_key_rule(self):
-        'Test a single rule with file and key'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 'one_entity',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_non_matching_key_rule(self):
-        'Test a single key rule with regex special chars that should not match'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': '.ne_entit.',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_single_matching_re_key_rule(self):
-        'Test a single key with regular expression'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-            'key': 're:.ne_entit.$',
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_rule(self):
-        'Test path shortcut, one for each of our files'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_file_key_rule(self):
-        'Test path and key shortcut, one key matching, one not'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/two/file.ftl',
-                '/tmp/somedir/{locale}/toolkit/two/one/file.ftl',
-            ],
-            'key': [
-                'one_entity',
-                'other_entity',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file, 'one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_single_wildcard_rule(self):
-        'Test single wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/browser/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/browser/one/*/*',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-    def test_double_wildcard_rule(self):
-        'Test double wildcard'
-        self.cfg.add_paths({
-            'l10n': '/tmp/somedir/{locale}/**'
-        })
-        self.cfg.add_rules({
-            'path': [
-                '/tmp/somedir/{locale}/**',
-            ],
-            'action': 'ignore'
-        })
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-
-
-class MockProjectFiles(ProjectFiles):
-    def __init__(self, mocks, locale, projects, mergebase=None):
-        (super(MockProjectFiles, self)
-            .__init__(locale, projects, mergebase=mergebase))
-        self.mocks = mocks
-
-    def _files(self, matcher):
-        base = matcher.prefix
-        for path in self.mocks.get(base, []):
-            p = mozpath.join(base, path)
-            if matcher.match(p):
-                yield p
-
-
-class TestProjectPaths(unittest.TestCase):
-    def test_l10n_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*'
-        })
-        mocks = {
-            '/tmp/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files), [('/tmp/de/good.ftl', None, None, set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/fr/something.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [('/tmp/de/good.ftl', None, 'merging/de/good.ftl', set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, 'merging/de/something.ftl', set()))
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_reference_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertIsNone(files.match('/tmp/l10n/de/subdir/bad.ftl'))
-        self.assertIsNone(files.match('/tmp/reference/subdir/bad.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 'merging/de/good.ftl', set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 'merging/de/ref.ftl', set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_partial_l10n(self):
-        cfg = ProjectConfig()
-        cfg.locales.extend(['de', 'fr'])
-        cfg.add_paths({
-            'l10n': '/tmp/{locale}/major/*'
-        }, {
-            'l10n': '/tmp/{locale}/minor/*',
-            'locales': ['de']
-        })
-        mocks = {
-            '/tmp/de/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/de/minor/': [
-                'good.ftl',
-            ],
-            '/tmp/fr/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/minor/': [
-                'good.ftl',
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/de/major/good.ftl', None, None, set()),
-                ('/tmp/de/minor/good.ftl', None, None, set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/de/major/some.ftl'),
-            ('/tmp/de/major/some.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/de/other/some.ftl'))
-        # 'fr' is not in the locale list of minor, should only return major
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/fr/major/good.ftl', None, None, set()),
-            ])
-        self.assertIsNone(files.match('/tmp/fr/minor/some.ftl'))
-
-    def test_validation_mode(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        # `None` switches on validation mode
-        files = MockProjectFiles(mocks, None, [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-
-
-class MockTOMLParser(TOMLParser):
-    def __init__(self, path_data, env=None, ignore_missing_includes=False):
-        # mock, use the path as data. Yeah, not nice
-        super(MockTOMLParser, self).__init__(
-            '/tmp/base.toml',
-            env=env, ignore_missing_includes=ignore_missing_includes
-        )
-        self.data = toml.loads(path_data)
-
-    def load(self):
-        # we mocked this
-        pass
-
-
-class TestL10nMerge(unittest.TestCase):
-    # need to go through TOMLParser, as that's handling most of the
-    # environment
-    def test_merge_paths(self):
-        cfg = MockTOMLParser.parse(
-            '''\
-basepath = "."
-locales = [
-    "de",
-]
-[env]
-    l = "{l10n_base}/{locale}/"
-[[paths]]
-    reference = "reference/*"
-    l10n = "{l}*"
-''',
-            env={'l10n_base': '/tmp/l10n'}
-        )
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        cfg.add_global_environment(l10n_base='/tmp/l10n')
-        files = MockProjectFiles(mocks, 'de', [cfg], '/tmp/mergers')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 '/tmp/mergers/de/good.ftl',
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 '/tmp/mergers/de/ref.ftl',
-                 set()),
-            ])
-
-
-class TestProjectConfig(unittest.TestCase):
-    def test_expand_paths(self):
-        pc = ProjectConfig()
-        pc.add_environment(one="first_path")
-        self.assertEqual(pc.expand('foo'), 'foo')
-        self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
-        pc.add_environment(l10n_base='../tmp/localizations')
-        self.assertEqual(
-            pc.expand('{l}dir', {'l': '{l10n_base}/{locale}/'}),
-            '../tmp/localizations/{locale}/dir')
-        self.assertEqual(
-            pc.expand('{l}dir', {
-                'l': '{l10n_base}/{locale}/',
-                'l10n_base': '../merge-base'
-            }),
-            '../merge-base/{locale}/dir')
-
-    def test_children(self):
-        pc = ProjectConfig()
-        child = ProjectConfig()
-        pc.add_child(child)
-        self.assertListEqual([pc, child], list(pc.configs))
+from compare_locales.paths import File
 
 
 class TestFile(unittest.TestCase):
     def test_hash_and_equality(self):
         f1 = File('/tmp/full/path/to/file', 'path/to/file')
         d = {}
         d[f1] = True
         self.assertIn(f1, d)
copy from compare_locales/tests/test_paths.py
copy to compare_locales/tests/paths/test_project.py
--- a/compare_locales/tests/test_paths.py
+++ b/compare_locales/tests/paths/test_project.py
@@ -1,185 +1,18 @@
 # -*- coding: utf-8 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 import unittest
 
-from compare_locales.paths import (
-    ProjectConfig, File, ProjectFiles, Matcher, TOMLParser
-)
-from compare_locales import mozpath
-import pytoml as toml
-
-
-class TestMatcher(unittest.TestCase):
-
-    def test_matcher(self):
-        one = Matcher('foo/*')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertFalse(one.match('foo/baz/qux'))
-        other = Matcher('bar/*')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertFalse(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertIsNone(one.sub(other, 'bar/baz'))
-        one = Matcher('foo/**')
-        self.assertTrue(one.match('foo/baz'))
-        self.assertTrue(one.match('foo/baz/qux'))
-        other = Matcher('bar/**')
-        self.assertTrue(other.match('bar/baz'))
-        self.assertTrue(other.match('bar/baz/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz'), 'bar/baz')
-        self.assertEqual(one.sub(other, 'foo/baz/qux'), 'bar/baz/qux')
-        one = Matcher('foo/*/one/**')
-        self.assertTrue(one.match('foo/baz/one/qux'))
-        self.assertFalse(one.match('foo/baz/bez/one/qux'))
-        other = Matcher('bar/*/other/**')
-        self.assertTrue(other.match('bar/baz/other/qux'))
-        self.assertFalse(other.match('bar/baz/bez/other/qux'))
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux'),
-                         'bar/baz/other/qux')
-        self.assertEqual(one.sub(other, 'foo/baz/one/qux/zzz'),
-                         'bar/baz/other/qux/zzz')
-        self.assertIsNone(one.sub(other, 'foo/baz/bez/one/qux'))
-        one = Matcher('foo/**/bar/**')
-        self.assertTrue(one.match('foo/bar/baz.qux'))
-        self.assertTrue(one.match('foo/tender/bar/baz.qux'))
-        self.assertFalse(one.match('foo/nobar/baz.qux'))
-        self.assertFalse(one.match('foo/tender/bar'))
-
-    def test_prefix(self):
-        self.assertEqual(
-            Matcher('foo/bar.file').prefix, 'foo/bar.file'
-        )
-        self.assertEqual(
-            Matcher('foo/*').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**').prefix, 'foo'
-        )
-        self.assertEqual(
-            Matcher('foo/*/bar').prefix, 'foo/'
-        )
-        self.assertEqual(
-            Matcher('foo/**/bar').prefix, 'foo'
-        )
-
-    def test_variables(self):
-        self.assertDictEqual(
-            Matcher('foo/bar.file').match('foo/bar.file').groupdict(),
-            {}
-        )
-        self.assertDictEqual(
-            Matcher('{path}/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-        self.assertDictEqual(
-            Matcher('{ path }/bar.file').match('foo/bar.file').groupdict(),
-            {
-                'path': 'foo'
-            }
-        )
-
-
-class SetupMixin(object):
-    def setUp(self):
-        self.cfg = ProjectConfig()
-        self.file = File(
-            '/tmp/somedir/de/browser/one/two/file.ftl',
-            'file.ftl',
-            module='browser', locale='de')
-        self.other_file = File(
-            '/tmp/somedir/de/toolkit/two/one/file.ftl',
-            'file.ftl',
-            module='toolkit', locale='de')
-
-
-class TestConfigLegacy(SetupMixin, unittest.TestCase):
-
-    def test_filter_py_true(self):
-        'Test filter.py just return bool(True)'
-        def filter(mod, path, entity=None):
-            return True
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_false(self):
-        'Test filter.py just return bool(False)'
-        def filter(mod, path, entity=None):
-            return False
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_error(self):
-        'Test filter.py just return str("error")'
-        def filter(mod, path, entity=None):
-            return 'error'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-
-    def test_filter_py_ignore(self):
-        'Test filter.py just return str("ignore")'
-        def filter(mod, path, entity=None):
-            return 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
-
-    def test_filter_py_report(self):
-        'Test filter.py just return str("report") and match to "warning"'
-        def filter(mod, path, entity=None):
-            return 'report'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'warning')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'warning')
-
-    def test_filter_py_module(self):
-        'Test filter.py to return str("error") for browser or "ignore"'
-        def filter(mod, path, entity=None):
-            return 'error' if mod == 'browser' else 'ignore'
-        self.cfg.set_filter_py(filter)
-        with self.assertRaises(AssertionError):
-            self.cfg.add_rules({})
-        rv = self.cfg.filter(self.file)
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.file, entity='one_entity')
-        self.assertEqual(rv, 'error')
-        rv = self.cfg.filter(self.other_file)
-        self.assertEqual(rv, 'ignore')
-        rv = self.cfg.filter(self.other_file, entity='one_entity')
-        self.assertEqual(rv, 'ignore')
+from compare_locales.paths import ProjectConfig
+from . import SetupMixin
 
 
 class TestConfigRules(SetupMixin, unittest.TestCase):
 
     def test_filter_empty(self):
         'Test that an empty config works'
         self.cfg.add_paths({
             'l10n': '/tmp/somedir/{locale}/browser/**'
@@ -326,269 +159,16 @@ class TestConfigRules(SetupMixin, unitte
             'action': 'ignore'
         })
         rv = self.cfg.filter(self.file)
         self.assertEqual(rv, 'ignore')
         rv = self.cfg.filter(self.other_file)
         self.assertEqual(rv, 'ignore')
 
 
-class MockProjectFiles(ProjectFiles):
-    def __init__(self, mocks, locale, projects, mergebase=None):
-        (super(MockProjectFiles, self)
-            .__init__(locale, projects, mergebase=mergebase))
-        self.mocks = mocks
-
-    def _files(self, matcher):
-        base = matcher.prefix
-        for path in self.mocks.get(base, []):
-            p = mozpath.join(base, path)
-            if matcher.match(p):
-                yield p
-
-
-class TestProjectPaths(unittest.TestCase):
-    def test_l10n_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*'
-        })
-        mocks = {
-            '/tmp/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files), [('/tmp/de/good.ftl', None, None, set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/fr/something.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [('/tmp/de/good.ftl', None, 'merging/de/good.ftl', set())])
-        self.assertTupleEqual(
-            files.match('/tmp/de/something.ftl'),
-            ('/tmp/de/something.ftl', None, 'merging/de/something.ftl', set()))
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_reference_path(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl', None,
-             set()),
-            )
-        self.assertIsNone(files.match('/tmp/l10n/de/subdir/bad.ftl'))
-        self.assertIsNone(files.match('/tmp/reference/subdir/bad.ftl'))
-        files = MockProjectFiles(mocks, 'de', [cfg], mergebase='merging')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 'merging/de/good.ftl', set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 'merging/de/ref.ftl', set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/l10n/de/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        self.assertTupleEqual(
-            files.match('/tmp/reference/good.ftl'),
-            ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-             'merging/de/good.ftl', set()),
-            )
-        # 'fr' is not in the locale list, should return no files
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(list(files), [])
-
-    def test_partial_l10n(self):
-        cfg = ProjectConfig()
-        cfg.locales.extend(['de', 'fr'])
-        cfg.add_paths({
-            'l10n': '/tmp/{locale}/major/*'
-        }, {
-            'l10n': '/tmp/{locale}/minor/*',
-            'locales': ['de']
-        })
-        mocks = {
-            '/tmp/de/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/de/minor/': [
-                'good.ftl',
-            ],
-            '/tmp/fr/major/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/fr/minor/': [
-                'good.ftl',
-            ],
-        }
-        files = MockProjectFiles(mocks, 'de', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/de/major/good.ftl', None, None, set()),
-                ('/tmp/de/minor/good.ftl', None, None, set()),
-            ])
-        self.assertTupleEqual(
-            files.match('/tmp/de/major/some.ftl'),
-            ('/tmp/de/major/some.ftl', None, None, set()))
-        self.assertIsNone(files.match('/tmp/de/other/some.ftl'))
-        # 'fr' is not in the locale list of minor, should only return major
-        files = MockProjectFiles(mocks, 'fr', [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/fr/major/good.ftl', None, None, set()),
-            ])
-        self.assertIsNone(files.match('/tmp/fr/minor/some.ftl'))
-
-    def test_validation_mode(self):
-        cfg = ProjectConfig()
-        cfg.add_environment(l10n_base='/tmp/l10n')
-        cfg.locales.append('de')
-        cfg.add_paths({
-            'l10n': '{l10n_base}/{locale}/*',
-            'reference': '/tmp/reference/*'
-        })
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        # `None` switches on validation mode
-        files = MockProjectFiles(mocks, None, [cfg])
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/reference/ref.ftl', '/tmp/reference/ref.ftl', None,
-                 set()),
-            ])
-
-
-class MockTOMLParser(TOMLParser):
-    def __init__(self, path_data, env=None, ignore_missing_includes=False):
-        # mock, use the path as data. Yeah, not nice
-        super(MockTOMLParser, self).__init__(
-            '/tmp/base.toml',
-            env=env, ignore_missing_includes=ignore_missing_includes
-        )
-        self.data = toml.loads(path_data)
-
-    def load(self):
-        # we mocked this
-        pass
-
-
-class TestL10nMerge(unittest.TestCase):
-    # need to go through TOMLParser, as that's handling most of the
-    # environment
-    def test_merge_paths(self):
-        cfg = MockTOMLParser.parse(
-            '''\
-basepath = "."
-locales = [
-    "de",
-]
-[env]
-    l = "{l10n_base}/{locale}/"
-[[paths]]
-    reference = "reference/*"
-    l10n = "{l}*"
-''',
-            env={'l10n_base': '/tmp/l10n'}
-        )
-        mocks = {
-            '/tmp/l10n/de/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/l10n/fr/': [
-                'good.ftl',
-                'not/subdir/bad.ftl'
-            ],
-            '/tmp/reference/': [
-                'ref.ftl',
-                'not/subdir/bad.ftl'
-            ],
-        }
-        cfg.add_global_environment(l10n_base='/tmp/l10n')
-        files = MockProjectFiles(mocks, 'de', [cfg], '/tmp/mergers')
-        self.assertListEqual(
-            list(files),
-            [
-                ('/tmp/l10n/de/good.ftl', '/tmp/reference/good.ftl',
-                 '/tmp/mergers/de/good.ftl',
-                 set()),
-                ('/tmp/l10n/de/ref.ftl', '/tmp/reference/ref.ftl',
-                 '/tmp/mergers/de/ref.ftl',
-                 set()),
-            ])
-
-
 class TestProjectConfig(unittest.TestCase):
     def test_expand_paths(self):
         pc = ProjectConfig()
         pc.add_environment(one="first_path")
         self.assertEqual(pc.expand('foo'), 'foo')
         self.assertEqual(pc.expand('foo{one}bar'), 'foofirst_pathbar')
         pc.add_environment(l10n_base='../tmp/localizations')
         self.assertEqual(
@@ -601,27 +181,8 @@ class TestProjectConfig(unittest.TestCas
             }),
             '../merge-base/{locale}/dir')
 
     def test_children(self):
         pc = ProjectConfig()
         child = ProjectConfig()
         pc.add_child(child)
         self.assertListEqual([pc, child], list(pc.configs))
-
-
-class TestFile(unittest.TestCase):
-    def test_hash_and_equality(self):
-        f1 = File('/tmp/full/path/to/file', 'path/to/file')
-        d = {}
-        d[f1] = True
-        self.assertIn(f1, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file')
-        self.assertIn(f2, d)
-        f2 = File('/tmp/full/path/to/file', 'path/to/file', locale='en')
-        self.assertNotIn(f2, d)
-        # trigger hash collisions between File and non-File objects
-        self.assertEqual(hash(f1), hash(f1.localpath))
-        self.assertNotIn(f1.localpath, d)
-        f1 = File('/tmp/full/other/path', 'other/path')
-        d[f1.localpath] = True
-        self.assertIn(f1.localpath, d)
-        self.assertNotIn(f1, d)