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