def _synchronize_docs(self): m = InstallManifest() m.add_symlink(self._conf_py_path, "conf.py") for dest, source in sorted(self._trees.items()): source_dir = os.path.join(self._topsrcdir, source) for root, dirs, files in os.walk(source_dir): for f in files: source_path = os.path.join(root, f) rel_source = source_path[len(source_dir) + 1 :] m.add_symlink(source_path, os.path.join(dest, rel_source)) copier = FileCopier() m.populate_registry(copier) copier.copy(self._docs_dir) with open(self._index_path, "rb") as fh: data = fh.read() indexes = ["%s/index" % p for p in sorted(self._trees.keys())] indexes = "\n ".join(indexes) packages = [os.path.basename(p) for p in self._python_package_dirs] packages = ["python/%s" % p for p in packages] packages = "\n ".join(sorted(packages)) data = data.format(indexes=indexes, python_packages=packages) with open(os.path.join(self._docs_dir, "index.rst"), "wb") as fh: fh.write(data)
def test_adds(self): m = InstallManifest() m.add_symlink('s_source', 's_dest') m.add_copy('c_source', 'c_dest') m.add_required_exists('e_dest') m.add_optional_exists('o_dest') m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest') self.assertEqual(len(m), 6) self.assertIn('s_dest', m) self.assertIn('c_dest', m) self.assertIn('e_dest', m) self.assertIn('o_dest', m) with self.assertRaises(ValueError): m.add_symlink('s_other', 's_dest') with self.assertRaises(ValueError): m.add_copy('c_other', 'c_dest') with self.assertRaises(ValueError): m.add_required_exists('e_dest') with self.assertRaises(ValueError): m.add_optional_exists('o_dest') with self.assertRaises(ValueError): m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') with self.assertRaises(ValueError): m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest')
def _synchronize_docs(self): m = InstallManifest() m.add_symlink(self._conf_py_path, 'conf.py') for dest, source in sorted(self._trees.items()): source_dir = os.path.join(self._topsrcdir, source) for root, dirs, files in os.walk(source_dir): for f in files: source_path = os.path.join(root, f) rel_source = source_path[len(source_dir) + 1:] m.add_symlink(source_path, os.path.join(dest, rel_source)) stage_dir = os.path.join(self._output_dir, 'staging') copier = FileCopier() m.populate_registry(copier) copier.copy(stage_dir) with open(self._index_path, 'rb') as fh: data = fh.read() indexes = ['%s/index' % p for p in sorted(self._trees.keys())] indexes = '\n '.join(indexes) packages = [os.path.basename(p) for p in self._python_package_dirs] packages = ['python/%s' % p for p in packages] packages = '\n '.join(sorted(packages)) data = data.format(indexes=indexes, python_packages=packages) with open(os.path.join(stage_dir, 'index.rst'), 'wb') as fh: fh.write(data)
def _synchronize_docs(self): m = InstallManifest() m.add_symlink(self._conf_py_path, 'conf.py') for dest, source in sorted(self._trees.items()): source_dir = os.path.join(self._topsrcdir, source) for root, dirs, files in os.walk(source_dir): for f in files: source_path = os.path.join(root, f) rel_source = source_path[len(source_dir) + 1:] m.add_symlink(source_path, os.path.join(dest, rel_source)) stage_dir = os.path.join(self._output_dir, 'staging') copier = FileCopier() m.populate_registry(copier) copier.copy(stage_dir) with open(self._index_path, 'rb') as fh: data = fh.read() indexes = ['%s/index' % p for p in sorted(self._trees.keys())] indexes = '\n '.join(indexes) packages = [os.path.basename(p) for p in self._python_package_dirs] packages = ['python/%s' % p for p in packages] packages = '\n '.join(sorted(packages)) data = data.format(indexes=indexes, python_packages=packages) with open(os.path.join(stage_dir, 'index.rst'), 'wb') as fh: fh.write(data)
def test_adds(self): m = InstallManifest() m.add_symlink('s_source', 's_dest') m.add_copy('c_source', 'c_dest') m.add_required_exists('e_dest') m.add_optional_exists('o_dest') m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest') self.assertEqual(len(m), 6) self.assertIn('s_dest', m) self.assertIn('c_dest', m) self.assertIn('e_dest', m) self.assertIn('o_dest', m) with self.assertRaises(ValueError): m.add_symlink('s_other', 's_dest') with self.assertRaises(ValueError): m.add_copy('c_other', 'c_dest') with self.assertRaises(ValueError): m.add_required_exists('e_dest') with self.assertRaises(ValueError): m.add_optional_exists('o_dest') with self.assertRaises(ValueError): m.add_pattern_symlink('ps_base', 'ps/*', 'ps_dest') with self.assertRaises(ValueError): m.add_pattern_copy('pc_base', 'pc/**', 'pc_dest')
def _get_test_manifest(self): m = InstallManifest() m.add_symlink(self.tmppath("s_source"), "s_dest") m.add_copy(self.tmppath("c_source"), "c_dest") m.add_required_exists("e_dest") return m
def _get_test_manifest(self): m = InstallManifest() m.add_symlink(self.tmppath('s_source'), 's_dest') m.add_copy(self.tmppath('c_source'), 'c_dest') m.add_required_exists('e_dest') m.add_optional_exists('o_dest') return m
def install_test_files(topsrcdir, topobjdir, tests_root, test_objs): """Installs the requested test files to the objdir. This is invoked by test runners to avoid installing tens of thousands of test files when only a few tests need to be run. """ flavor_info = { flavor: (root, prefix, install) for (flavor, root, prefix, install) in TEST_MANIFESTS.values() } objdir_dest = mozpath.join(topobjdir, tests_root) converter = SupportFilesConverter() install_info = TestInstallInfo() for o in test_objs: flavor = o['flavor'] if flavor not in flavor_info: # This is a test flavor that isn't installed by the build system. continue root, prefix, install = flavor_info[flavor] if not install: # This flavor isn't installed to the objdir. continue manifest_path = o['manifest'] manifest_dir = mozpath.dirname(manifest_path) out_dir = mozpath.join(root, prefix, manifest_dir[len(topsrcdir) + 1:]) file_relpath = o['file_relpath'] source = mozpath.join(topsrcdir, file_relpath) dest = mozpath.join(root, prefix, file_relpath) if 'install-to-subdir' in o: out_dir = mozpath.join(out_dir, o['install-to-subdir']) manifest_relpath = mozpath.relpath(source, mozpath.dirname(manifest_path)) dest = mozpath.join(out_dir, manifest_relpath) install_info.installs.append((source, dest)) install_info |= converter.convert_support_files( o, root, manifest_dir, out_dir) manifest = InstallManifest() for source, dest in set(install_info.installs): if dest in install_info.external_installs: continue manifest.add_symlink(source, dest) for base, pattern, dest in install_info.pattern_installs: manifest.add_pattern_symlink(base, pattern, dest) _resolve_installs(install_info.deferred_installs, topobjdir, manifest) # Harness files are treated as a monolith and installed each time we run tests. # Fortunately there are not very many. manifest |= InstallManifest( mozpath.join(topobjdir, '_build_manifests', 'install', tests_root)) copier = FileCopier() manifest.populate_registry(copier) copier.copy(objdir_dest, remove_unaccounted=False)
def install_test_files(topsrcdir, topobjdir, tests_root, test_objs): """Installs the requested test files to the objdir. This is invoked by test runners to avoid installing tens of thousands of test files when only a few tests need to be run. """ flavor_info = {flavor: (root, prefix, install) for (flavor, root, prefix, install) in TEST_MANIFESTS.values()} objdir_dest = mozpath.join(topobjdir, tests_root) converter = SupportFilesConverter() install_info = TestInstallInfo() for o in test_objs: flavor = o['flavor'] if flavor not in flavor_info: # This is a test flavor that isn't installed by the build system. continue root, prefix, install = flavor_info[flavor] if not install: # This flavor isn't installed to the objdir. continue manifest_path = o['manifest'] manifest_dir = mozpath.dirname(manifest_path) out_dir = mozpath.join(root, prefix, manifest_dir[len(topsrcdir) + 1:]) file_relpath = o['file_relpath'] source = mozpath.join(topsrcdir, file_relpath) dest = mozpath.join(root, prefix, file_relpath) if 'install-to-subdir' in o: out_dir = mozpath.join(out_dir, o['install-to-subdir']) manifest_relpath = mozpath.relpath(source, mozpath.dirname(manifest_path)) dest = mozpath.join(out_dir, manifest_relpath) install_info.installs.append((source, dest)) install_info |= converter.convert_support_files(o, root, manifest_dir, out_dir) manifest = InstallManifest() for source, dest in set(install_info.installs): if dest in install_info.external_installs: continue manifest.add_symlink(source, dest) for base, pattern, dest in install_info.pattern_installs: manifest.add_pattern_symlink(base, pattern, dest) _resolve_installs(install_info.deferred_installs, topobjdir, manifest) # Harness files are treated as a monolith and installed each time we run tests. # Fortunately there are not very many. manifest |= InstallManifest(mozpath.join(topobjdir, '_build_manifests', 'install', tests_root)) copier = FileCopier() manifest.populate_registry(copier) copier.copy(objdir_dest, remove_unaccounted=False)
def _get_test_manifest(self): m = InstallManifest() m.add_symlink(self.tmppath('s_source'), 's_dest') m.add_copy(self.tmppath('c_source'), 'c_dest') m.add_preprocess(self.tmppath('p_source'), 'p_dest', self.tmppath('p_source.pp'), '#', {'FOO':'BAR', 'BAZ':'QUX'}) m.add_required_exists('e_dest') m.add_optional_exists('o_dest') m.add_pattern_symlink('ps_base', '*', 'ps_dest') m.add_pattern_copy('pc_base', '**', 'pc_dest') return m
def test_or(self): m1 = self._get_test_manifest() m2 = InstallManifest() m2.add_symlink('s_source2', 's_dest2') m2.add_copy('c_source2', 'c_dest2') m1 |= m2 self.assertEqual(len(m2), 2) self.assertEqual(len(m1), 6) self.assertIn('s_dest2', m1) self.assertIn('c_dest2', m1)
def _get_test_manifest(self): m = InstallManifest() m.add_symlink(self.tmppath("s_source"), "s_dest") m.add_copy(self.tmppath("c_source"), "c_dest") m.add_preprocess( self.tmppath("p_source"), "p_dest", self.tmppath("p_source.pp"), "#", {"FOO": "BAR", "BAZ": "QUX"} ) m.add_required_exists("e_dest") m.add_optional_exists("o_dest") m.add_pattern_symlink("ps_base", "*", "ps_dest") m.add_pattern_copy("pc_base", "**", "pc_dest") return m
def test_or(self): m1 = self._get_test_manifest() m2 = InstallManifest() m2.add_symlink("s_source2", "s_dest2") m2.add_copy("c_source2", "c_dest2") m1 |= m2 self.assertEqual(len(m2), 2) self.assertEqual(len(m1), 5) self.assertIn("s_dest2", m1) self.assertIn("c_dest2", m1)
def _get_test_manifest(self): m = InstallManifest() m.add_symlink(self.tmppath('s_source'), 's_dest') m.add_copy(self.tmppath('c_source'), 'c_dest') m.add_preprocess(self.tmppath('p_source'), 'p_dest', self.tmppath('p_source.pp'), '#', { 'FOO': 'BAR', 'BAZ': 'QUX' }) m.add_required_exists('e_dest') m.add_optional_exists('o_dest') m.add_pattern_symlink('ps_base', '*', 'ps_dest') m.add_pattern_copy('pc_base', '**', 'pc_dest') return m
def test_install_manifests_written(self): env, objs = self._emit('stub0') backend = RecursiveMakeBackend(env) m = InstallManifest() backend._install_manifests['testing'] = m m.add_symlink(__file__, 'self') backend.consume(objs) man_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') self.assertTrue(os.path.isdir(man_dir)) expected = ['testing'] for e in expected: full = mozpath.join(man_dir, e) self.assertTrue(os.path.exists(full)) m2 = InstallManifest(path=full) self.assertEqual(m, m2)
def test_install_manifests_written(self): env, objs = self._emit('stub0') backend = RecursiveMakeBackend(env) m = InstallManifest() backend._install_manifests['testing'] = m m.add_symlink(__file__, 'self') backend.consume(objs) man_dir = mozpath.join(env.topobjdir, '_build_manifests', 'install') self.assertTrue(os.path.isdir(man_dir)) expected = ['testing'] for e in expected: full = mozpath.join(man_dir, e) self.assertTrue(os.path.exists(full)) m2 = InstallManifest(path=full) self.assertEqual(m, m2)
def test_install_manifests_written(self): env, objs = self._emit("stub0") backend = RecursiveMakeBackend(env) m = InstallManifest() backend._install_manifests["testing"] = m m.add_symlink(__file__, "self") backend.consume(objs) man_dir = os.path.join(env.topobjdir, "_build_manifests", "install") self.assertTrue(os.path.isdir(man_dir)) expected = ["testing"] for e in expected: full = os.path.join(man_dir, e) self.assertTrue(os.path.exists(full)) m2 = InstallManifest(path=full) self.assertEqual(m, m2)
def test_adds(self): m = InstallManifest() m.add_symlink("s_source", "s_dest") m.add_copy("c_source", "c_dest") m.add_required_exists("e_dest") self.assertEqual(len(m), 3) self.assertIn("s_dest", m) self.assertIn("c_dest", m) self.assertIn("e_dest", m) with self.assertRaises(ValueError): m.add_symlink("s_other", "s_dest") with self.assertRaises(ValueError): m.add_copy("c_other", "c_dest") with self.assertRaises(ValueError): m.add_required_exists("e_dest")
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml') if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), 'res') if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets') for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. For now, # we're adding class path entries with the full path to # required JAR files (which makes sense for JARs in the source # directory, but probably doesn't for JARs in the object # directory). This could be a problem because we only know # the contents of (a subdirectory of) libs/ after a successful # build and package, which is after build-backend time. So we # use a pattern symlink that is resolved at manifest install # time. if project.libs: manifest.add_pattern_copy(mozpath.join(srcdir, project.libs), '**', 'libs') return manifest
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml') if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), 'res') else: # Eclipse expects a res directory no matter what, so we # make an empty directory if the project doesn't specify. res = os.path.abspath(mozpath.join(os.path.dirname(__file__), 'templates', 'android_eclipse_empty_resource_directory')) manifest.add_pattern_copy(res, '.**', 'res') if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets') for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. For now, # we're adding class path entries with the full path to # required JAR files (which makes sense for JARs in the source # directory, but probably doesn't for JARs in the object # directory). This could be a problem because we only know # the contents of (a subdirectory of) libs/ after a successful # build and package, which is after build-backend time. So we # use a pattern symlink that is resolved at manifest install # time. if project.libs: manifest.add_pattern_copy(mozpath.join(srcdir, project.libs), '**', 'libs') return manifest
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml') if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), 'res') else: # Eclipse expects a res directory no matter what, so we # make an empty directory if the project doesn't specify. res = os.path.abspath(mozpath.join(os.path.dirname(__file__), 'templates', 'android_eclipse_empty_resource_directory')) manifest.add_pattern_copy(res, '.**', 'res') if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets') for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. For now, # we're adding class path entries with the full path to # required JAR files (which makes sense for JARs in the source # directory, but probably doesn't for JARs in the object # directory). This could be a problem because we only know # the contents of (a subdirectory of) libs/ after a successful # build and package, which is after build-backend time. So we # use a pattern symlink that is resolved at manifest install # time. if project.libs: manifest.add_pattern_copy(mozpath.join(srcdir, project.libs), '**', 'libs') return manifest
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml') if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), 'res') else: # Eclipse expects a res directory no matter what, so we # make an empty directory if the project doesn't specify. res = os.path.abspath(mozpath.join(os.path.dirname(__file__), 'templates', 'android_eclipse_empty_resource_directory')) manifest.add_pattern_copy(res, '.**', 'res') if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets') for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. For now, we're adding # class path entries with the full path to required JAR files (which # makes sense for JARs in the source directory, but probably doesn't for # JARs in the object directory). This could be a problem because we only # know the contents of (a subdirectory of) libs/ after a successful # build and package, which is after build-backend time. At the cost of # some flexibility, we explicitly copy certain libraries here; if the # libraries aren't present -- namely, when the tree hasn't been packaged # -- this fails. That's by design, to avoid crashes on device caused by # missing native libraries. for src, dst in project.libs: manifest.add_copy(mozpath.join(srcdir, src), dst) return manifest
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), "AndroidManifest.xml") if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), "res") if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), "assets") for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. This # wouldn't be a problem, except we only know the contents of # (a subdirectory of) libs/ after a successful build and # package, which is after build-backend time. So we use a # pattern symlink that is resolved at manifest install time. if project.libs: manifest.add_pattern_copy(mozpath.join(srcdir, project.libs), "**", "libs") for extra_jar in sorted(project.extra_jars): manifest.add_copy(mozpath.join(srcdir, extra_jar), mozpath.join("libs", os.path.basename(extra_jar))) return manifest
def _manifest_for_project(self, srcdir, project): manifest = InstallManifest() if project.manifest: manifest.add_copy(mozpath.join(srcdir, project.manifest), 'AndroidManifest.xml') if project.res: manifest.add_symlink(mozpath.join(srcdir, project.res), 'res') else: # Eclipse expects a res directory no matter what, so we # make an empty directory if the project doesn't specify. res = os.path.abspath(mozpath.join(os.path.dirname(__file__), 'templates', 'android_eclipse_empty_resource_directory')) manifest.add_pattern_copy(res, '.**', 'res') if project.assets: manifest.add_symlink(mozpath.join(srcdir, project.assets), 'assets') for cpe in project._classpathentries: manifest.add_symlink(mozpath.join(srcdir, cpe.srcdir), cpe.dstdir) # JARs and native libraries go in the same place. For now, we're adding # class path entries with the full path to required JAR files (which # makes sense for JARs in the source directory, but probably doesn't for # JARs in the object directory). This could be a problem because we only # know the contents of (a subdirectory of) libs/ after a successful # build and package, which is after build-backend time. At the cost of # some flexibility, we explicitly copy certain libraries here; if the # libraries aren't present -- namely, when the tree hasn't been packaged # -- this fails. That's by design, to avoid crashes on device caused by # missing native libraries. for src, dst in project.libs: manifest.add_copy(mozpath.join(srcdir, src), dst) return manifest
def test_test_manifest_deffered_installs_written(self): """Shared support files are written to their own data file by the backend.""" env = self._consume('test-manifest-shared-support', RecursiveMakeBackend) all_tests_path = mozpath.join(env.topobjdir, 'all-tests.pkl') self.assertTrue(os.path.exists(all_tests_path)) test_installs_path = mozpath.join(env.topobjdir, 'test-installs.pkl') with open(test_installs_path, 'r') as fh: test_installs = pickle.load(fh) self.assertEqual( set(test_installs.keys()), set([ 'child/test_sub.js', 'child/data/**', 'child/another-file.sjs' ])) for key in test_installs.keys(): self.assertIn(key, test_installs) test_files_manifest = mozpath.join(env.topobjdir, '_build_manifests', 'install', '_test_files') # First, read the generated for ini manifest contents. m = InstallManifest(path=test_files_manifest) # Then, synthesize one from the test-installs.pkl file. This should # allow us to re-create a subset of the above. synthesized_manifest = InstallManifest() for item, installs in test_installs.items(): for install_info in installs: if len(install_info) == 3: synthesized_manifest.add_pattern_symlink(*install_info) if len(install_info) == 2: synthesized_manifest.add_symlink(*install_info) self.assertEqual(len(synthesized_manifest), 3) for item, info in synthesized_manifest._dests.items(): self.assertIn(item, m) self.assertEqual(info, m._dests[item])
def test_test_manifest_deffered_installs_written(self): """Shared support files are written to their own data file by the backend.""" env = self._consume('test-manifest-shared-support', RecursiveMakeBackend) all_tests_path = mozpath.join(env.topobjdir, 'all-tests.json') self.assertTrue(os.path.exists(all_tests_path)) test_installs_path = mozpath.join(env.topobjdir, 'test-installs.json') with open(test_installs_path, 'r') as fh: test_installs = json.load(fh) self.assertEqual(set(test_installs.keys()), set(['child/test_sub.js', 'child/data/**', 'child/another-file.sjs'])) for key in test_installs.keys(): self.assertIn(key, test_installs) test_files_manifest = mozpath.join(env.topobjdir, '_build_manifests', 'install', '_test_files') # First, read the generated for ini manifest contents. m = InstallManifest(path=test_files_manifest) # Then, synthesize one from the test-installs.json file. This should # allow us to re-create a subset of the above. synthesized_manifest = InstallManifest() for item, installs in test_installs.items(): for install_info in installs: if len(install_info) == 3: synthesized_manifest.add_pattern_symlink(*install_info) if len(install_info) == 2: synthesized_manifest.add_symlink(*install_info) self.assertEqual(len(synthesized_manifest), 3) for item, info in synthesized_manifest._dests.items(): self.assertIn(item, m) self.assertEqual(info, m._dests[item])
def test_adds(self): m = InstallManifest() m.add_symlink("s_source", "s_dest") m.add_copy("c_source", "c_dest") m.add_required_exists("e_dest") m.add_optional_exists("o_dest") m.add_pattern_symlink("ps_base", "ps/*", "ps_dest") m.add_pattern_copy("pc_base", "pc/**", "pc_dest") m.add_preprocess("p_source", "p_dest", "p_source.pp") self.assertEqual(len(m), 7) self.assertIn("s_dest", m) self.assertIn("c_dest", m) self.assertIn("p_dest", m) self.assertIn("e_dest", m) self.assertIn("o_dest", m) with self.assertRaises(ValueError): m.add_symlink("s_other", "s_dest") with self.assertRaises(ValueError): m.add_copy("c_other", "c_dest") with self.assertRaises(ValueError): m.add_preprocess("p_other", "p_dest", "p_other.pp") with self.assertRaises(ValueError): m.add_required_exists("e_dest") with self.assertRaises(ValueError): m.add_optional_exists("o_dest") with self.assertRaises(ValueError): m.add_pattern_symlink("ps_base", "ps/*", "ps_dest") with self.assertRaises(ValueError): m.add_pattern_copy("pc_base", "pc/**", "pc_dest")