blob: eb8a1273d205fdc374eb03e794de89fbf49c3851 [file] [log] [blame]
# Copyright 2015 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Testing for pkg_tar."""
import tarfile
import unittest
from bazel_tools.tools.python.runfiles import runfiles
from pkg.private.tar import tar_writer
PORTABLE_MTIME = 946684800 # 2000-01-01 00:00:00.000 UTC
class PkgTarTest(unittest.TestCase):
"""Testing for pkg_tar rule."""
def assertTarFileContent(self, file_name, content, verbose=False):
"""Assert that tarfile contains exactly the entry described by `content`.
Args:
file_name: the path to the TAR file to test.
content: an array describing the expected content of the TAR file.
Each entry in that list should be a dictionary where each field
is a field to test in the corresponding TarInfo. For
testing the presence of a file "x", then the entry could simply
be `{'name': 'x'}`, the missing field will be ignored. To match
the content of a file entry, use the key 'data'.
"""
# NOTE: This is portable to Windows. os.path.join('rules_pkg', 'tests',
# filename) is not.
file_path = runfiles.Create().Rlocation('rules_pkg/tests/tar/' + file_name)
got = []
with tarfile.open(file_path, 'r:*') as f:
i = 0
for info in f:
if verbose:
print(' >> from tar file:', info.name)
error_msg = 'Extraneous file at end of archive %s: %s' % (
file_path,
info.name
)
self.assertLess(i, len(content), error_msg)
for k, v in content[i].items():
if k == 'halt':
return
elif k == 'data':
value = f.extractfile(info).read()
elif k == 'isdir':
value = info.isdir()
else:
value = getattr(info, k)
if k == 'mode':
p_value = '0o%o' % value
p_v = '0o%o' % v
else:
p_value = str(value)
p_v = str(v)
error_msg = ' '.join([
'Value `%s` for key `%s` of file' % (p_value, k),
'%s in archive %s does' % (info.name, file_path),
'not match expected value `%s`' % p_v
])
self.assertEqual(value, v, error_msg)
if value != v:
print(error_msg)
i += 1
if i < len(content):
self.fail('Missing file %s in archive %s of [%s]' % (
content[i], file_path, ',\n '.join(got)))
def test_strip_prefix_empty(self):
content = [
{'name': 'nsswitch.conf'},
]
self.assertTarFileContent('test-tar-strip_prefix-empty.tar', content)
def test_strip_prefix_none(self):
content = [
{'name': 'nsswitch.conf'},
]
self.assertTarFileContent('test-tar-strip_prefix-none.tar', content)
def test_strip_prefix_etc(self):
content = [
{'name': 'nsswitch.conf'},
]
self.assertTarFileContent('test-tar-strip_prefix-etc.tar', content)
def test_strip_prefix_substring(self):
content = [
{'name': 'etc', 'isdir': True},
{'name': 'etc/nsswitch.conf'},
]
self.assertTarFileContent('test-tar-strip_prefix-substring.tar', content)
def test_strip_prefix_dot(self):
content = [
{'name': 'etc'},
{'name': 'etc/nsswitch.conf'},
{'name': 'external'},
{'name': 'external/bazel_tools'},
{'name': 'external/bazel_tools/tools'},
{'name': 'external/bazel_tools/tools/python'},
{'name': 'external/bazel_tools/tools/python/runfiles'},
# This is brittle. In old bazel the next file would be
# external/bazel_tools/tools/python/runfiles/runfiles.py, but there
# is now _runfiles_constants.py, first. So this is too brittle.
{'halt': None},
]
self.assertTarFileContent('test-tar-strip_prefix-dot.tar', content)
def test_strip_files_dict(self):
content = [
{'name': 'not-etc'},
{'name': 'not-etc/mapped-filename.conf'},
]
self.assertTarFileContent('test-tar-files_dict.tar', content)
def test_empty_files(self):
content = [
{'name': 'a', 'size': 0, 'uid': 0},
{'name': 'b', 'size': 0, 'uid': 0, 'mtime': PORTABLE_MTIME},
]
self.assertTarFileContent('test-tar-empty_files.tar', content)
def test_empty_dirs(self):
content = [
{'name': 'pmt', 'isdir': True, 'size': 0, 'uid': 0,
'mtime': PORTABLE_MTIME},
{'name': 'tmp', 'isdir': True, 'size': 0, 'uid': 0,
'mtime': PORTABLE_MTIME},
]
self.assertTarFileContent('test-tar-empty_dirs.tar', content)
def test_mtime(self):
# Note strange mtime. It is specified in the BUILD file.
content = [
{'name': 'nsswitch.conf', 'mtime': 946684740},
]
self.assertTarFileContent('test-tar-mtime.tar', content)
def test_basic(self):
# Check the set of 'test-tar-basic-*' smoke test.
content = [
{'name': 'etc',
'uid': 24, 'gid': 42, 'uname': 'tata', 'gname': 'titi'},
{'name': 'etc/nsswitch.conf',
'mode': 0o644,
'uid': 24, 'gid': 42, 'uname': 'tata', 'gname': 'titi'
},
{'name': 'usr',
'uid': 42, 'gid': 24, 'uname': 'titi', 'gname': 'tata'},
{'name': 'usr/bin'},
{'name': 'usr/bin/java', 'linkname': '/path/to/bin/java'},
{'name': 'usr/titi',
'mode': 0o755,
'uid': 42, 'gid': 24, 'uname': 'titi', 'gname': 'tata'},
]
for ext in [('.' + comp if comp else '')
for comp in tar_writer.COMPRESSIONS]:
with self.subTest(ext=ext):
self.assertTarFileContent('test-tar-basic-%s.tar%s' % (ext[1:], ext),
content)
def test_file_inclusion(self):
content = [
{'name': 'etc', 'uid': 24, 'gid': 42},
{'name': 'etc/nsswitch.conf', 'mode': 0o644, 'uid': 24, 'gid': 42},
{'name': 'usr', 'uid': 42, 'gid': 24},
{'name': 'usr/bin'},
{'name': 'usr/bin/java', 'linkname': '/path/to/bin/java'},
{'name': 'usr/titi', 'mode': 0o755, 'uid': 42, 'gid': 24},
{'name': 'BUILD'},
]
for ext in [('.' + comp if comp else '')
for comp in tar_writer.COMPRESSIONS]:
with self.subTest(ext=ext):
self.assertTarFileContent('test-tar-inclusion-%s.tar' % ext[1:],
content)
def test_strip_prefix_empty(self):
content = [
{'name': 'level1'},
{'name': 'level1/some_value'},
{'name': 'level1/some_value/level3'},
{'name': 'level1/some_value/level3/BUILD'},
{'name': 'level1/some_value/level3/mydir'},
]
self.assertTarFileContent('test_tar_package_dir_substitution.tar', content)
def test_tar_with_long_file_name(self):
content = [
{'name': 'file_with_a_ridiculously_long_name_consectetur_adipiscing_elit_fusce_laoreet_lorem_neque_sed_pharetra_erat.txt'}
]
self.assertTarFileContent('test-tar-long-filename.tar', content)
def test_repackage_file_with_long_name(self):
content = [
{'name': 'can_i_repackage_a_file_with_a_long_name'},
{'name': 'can_i_repackage_a_file_with_a_long_name/file_with_a_ridiculously_long_name_consectetur_adipiscing_elit_fusce_laoreet_lorem_neque_sed_pharetra_erat.txt'}
]
self.assertTarFileContent('test-tar-repackaging-long-filename.tar', content)
def test_tar_with_tree_artifact(self):
# (sorted) list of files:
# "a/a"
# "a/b/c"
# "b/c/d"
# "b/d"
# "b/e"
content = [
{'name': 'a_tree', 'isdir': True},
{'name': 'a_tree/generate_tree', 'isdir': True, 'mode': 0o755},
{'name': 'a_tree/generate_tree/a', 'isdir': True, 'mode': 0o755},
{'name': 'a_tree/generate_tree/a/a'},
{'name': 'a_tree/generate_tree/a/b', 'isdir': True, 'mode': 0o755},
{'name': 'a_tree/generate_tree/a/b/c'},
{'name': 'a_tree/generate_tree/b', 'isdir': True, 'mode': 0o755},
{'name': 'a_tree/generate_tree/b/c', 'isdir': True, 'mode': 0o755},
{'name': 'a_tree/generate_tree/b/c/d'},
{'name': 'a_tree/generate_tree/b/d'},
{'name': 'a_tree/generate_tree/b/e'},
{'name': 'a_tree/loremipsum.txt'},
]
self.assertTarFileContent('test-tar-tree-artifact.tar', content)
# Now test against the tree artifact with the dir name stripped.
noroot_content = []
for c in content[1:]: # one less level in tree. Skip first element.
nc = dict(c)
nc['name'] = c['name'].replace('/generate_tree', '')
noroot_content.append(nc)
self.assertTarFileContent('test-tar-tree-artifact-noroot.tar',
noroot_content)
def test_tar_with_runfiles(self):
content = [
{'name': 'BUILD' },
{'name': 'a_program' },
{'name': 'executable.sh' },
]
self.assertTarFileContent('test-tar-with-runfiles.tar', content)
def test_tar_leading_dotslash(self):
content = [
{'name': './loremipsum.txt'},
]
self.assertTarFileContent('test_tar_leading_dotslash.tar', content)
def test_pkg_tar_with_attributes(self):
content = [
{'name': 'foo','uid': 0, 'gid': 1000, 'uname': '', 'gname': ''},
{'name': 'foo/bar','uid': 0, 'gid': 1000, 'uname': '', 'gname': ''},
{'name': 'foo/bar/loremipsum.txt','uid': 0, 'gid': 1000, 'uname': '', 'gname': ''},
]
self.assertTarFileContent('test-pkg-tar-with-attributes.tar', content)
def test_pkg_files_with_attributes(self):
content = [
{'name': 'foo','uid': 0, 'gid': 1000, 'uname': 'person', 'gname': 'grp'},
{'name': 'foo/bar','uid': 0, 'gid': 1000, 'uname': 'person', 'gname': 'grp'},
{'name': 'foo/bar/loremipsum.txt','uid': 0, 'gid': 1000, 'uname': 'person', 'gname': 'grp'},
]
self.assertTarFileContent('test-pkg-tar-from-pkg-files-with-attributes.tar', content)
def test_tar_with_tree_artifact_and_strip_prefix(self):
content = [
{'name': 'a', 'isdir': True},
{'name': 'a/a'},
{'name': 'a/b'},
]
self.assertTarFileContent('test-tree-input-with-strip-prefix.tar', content)
def test_remap_paths_tree_artifact(self):
content = [
{'name': 'a_new_name', 'isdir': True},
{'name': 'a_new_name/a'},
{'name': 'a_new_name/rename_me', 'isdir': True},
{'name': 'a_new_name/rename_me/should_not_rename'},
]
self.assertTarFileContent('test-remap-paths-tree-artifact.tar', content)
if __name__ == '__main__':
unittest.main()