test_archive.py (6046B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 import hashlib 6 import os 7 import shutil 8 import stat 9 import tarfile 10 import tempfile 11 import unittest 12 13 import pytest 14 from mozunit import main 15 16 from mozpack.archive import ( 17 DEFAULT_MTIME, 18 create_tar_bz2_from_files, 19 create_tar_from_files, 20 create_tar_gz_from_files, 21 ) 22 from mozpack.files import GeneratedFile 23 24 MODE_STANDARD = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH 25 26 27 def file_hash(path): 28 h = hashlib.sha1() 29 with open(path, "rb") as fh: 30 while True: 31 data = fh.read(8192) 32 if not data: 33 break 34 h.update(data) 35 36 return h.hexdigest() 37 38 39 class TestArchive(unittest.TestCase): 40 def _create_files(self, root): 41 files = {} 42 for i in range(10): 43 p = os.path.join(root, "file%02d" % i) 44 with open(p, "wb") as fh: 45 fh.write(b"file%02d" % i) 46 # Need to set permissions or umask may influence testing. 47 os.chmod(p, MODE_STANDARD) 48 files["file%02d" % i] = p 49 50 for i in range(10): 51 files["file%02d" % (i + 10)] = GeneratedFile(b"file%02d" % (i + 10)) 52 53 return files 54 55 def _verify_basic_tarfile(self, tf): 56 self.assertEqual(len(tf.getmembers()), 20) 57 58 names = ["file%02d" % i for i in range(20)] 59 self.assertEqual(tf.getnames(), names) 60 61 for ti in tf.getmembers(): 62 self.assertEqual(ti.uid, 0) 63 self.assertEqual(ti.gid, 0) 64 self.assertEqual(ti.uname, "") 65 self.assertEqual(ti.gname, "") 66 self.assertEqual(ti.mode, MODE_STANDARD) 67 self.assertEqual(ti.mtime, DEFAULT_MTIME) 68 69 @pytest.mark.xfail( 70 reason="ValueError is not thrown despite being provided directory." 71 ) 72 def test_dirs_refused(self): 73 d = tempfile.mkdtemp() 74 try: 75 tp = os.path.join(d, "test.tar") 76 with open(tp, "wb") as fh: 77 with self.assertRaisesRegex(ValueError, "not a regular"): 78 create_tar_from_files(fh, {"test": d}) 79 finally: 80 shutil.rmtree(d) 81 82 @pytest.mark.xfail(reason="ValueError is not thrown despite uid/gid being set.") 83 def test_setuid_setgid_refused(self): 84 d = tempfile.mkdtemp() 85 try: 86 uid = os.path.join(d, "setuid") 87 gid = os.path.join(d, "setgid") 88 with open(uid, "a"): 89 pass 90 with open(gid, "a"): 91 pass 92 93 os.chmod(uid, MODE_STANDARD | stat.S_ISUID) 94 os.chmod(gid, MODE_STANDARD | stat.S_ISGID) 95 96 tp = os.path.join(d, "test.tar") 97 with open(tp, "wb") as fh: 98 with self.assertRaisesRegex(ValueError, "cannot add file with setuid"): 99 create_tar_from_files(fh, {"test": uid}) 100 with self.assertRaisesRegex(ValueError, "cannot add file with setuid"): 101 create_tar_from_files(fh, {"test": gid}) 102 finally: 103 shutil.rmtree(d) 104 105 def test_create_tar_basic(self): 106 d = tempfile.mkdtemp() 107 try: 108 files = self._create_files(d) 109 110 tp = os.path.join(d, "test.tar") 111 with open(tp, "wb") as fh: 112 create_tar_from_files(fh, files) 113 114 # Output should be deterministic. 115 self.assertEqual(file_hash(tp), "01cd314e277f060e98c7de6c8ea57f96b3a2065c") 116 117 with tarfile.open(tp, "r") as tf: 118 self._verify_basic_tarfile(tf) 119 120 finally: 121 shutil.rmtree(d) 122 123 @pytest.mark.xfail(reason="hash mismatch") 124 def test_executable_preserved(self): 125 d = tempfile.mkdtemp() 126 try: 127 p = os.path.join(d, "exec") 128 with open(p, "wb") as fh: 129 fh.write("#!/bin/bash\n") 130 os.chmod(p, MODE_STANDARD | stat.S_IXUSR) 131 132 tp = os.path.join(d, "test.tar") 133 with open(tp, "wb") as fh: 134 create_tar_from_files(fh, {"exec": p}) 135 136 self.assertEqual(file_hash(tp), "357e1b81c0b6cfdfa5d2d118d420025c3c76ee93") 137 138 with tarfile.open(tp, "r") as tf: 139 m = tf.getmember("exec") 140 self.assertEqual(m.mode, MODE_STANDARD | stat.S_IXUSR) 141 142 finally: 143 shutil.rmtree(d) 144 145 def test_create_tar_gz_basic(self): 146 d = tempfile.mkdtemp() 147 try: 148 files = self._create_files(d) 149 150 gp = os.path.join(d, "test.tar.gz") 151 with open(gp, "wb") as fh: 152 create_tar_gz_from_files(fh, files) 153 154 self.assertEqual(file_hash(gp), "7c4da5adc5088cdf00911d5daf9a67b15de714b7") 155 156 with tarfile.open(gp, "r:gz") as tf: 157 self._verify_basic_tarfile(tf) 158 159 finally: 160 shutil.rmtree(d) 161 162 def test_tar_gz_name(self): 163 d = tempfile.mkdtemp() 164 try: 165 files = self._create_files(d) 166 167 gp = os.path.join(d, "test.tar.gz") 168 with open(gp, "wb") as fh: 169 create_tar_gz_from_files(fh, files, filename="foobar") 170 171 self.assertEqual(file_hash(gp), "721e00083c17d16df2edbddf40136298c06d0c49") 172 173 with tarfile.open(gp, "r:gz") as tf: 174 self._verify_basic_tarfile(tf) 175 176 finally: 177 shutil.rmtree(d) 178 179 def test_create_tar_bz2_basic(self): 180 d = tempfile.mkdtemp() 181 try: 182 files = self._create_files(d) 183 184 bp = os.path.join(d, "test.tar.bz2") 185 with open(bp, "wb") as fh: 186 create_tar_bz2_from_files(fh, files) 187 188 self.assertEqual(file_hash(bp), "eb5096d2fbb71df7b3d690001a6f2e82a5aad6a7") 189 190 with tarfile.open(bp, "r:bz2") as tf: 191 self._verify_basic_tarfile(tf) 192 finally: 193 shutil.rmtree(d) 194 195 196 if __name__ == "__main__": 197 main()