# 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/.
import os import stat import unittest
import mozunit import six
import mozpack.path as mozpath from mozpack.copier import FileCopier, FileRegistry, FileRegistrySubtree, Jarrer from mozpack.errors import ErrorMessage from mozpack.files import ExistingFile, GeneratedFile from mozpack.mozjar import JarReader from mozpack.test.test_files import MatchTestTemplate, MockDest, TestWithTmpDir
class BaseTestFileRegistry(MatchTestTemplate): def add(self, path):
self.registry.add(path, GeneratedFile(path))
# Can't add a file if it requires a directory in place of a # file we also require.
self.registry.add("foo", GeneratedFile(b"foo"))
self.assertRaises(
ErrorMessage, self.registry.add, "foo/bar", GeneratedFile(b"foobar")
)
# Can't add a file if we already have a directory there.
self.registry.add("bar/baz", GeneratedFile(b"barbaz"))
self.assertRaises(ErrorMessage, self.registry.add, "bar", GeneratedFile(b"bar"))
# Bump the count of things that require bar/ to 2.
self.registry.add("bar/zot", GeneratedFile(b"barzot"))
self.assertRaises(ErrorMessage, self.registry.add, "bar", GeneratedFile(b"bar"))
# Drop the count of things that require bar/ to 1.
self.registry.remove("bar/baz")
self.assertRaises(ErrorMessage, self.registry.add, "bar", GeneratedFile(b"bar"))
# Drop the count of things that require bar/ to 0.
self.registry.remove("bar/zot")
self.registry.add("bar/zot", GeneratedFile(b"barzot"))
class TestFileRegistry(BaseTestFileRegistry, unittest.TestCase): def test_partial_paths(self):
cases = { "foo/bar/baz/zot": ["foo/bar/baz", "foo/bar", "foo"], "foo/bar": ["foo"], "bar": [],
}
reg = FileRegistry() for path, parts in six.iteritems(cases):
self.assertEqual(reg._partial_paths(path), parts)
class TestFileCopier(TestWithTmpDir): def all_dirs(self, base):
all_dirs = set() for root, dirs, files in os.walk(base): ifnot dirs:
all_dirs.add(mozpath.relpath(root, base)) return all_dirs
def all_files(self, base):
all_files = set() for root, dirs, files in os.walk(base): for f in files:
all_files.add(mozpath.join(mozpath.relpath(root, base), f)) return all_files
result = copier.copy(self.tmpdir)
self.assertEqual(self.all_files(self.tmpdir), set(copier.paths()))
self.assertEqual(
self.all_dirs(self.tmpdir), set(["foo/deep/nested/directory", "qux"])
)
self.assertEqual(
result.updated_files,
set(self.tmppath(p) for p in self.all_files(self.tmpdir)),
)
self.assertEqual(result.existing_files, set())
self.assertEqual(result.removed_files, set())
self.assertEqual(result.removed_directories, set())
copier.remove("foo")
copier.add("test", GeneratedFile(b"test"))
result = copier.copy(self.tmpdir)
self.assertEqual(self.all_files(self.tmpdir), set(copier.paths()))
self.assertEqual(self.all_dirs(self.tmpdir), set(["qux"]))
self.assertEqual(
result.removed_files,
set(
self.tmppath(p) for p in ("foo/bar", "foo/qux", "foo/deep/nested/directory/file")
),
)
def test_symlink_directory_replaced(self): """Directory symlinks in destination are replaced if they need to be
real directories.""" ifnot self.symlink_supported: return
def test_remove_unaccounted_directory_symlinks(self): """Directory symlinks in destination that are not in the way are
deleted according to remove_unaccounted and
remove_all_directory_symlinks. """ ifnot self.symlink_supported: return
os.mkdir(self.tmppath("dest/zot"))
link = self.tmppath("dest/zot/zap")
os.symlink(dummy, link)
# If not remove_unaccounted but remove_empty_directories, then # the symlinked directory remains (as does its containing # directory).
result = copier.copy(
dest,
remove_unaccounted=False,
remove_empty_directories=True,
remove_all_directory_symlinks=False,
)
st = os.lstat(link)
self.assertTrue(stat.S_ISLNK(st.st_mode))
self.assertFalse(stat.S_ISDIR(st.st_mode))
# If remove_unaccounted but not remove_empty_directories, then # only the symlinked directory is removed.
result = copier.copy(
dest,
remove_unaccounted=True,
remove_empty_directories=False,
remove_all_directory_symlinks=False,
)
st = os.lstat(self.tmppath("dest/zot"))
self.assertFalse(stat.S_ISLNK(st.st_mode))
self.assertTrue(stat.S_ISDIR(st.st_mode))
# If remove_unaccounted and remove_empty_directories, then # both the symlink and its containing directory are removed.
link = self.tmppath("dest/zot/zap")
os.symlink(dummy, link)
result = copier.copy(
dest,
remove_unaccounted=True,
remove_empty_directories=True,
remove_all_directory_symlinks=False,
)
def test_permissions(self): """Ensure files without write permission can be deleted.""" with open(self.tmppath("dummy"), "a"): pass
p = self.tmppath("no_perms") with open(p, "a"): pass
# Make file and directory unwritable. Reminder: making a directory # unwritable prevents modifications (including deletes) from the list # of files in that directory.
os.chmod(p, 0o400)
os.chmod(self.tmpdir, 0o400)
def test_optional_exists_creates_unneeded_directory(self): """Demonstrate that a directory not strictly required, but specified as the path to an optional file, will be unnecessarily created.
This behaviour is wrong; fixing it is tracked by Bug 972432; and this test exists to guard against unexpected changes in
behaviour. """
st = os.lstat(self.tmppath("dest/foo"))
self.assertFalse(stat.S_ISLNK(st.st_mode))
self.assertTrue(stat.S_ISDIR(st.st_mode))
# What's worse, we have no record that dest was created.
self.assertEqual(len(result.updated_files), 0)
# But we do have an erroneous record of an optional file # existing when it does not.
self.assertIn(self.tmppath("dest/foo/bar"), result.existing_files)
# We expect only files copied from the first copier to be removed, # not the extra file that was there beforehand.
result = copier2.copy(dest, remove_unaccounted=copier)
self.assertEqual(
self.all_files(dest), set(copier2.paths()) | {"foo/toto/toto", "bar/bar"}
)
self.assertEqual(self.all_dirs(dest), {"foo/hoge", "foo/toto", "bar"})
self.assertEqual(result.updated_files, {self.tmppath("dest/foo/hoge/fuga")})
self.assertEqual(result.existing_files, set())
self.assertEqual(
result.removed_files,
{
self.tmppath(p) for p in ("dest/foo/bar/baz", "dest/foo/bar/qux", "dest/foo/toto/tata")
},
)
self.assertEqual(result.removed_directories, {self.tmppath("dest/foo/bar")})
class TestJarrer(unittest.TestCase): def check_jar(self, dest, copier):
jar = JarReader(fileobj=dest)
self.assertEqual([f.filename for f in jar], copier.paths()) for f in jar:
self.assertEqual(f.uncompressed_data.read(), copier[f.filename].content)
dest.seek(0)
jar = JarReader(fileobj=dest)
self.assertEqual(
[f.filename for f in jar],
preloaded + [p for p in copier.paths() if p notin preloaded],
)
self.assertEqual(jar.last_preloaded, preloaded[-1])
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.