1*61c4878aSAndroid Build Coastguard Worker# Copyright 2020 The Pigweed Authors 2*61c4878aSAndroid Build Coastguard Worker# 3*61c4878aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4*61c4878aSAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of 5*61c4878aSAndroid Build Coastguard Worker# the License at 6*61c4878aSAndroid Build Coastguard Worker# 7*61c4878aSAndroid Build Coastguard Worker# https://www.apache.org/licenses/LICENSE-2.0 8*61c4878aSAndroid Build Coastguard Worker# 9*61c4878aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*61c4878aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11*61c4878aSAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12*61c4878aSAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under 13*61c4878aSAndroid Build Coastguard Worker# the License. 14*61c4878aSAndroid Build Coastguard Worker"""Tests for the pw_build.zip module.""" 15*61c4878aSAndroid Build Coastguard Worker 16*61c4878aSAndroid Build Coastguard Workerimport unittest 17*61c4878aSAndroid Build Coastguard Workerimport os 18*61c4878aSAndroid Build Coastguard Workerimport tempfile 19*61c4878aSAndroid Build Coastguard Workerimport pathlib 20*61c4878aSAndroid Build Coastguard Workerimport zipfile 21*61c4878aSAndroid Build Coastguard Worker 22*61c4878aSAndroid Build Coastguard Workerfrom pw_build.zip import zip_up, ZipError 23*61c4878aSAndroid Build Coastguard Worker 24*61c4878aSAndroid Build Coastguard WorkerDELIMITER = '>' 25*61c4878aSAndroid Build Coastguard WorkerIN_FILENAMES = [ 26*61c4878aSAndroid Build Coastguard Worker 'file1.txt', 27*61c4878aSAndroid Build Coastguard Worker 'file2.txt', 28*61c4878aSAndroid Build Coastguard Worker 'dir1/file3.txt', 29*61c4878aSAndroid Build Coastguard Worker 'dir1/file4.txt', 30*61c4878aSAndroid Build Coastguard Worker 'dir1/dir2/file5.txt', 31*61c4878aSAndroid Build Coastguard Worker 'dir1/dir2/file6.txt', 32*61c4878aSAndroid Build Coastguard Worker] 33*61c4878aSAndroid Build Coastguard Worker 34*61c4878aSAndroid Build Coastguard Worker 35*61c4878aSAndroid Build Coastguard Workerdef make_directory(parent_path: pathlib.Path, dir_name: str, filenames: list): 36*61c4878aSAndroid Build Coastguard Worker """Creates a directory and returns a pathlib.Path() of it's root dir. 37*61c4878aSAndroid Build Coastguard Worker 38*61c4878aSAndroid Build Coastguard Worker Args: 39*61c4878aSAndroid Build Coastguard Worker parent_path: Path to directory where the new directory will be made. 40*61c4878aSAndroid Build Coastguard Worker dir_name: Name of the new directory. 41*61c4878aSAndroid Build Coastguard Worker filenames: list of file contents of the new directory. Also allows 42*61c4878aSAndroid Build Coastguard Worker the creation of subdirectories. Example: 43*61c4878aSAndroid Build Coastguard Worker [ 44*61c4878aSAndroid Build Coastguard Worker 'file1.txt', 45*61c4878aSAndroid Build Coastguard Worker 'subdir/file2.txt' 46*61c4878aSAndroid Build Coastguard Worker ] 47*61c4878aSAndroid Build Coastguard Worker 48*61c4878aSAndroid Build Coastguard Worker Returns: pathlib.Path() to the newly created directory. 49*61c4878aSAndroid Build Coastguard Worker """ 50*61c4878aSAndroid Build Coastguard Worker root_path = pathlib.Path(parent_path / dir_name) 51*61c4878aSAndroid Build Coastguard Worker os.mkdir(root_path) 52*61c4878aSAndroid Build Coastguard Worker for filename in filenames: 53*61c4878aSAndroid Build Coastguard Worker # Make the sub directories if they don't already exist. 54*61c4878aSAndroid Build Coastguard Worker directories = filename.split('/')[:-1] 55*61c4878aSAndroid Build Coastguard Worker for i in range(len(directories)): 56*61c4878aSAndroid Build Coastguard Worker directory = pathlib.PurePath('/'.join(directories[: i + 1])) 57*61c4878aSAndroid Build Coastguard Worker if not (root_path / directory).is_dir(): 58*61c4878aSAndroid Build Coastguard Worker os.mkdir(root_path / directory) 59*61c4878aSAndroid Build Coastguard Worker 60*61c4878aSAndroid Build Coastguard Worker # Create a file at the destination. 61*61c4878aSAndroid Build Coastguard Worker touch(root_path, filename) 62*61c4878aSAndroid Build Coastguard Worker return root_path 63*61c4878aSAndroid Build Coastguard Worker 64*61c4878aSAndroid Build Coastguard Worker 65*61c4878aSAndroid Build Coastguard Workerdef touch(parent_dir: pathlib.Path, filename: str): 66*61c4878aSAndroid Build Coastguard Worker """Creates an empty file at parent_dir/filename.""" 67*61c4878aSAndroid Build Coastguard Worker with open(parent_dir / filename, 'a') as touch_file: 68*61c4878aSAndroid Build Coastguard Worker touch_file.write(filename) 69*61c4878aSAndroid Build Coastguard Worker 70*61c4878aSAndroid Build Coastguard Worker 71*61c4878aSAndroid Build Coastguard Workerdef get_directory_contents(path: pathlib.Path): 72*61c4878aSAndroid Build Coastguard Worker """Iterates through a directory and returns a set of its contents.""" 73*61c4878aSAndroid Build Coastguard Worker contents = set() 74*61c4878aSAndroid Build Coastguard Worker for filename in path.glob('**/*'): 75*61c4878aSAndroid Build Coastguard Worker # Remove the original parent directories to get just the relative path. 76*61c4878aSAndroid Build Coastguard Worker contents.add(filename.relative_to(path)) 77*61c4878aSAndroid Build Coastguard Worker return contents 78*61c4878aSAndroid Build Coastguard Worker 79*61c4878aSAndroid Build Coastguard Worker 80*61c4878aSAndroid Build Coastguard Workerclass TestZipping(unittest.TestCase): 81*61c4878aSAndroid Build Coastguard Worker """Tests for the pw_build.zip module.""" 82*61c4878aSAndroid Build Coastguard Worker 83*61c4878aSAndroid Build Coastguard Worker def test_zip_up_file(self): 84*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 85*61c4878aSAndroid Build Coastguard Worker # Arrange. 86*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 87*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 88*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/file1.txt {DELIMITER} /'] 89*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 90*61c4878aSAndroid Build Coastguard Worker 91*61c4878aSAndroid Build Coastguard Worker # Act. 92*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 93*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 94*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 95*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 96*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory(tmp_path, 'expected', ['file1.txt']) 97*61c4878aSAndroid Build Coastguard Worker 98*61c4878aSAndroid Build Coastguard Worker # Assert. 99*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 100*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 101*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 102*61c4878aSAndroid Build Coastguard Worker ) 103*61c4878aSAndroid Build Coastguard Worker 104*61c4878aSAndroid Build Coastguard Worker def test_zip_up_dir(self): 105*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 106*61c4878aSAndroid Build Coastguard Worker # Arrange. 107*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 108*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 109*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/dir1/ {DELIMITER} /'] 110*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 111*61c4878aSAndroid Build Coastguard Worker 112*61c4878aSAndroid Build Coastguard Worker # Act. 113*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 114*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 115*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 116*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 117*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory( 118*61c4878aSAndroid Build Coastguard Worker tmp_path, 119*61c4878aSAndroid Build Coastguard Worker 'expected', 120*61c4878aSAndroid Build Coastguard Worker [ 121*61c4878aSAndroid Build Coastguard Worker 'file3.txt', 122*61c4878aSAndroid Build Coastguard Worker 'file4.txt', 123*61c4878aSAndroid Build Coastguard Worker 'dir2/file5.txt', 124*61c4878aSAndroid Build Coastguard Worker 'dir2/file6.txt', 125*61c4878aSAndroid Build Coastguard Worker ], 126*61c4878aSAndroid Build Coastguard Worker ) 127*61c4878aSAndroid Build Coastguard Worker 128*61c4878aSAndroid Build Coastguard Worker # Assert. 129*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 130*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 131*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 132*61c4878aSAndroid Build Coastguard Worker ) 133*61c4878aSAndroid Build Coastguard Worker 134*61c4878aSAndroid Build Coastguard Worker def test_file_rename(self): 135*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 136*61c4878aSAndroid Build Coastguard Worker # Arrange. 137*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 138*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 139*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/file1.txt {DELIMITER} /renamed.txt'] 140*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 141*61c4878aSAndroid Build Coastguard Worker 142*61c4878aSAndroid Build Coastguard Worker # Act. 143*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 144*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 145*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 146*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 147*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory( 148*61c4878aSAndroid Build Coastguard Worker tmp_path, 'expected', ['renamed.txt'] 149*61c4878aSAndroid Build Coastguard Worker ) 150*61c4878aSAndroid Build Coastguard Worker 151*61c4878aSAndroid Build Coastguard Worker # Assert. 152*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 153*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 154*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 155*61c4878aSAndroid Build Coastguard Worker ) 156*61c4878aSAndroid Build Coastguard Worker 157*61c4878aSAndroid Build Coastguard Worker def test_file_move(self): 158*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 159*61c4878aSAndroid Build Coastguard Worker # Arrange. 160*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 161*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 162*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/file1.txt {DELIMITER} /foo/'] 163*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 164*61c4878aSAndroid Build Coastguard Worker 165*61c4878aSAndroid Build Coastguard Worker # Act. 166*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 167*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 168*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 169*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 170*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory( 171*61c4878aSAndroid Build Coastguard Worker tmp_path, 'expected', ['foo/file1.txt'] 172*61c4878aSAndroid Build Coastguard Worker ) 173*61c4878aSAndroid Build Coastguard Worker 174*61c4878aSAndroid Build Coastguard Worker # Assert. 175*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 176*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 177*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 178*61c4878aSAndroid Build Coastguard Worker ) 179*61c4878aSAndroid Build Coastguard Worker 180*61c4878aSAndroid Build Coastguard Worker def test_dir_move(self): 181*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 182*61c4878aSAndroid Build Coastguard Worker # Arrange. 183*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 184*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 185*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/dir1/ {DELIMITER} /foo/'] 186*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 187*61c4878aSAndroid Build Coastguard Worker 188*61c4878aSAndroid Build Coastguard Worker # Act. 189*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 190*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 191*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 192*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 193*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory( 194*61c4878aSAndroid Build Coastguard Worker tmp_path, 195*61c4878aSAndroid Build Coastguard Worker 'expected', 196*61c4878aSAndroid Build Coastguard Worker [ 197*61c4878aSAndroid Build Coastguard Worker 'foo/file3.txt', 198*61c4878aSAndroid Build Coastguard Worker 'foo/file4.txt', 199*61c4878aSAndroid Build Coastguard Worker 'foo/dir2/file5.txt', 200*61c4878aSAndroid Build Coastguard Worker 'foo/dir2/file6.txt', 201*61c4878aSAndroid Build Coastguard Worker ], 202*61c4878aSAndroid Build Coastguard Worker ) 203*61c4878aSAndroid Build Coastguard Worker 204*61c4878aSAndroid Build Coastguard Worker # Assert. 205*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 206*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 207*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 208*61c4878aSAndroid Build Coastguard Worker ) 209*61c4878aSAndroid Build Coastguard Worker 210*61c4878aSAndroid Build Coastguard Worker def test_change_delimiter(self): 211*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 212*61c4878aSAndroid Build Coastguard Worker # Arrange. 213*61c4878aSAndroid Build Coastguard Worker tmp_path = pathlib.Path(tmp_dir) 214*61c4878aSAndroid Build Coastguard Worker in_path = make_directory(tmp_path, 'in', IN_FILENAMES) 215*61c4878aSAndroid Build Coastguard Worker delimiter = '==>' 216*61c4878aSAndroid Build Coastguard Worker input_list = [f'{in_path}/file1.txt {delimiter} /'] 217*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_path}/out.zip' 218*61c4878aSAndroid Build Coastguard Worker 219*61c4878aSAndroid Build Coastguard Worker # Act. 220*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename, delimiter=delimiter) 221*61c4878aSAndroid Build Coastguard Worker out_path = pathlib.Path(f'{tmp_path}/out/') 222*61c4878aSAndroid Build Coastguard Worker with zipfile.ZipFile(out_filename, 'r') as zip_file: 223*61c4878aSAndroid Build Coastguard Worker zip_file.extractall(out_path) 224*61c4878aSAndroid Build Coastguard Worker expected_path = make_directory(tmp_path, 'expected', ['file1.txt']) 225*61c4878aSAndroid Build Coastguard Worker 226*61c4878aSAndroid Build Coastguard Worker # Assert. 227*61c4878aSAndroid Build Coastguard Worker self.assertSetEqual( 228*61c4878aSAndroid Build Coastguard Worker get_directory_contents(out_path), 229*61c4878aSAndroid Build Coastguard Worker get_directory_contents(expected_path), 230*61c4878aSAndroid Build Coastguard Worker ) 231*61c4878aSAndroid Build Coastguard Worker 232*61c4878aSAndroid Build Coastguard Worker def test_wrong_input_syntax_raises_error(self): 233*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 234*61c4878aSAndroid Build Coastguard Worker # Arrange. 235*61c4878aSAndroid Build Coastguard Worker bad_inputs = [ 236*61c4878aSAndroid Build Coastguard Worker '', # Empty input 237*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/ /', # No delimiter 238*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/ {DELIMITER} ', # No zip destination 239*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir} /', # No source 240*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/', # No delimiter or zip destination 241*61c4878aSAndroid Build Coastguard Worker f'{DELIMITER}', # No source or zip destination 242*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir} {DELIMITER} /', # No trailing source '/' 243*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/ {DELIMITER} foo/', # No leading zip root '/' 244*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/ {DELIMITER} /foo', # No trailing zip dest '/' 245*61c4878aSAndroid Build Coastguard Worker f'{tmp_dir}/ {DELIMITER} /{tmp_dir}/ ' 246*61c4878aSAndroid Build Coastguard Worker f'{DELIMITER} /{tmp_dir}/', # Too many paths on split 247*61c4878aSAndroid Build Coastguard Worker ] 248*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_dir}/out.zip' 249*61c4878aSAndroid Build Coastguard Worker 250*61c4878aSAndroid Build Coastguard Worker # Act & Assert. 251*61c4878aSAndroid Build Coastguard Worker for bad_input in bad_inputs: 252*61c4878aSAndroid Build Coastguard Worker with self.assertRaises(ZipError): 253*61c4878aSAndroid Build Coastguard Worker zip_up([bad_input], out_filename) 254*61c4878aSAndroid Build Coastguard Worker 255*61c4878aSAndroid Build Coastguard Worker def test_nonexistant_file_raises_error(self): 256*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 257*61c4878aSAndroid Build Coastguard Worker # Arrange. 258*61c4878aSAndroid Build Coastguard Worker input_list = [f'{tmp_dir}/nonexistant-file.txt > /'] 259*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_dir}/out.zip' 260*61c4878aSAndroid Build Coastguard Worker 261*61c4878aSAndroid Build Coastguard Worker # Act & Assert. 262*61c4878aSAndroid Build Coastguard Worker with self.assertRaises(ZipError): 263*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 264*61c4878aSAndroid Build Coastguard Worker 265*61c4878aSAndroid Build Coastguard Worker def test_nonexistant_dir_raises_error(self): 266*61c4878aSAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmp_dir: 267*61c4878aSAndroid Build Coastguard Worker # Arrange. 268*61c4878aSAndroid Build Coastguard Worker input_list = [f'{tmp_dir}/nonexistant-dir/ > /'] 269*61c4878aSAndroid Build Coastguard Worker out_filename = f'{tmp_dir}/out.zip' 270*61c4878aSAndroid Build Coastguard Worker 271*61c4878aSAndroid Build Coastguard Worker # Act & Assert. 272*61c4878aSAndroid Build Coastguard Worker with self.assertRaises(ZipError): 273*61c4878aSAndroid Build Coastguard Worker zip_up(input_list, out_filename) 274*61c4878aSAndroid Build Coastguard Worker 275*61c4878aSAndroid Build Coastguard Worker 276*61c4878aSAndroid Build Coastguard Workerif __name__ == '__main__': 277*61c4878aSAndroid Build Coastguard Worker unittest.main() 278