# Copyright 2020 The Pigweed Authors # # 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 # # https://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. """Tests for the pw_build.zip module.""" import unittest import os import tempfile import pathlib import zipfile from pw_build.zip import zip_up, ZipError DELIMITER = '>' IN_FILENAMES = [ 'file1.txt', 'file2.txt', 'dir1/file3.txt', 'dir1/file4.txt', 'dir1/dir2/file5.txt', 'dir1/dir2/file6.txt', ] def make_directory(parent_path: pathlib.Path, dir_name: str, filenames: list): """Creates a directory and returns a pathlib.Path() of it's root dir. Args: parent_path: Path to directory where the new directory will be made. dir_name: Name of the new directory. filenames: list of file contents of the new directory. Also allows the creation of subdirectories. Example: [ 'file1.txt', 'subdir/file2.txt' ] Returns: pathlib.Path() to the newly created directory. """ root_path = pathlib.Path(parent_path / dir_name) os.mkdir(root_path) for filename in filenames: # Make the sub directories if they don't already exist. directories = filename.split('/')[:-1] for i in range(len(directories)): directory = pathlib.PurePath('/'.join(directories[:i + 1])) if not (root_path / directory).is_dir(): os.mkdir(root_path / directory) # Create a file at the destination. touch(root_path, filename) return root_path def touch(parent_dir: pathlib.Path, filename: str): """Creates an empty file at parent_dir/filename.""" with open(parent_dir / filename, 'a') as touch_file: touch_file.write(filename) def get_directory_contents(path: pathlib.Path): """Iterates through a directory and returns a set of its contents.""" contents = set() for filename in path.glob('**/*'): # Remove the original parent directories to get just the relative path. contents.add(filename.relative_to(path)) return contents class TestZipping(unittest.TestCase): """Tests for the pw_build.zip module.""" def test_zip_up_file(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) input_list = [f'{in_path}/file1.txt {DELIMITER} /'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', ['file1.txt']) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_zip_up_dir(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) input_list = [f'{in_path}/dir1/ {DELIMITER} /'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', [ 'file3.txt', 'file4.txt', 'dir2/file5.txt', 'dir2/file6.txt', ]) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_file_rename(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) input_list = [f'{in_path}/file1.txt {DELIMITER} /renamed.txt'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', ['renamed.txt']) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_file_move(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) input_list = [f'{in_path}/file1.txt {DELIMITER} /foo/'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', ['foo/file1.txt']) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_dir_move(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) input_list = [f'{in_path}/dir1/ {DELIMITER} /foo/'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', [ 'foo/file3.txt', 'foo/file4.txt', 'foo/dir2/file5.txt', 'foo/dir2/file6.txt', ]) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_change_delimiter(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. tmp_path = pathlib.Path(tmp_dir) in_path = make_directory(tmp_path, 'in', IN_FILENAMES) delimiter = '==>' input_list = [f'{in_path}/file1.txt {delimiter} /'] out_filename = f'{tmp_path}/out.zip' # Act. zip_up(input_list, out_filename, delimiter=delimiter) out_path = pathlib.Path(f'{tmp_path}/out/') with zipfile.ZipFile(out_filename, 'r') as zip_file: zip_file.extractall(out_path) expected_path = make_directory(tmp_path, 'expected', ['file1.txt']) # Assert. self.assertSetEqual(get_directory_contents(out_path), get_directory_contents(expected_path)) def test_wrong_input_syntax_raises_error(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. bad_inputs = [ '', # Empty input f'{tmp_dir}/ /', # No delimiter f'{tmp_dir}/ {DELIMITER} ', # No zip destination f'{tmp_dir} /', # No source f'{tmp_dir}/', # No delimiter or zip destination f'{DELIMITER}', # No source or zip destination f'{tmp_dir} {DELIMITER} /', # No trailing source '/' f'{tmp_dir}/ {DELIMITER} foo/', # No leading zip root '/' f'{tmp_dir}/ {DELIMITER} /foo', # No trailing zip dest '/' f'{tmp_dir}/ {DELIMITER} /{tmp_dir}/ ' f'{DELIMITER} /{tmp_dir}/', # Too many paths on split ] out_filename = f'{tmp_dir}/out.zip' # Act & Assert. for bad_input in bad_inputs: with self.assertRaises(ZipError): zip_up([bad_input], out_filename) def test_nonexistant_file_raises_error(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. input_list = [f'{tmp_dir}/nonexistant-file.txt > /'] out_filename = f'{tmp_dir}/out.zip' # Act & Assert. with self.assertRaises(ZipError): zip_up(input_list, out_filename) def test_nonexistant_dir_raises_error(self): with tempfile.TemporaryDirectory() as tmp_dir: # Arrange. input_list = [f'{tmp_dir}/nonexistant-dir/ > /'] out_filename = f'{tmp_dir}/out.zip' # Act & Assert. with self.assertRaises(ZipError): zip_up(input_list, out_filename) if __name__ == '__main__': unittest.main()