OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
M-A Ruel
2016/06/22 14:21:32
please rename it to ar_test.py
mithro
2016/06/23 07:10:55
Done.
| |
2 # Copyright 2016 The LUCI Authors. All rights reserved. | |
3 # Use of this source code is governed under the Apache License, Version 2.0 | |
4 # that can be found in the LICENSE file. | |
5 | |
6 # pylint: disable=relative-import | |
7 | |
8 import io | |
9 import os | |
10 import shutil | |
11 import subprocess | |
12 import sys | |
13 import tempfile | |
14 import unittest | |
15 | |
16 import arfile | |
17 import cli | |
18 | |
19 | |
20 ARFILE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
21 sys.path.insert(0, ARFILE_DIR) | |
22 | |
23 | |
24 if not hasattr(subprocess, 'DEVNULL'): | |
25 subprocess.DEVNULL = file(os.devnull, 'wb') | |
26 | |
27 | |
28 class ClosesSaveIOBytes(io.BytesIO): | |
29 | |
30 def close(self): | |
31 _value = self.getvalue() | |
32 self.getvalue = lambda: _value | |
33 io.BytesIO.close(self) | |
34 | |
35 | |
36 AR_TEST_SIMPLE1 = ( | |
37 # ar file header | |
38 '!<arch>\n' | |
39 # File 1 | |
40 # ---------------------- | |
41 # (16 bytes) simple file | |
42 'filename1 ' | |
43 # (12 bytes) modification time | |
44 '123 ' | |
45 # (6 bytes) user id | |
46 '1000 ' | |
47 # (6 bytes) group id | |
48 '1000 ' | |
49 # (8 bytes) file mode | |
50 '100640 ' | |
51 # (10 bytes) data size | |
52 '6 ' | |
53 # (2 bytes) file magic | |
54 '\x60\n' | |
55 # File data | |
56 'abc123' | |
57 # Finished | |
58 '') | |
59 | |
60 AR_TEST_SIMPLE_UTF = ( | |
61 # ar file header | |
62 '!<arch>\n' | |
63 # File 1 | |
64 # ---------------------- | |
65 # (16 bytes) simple file | |
66 '\xe2\x98\x83 ' | |
67 # (12 bytes) modification time | |
68 '123 ' | |
69 # (6 bytes) user id | |
70 '1000 ' | |
71 # (6 bytes) group id | |
72 '1000 ' | |
73 # (8 bytes) file mode | |
74 '100640 ' | |
75 # (10 bytes) data size | |
76 '4 ' | |
77 # (2 bytes) file magic | |
78 '\x60\n' | |
79 # (4 bytes) File data | |
80 '\xf0\x9f\x92\xa9' | |
81 # Finished | |
82 '') | |
83 | |
84 AR_TEST_BSD1 = ( | |
85 # ar file header | |
86 '!<arch>\n' | |
87 # File 1 | |
88 # ---------------------- | |
89 # (16 bytes) BSD style filename length | |
90 '#1/9 ' | |
91 # (12 bytes) modification time | |
92 '1234 ' | |
93 # (6 bytes) user id | |
94 '1001 ' | |
95 # (6 bytes) group id | |
96 '1001 ' | |
97 # (8 bytes) file mode | |
98 '100644 ' | |
99 # (10 bytes) data size | |
100 '15 ' | |
101 # (2 bytes) file magic | |
102 '\x60\n' | |
103 # BSD style filename | |
104 'filename1' | |
105 # File data | |
106 'abc123' | |
107 # Padding | |
108 '\n' | |
109 # Finished | |
110 '') | |
111 | |
112 AR_TEST_BSD2 = ( | |
113 # ar file header | |
114 '!<arch>\n' | |
115 | |
116 # File 1 | |
117 # ---------------------- | |
118 # (16 bytes) filename len | |
119 '#1/5 ' | |
120 # (12 bytes) mtime | |
121 '1447140471 ' | |
122 # (6 bytes) owner id | |
123 '1000 ' | |
124 # (6 bytes) group id | |
125 '1000 ' | |
126 # (8 bytes) file mode | |
127 '100640 ' | |
128 # (10 bytes) Data size | |
129 '13 ' | |
130 # (2 bytes) File magic | |
131 '\x60\n' | |
132 # (9 bytes) File name | |
133 'file1' | |
134 # (6 bytes) File data | |
135 'contents' | |
136 # (1 byte) Padding | |
137 '\n' | |
138 | |
139 # File 2 | |
140 # ---------------------- | |
141 # (16 bytes) filename len | |
142 '#1/7 ' | |
143 # (12 bytes) mtime | |
144 '1447140471 ' | |
145 # (6 bytes) owner id | |
146 '1000 ' | |
147 # (6 bytes) group id | |
148 '1000 ' | |
149 # (8 bytes) file mode | |
150 '100640 ' | |
151 # (10 bytes) Data size | |
152 '10 ' | |
153 # (2 bytes) File magic | |
154 '\x60\n' | |
155 # (9 bytes) File name | |
156 'fileabc' | |
157 # (6 bytes) File data | |
158 '123' | |
159 # (0 byte) No padding | |
160 '' | |
161 | |
162 # File 3 | |
163 # ---------------------- | |
164 # (16 bytes) filename len | |
165 '#1/10 ' | |
166 # (12 bytes) mtime | |
167 '1447140471 ' | |
168 # (6 bytes) owner id | |
169 '1000 ' | |
170 # (6 bytes) group id | |
171 '1000 ' | |
172 # (8 bytes) file mode | |
173 '100640 ' | |
174 # (10 bytes) Data size | |
175 '16 ' | |
176 # (2 bytes) File magic | |
177 '\x60\n' | |
178 # (9 bytes) File name | |
179 'dir1/file1' | |
180 # (6 bytes) File data | |
181 '123abc' | |
182 # (0 byte) No padding | |
183 '' | |
184 | |
185 # Finished | |
186 '') | |
187 | |
188 AR_TEST_BSD_UTF = ( | |
189 # ar file header | |
190 '!<arch>\n' | |
191 # File 1 | |
192 # ---------------------- | |
193 # (16 bytes) BSD style filename length | |
194 '#1/3 ' | |
195 # (12 bytes) modification time | |
196 '1234 ' | |
197 # (6 bytes) user id | |
198 '1001 ' | |
199 # (6 bytes) group id | |
200 '1001 ' | |
201 # (8 bytes) file mode | |
202 '100644 ' | |
203 # (10 bytes) data size | |
204 '7 ' | |
205 # (2 bytes) file magic | |
206 '\x60\n' | |
207 # (3 bytes) BSD style filename | |
208 '\xe2\x98\x83' | |
209 # (4 bytes) File data | |
210 '\xf0\x9f\x92\xa9' | |
211 # Padding | |
212 '\n' | |
213 # Finished | |
214 '') | |
215 | |
216 | |
217 class TestArFileReader(unittest.TestCase): | |
218 | |
219 def testSimple1(self): | |
220 fileobj = io.BytesIO(AR_TEST_SIMPLE1) | |
221 | |
222 afri = iter(arfile.ArFileReader(fileobj)) | |
223 ai, af = afri.next() | |
224 self.assertIs(arfile.AR_FORMAT_SIMPLE, ai.format) | |
225 self.assertEqual('filename1', ai.name) | |
226 self.assertEqual(6, ai.size) | |
227 self.assertEqual(123, ai.mtime) | |
228 self.assertEqual(1000, ai.uid) | |
229 self.assertEqual(1000, ai.gid) | |
230 self.assertEqual('0100640', oct(ai.mode)) | |
231 self.assertEqual('abc123', af.read(ai.size)) | |
232 | |
233 def testSimpleUTF(self): | |
234 fileobj = io.BytesIO(AR_TEST_SIMPLE_UTF) | |
235 | |
236 afri = iter(arfile.ArFileReader(fileobj)) | |
237 ai, af = afri.next() | |
238 self.assertIs(arfile.AR_FORMAT_SIMPLE, ai.format) | |
239 self.assertEqual(u'\u2603', ai.name) | |
240 self.assertEqual(4, ai.size) | |
241 self.assertEqual(123, ai.mtime) | |
242 self.assertEqual(1000, ai.uid) | |
243 self.assertEqual(1000, ai.gid) | |
244 self.assertEqual('0100640', oct(ai.mode)) | |
245 self.assertEqual(u'\U0001f4a9', af.read(ai.size).decode('utf-8')) | |
246 | |
247 def testBSD1(self): | |
248 fileobj = io.BytesIO(AR_TEST_BSD1) | |
249 | |
250 afri = iter(arfile.ArFileReader(fileobj)) | |
251 ai, af = afri.next() | |
252 self.assertIs(arfile.AR_FORMAT_BSD, ai.format) | |
253 self.assertEqual('filename1', ai.name) | |
254 self.assertEqual(6, ai.size) | |
255 self.assertEqual(1234, ai.mtime) | |
256 self.assertEqual(1001, ai.uid) | |
257 self.assertEqual(1001, ai.gid) | |
258 self.assertEqual('0100644', oct(ai.mode)) | |
259 self.assertEqual('abc123', af.read(ai.size)) | |
260 | |
261 def testBSD2(self): | |
262 fileobj = io.BytesIO(AR_TEST_BSD2) | |
263 | |
264 afri = iter(arfile.ArFileReader(fileobj)) | |
265 ai, af = afri.next() | |
266 self.assertIs(arfile.AR_FORMAT_BSD, ai.format) | |
267 self.assertEqual('file1', ai.name) | |
268 self.assertEqual(8, ai.size) | |
269 self.assertEqual(1447140471, ai.mtime) | |
270 self.assertEqual(1000, ai.uid) | |
271 self.assertEqual(1000, ai.gid) | |
272 self.assertEqual('0100640', oct(ai.mode)) | |
273 self.assertEqual('contents', af.read(ai.size)) | |
274 | |
275 ai, af = afri.next() | |
276 self.assertIs(arfile.AR_FORMAT_BSD, ai.format) | |
277 self.assertEqual('fileabc', ai.name) | |
278 self.assertEqual(3, ai.size) | |
279 self.assertEqual(1447140471, ai.mtime) | |
280 self.assertEqual(1000, ai.uid) | |
281 self.assertEqual(1000, ai.gid) | |
282 self.assertEqual('0100640', oct(ai.mode)) | |
283 self.assertEqual('123', af.read(ai.size)) | |
284 | |
285 ai, af = afri.next() | |
286 self.assertIs(arfile.AR_FORMAT_BSD, ai.format) | |
287 self.assertEqual('dir1/file1', ai.name) | |
288 self.assertEqual(6, ai.size) | |
289 self.assertEqual(1447140471, ai.mtime) | |
290 self.assertEqual(1000, ai.uid) | |
291 self.assertEqual(1000, ai.gid) | |
292 self.assertEqual('0100640', oct(ai.mode)) | |
293 self.assertEqual('123abc', af.read(ai.size)) | |
294 | |
295 def testBSDUTF(self): | |
296 fileobj = io.BytesIO(AR_TEST_BSD_UTF) | |
297 | |
298 afri = iter(arfile.ArFileReader(fileobj)) | |
299 ai, af = afri.next() | |
300 self.assertIs(arfile.AR_FORMAT_BSD, ai.format) | |
301 self.assertEqual(u'\u2603', ai.name) | |
302 self.assertEqual(4, ai.size) | |
303 self.assertEqual(1234, ai.mtime) | |
304 self.assertEqual(1001, ai.uid) | |
305 self.assertEqual(1001, ai.gid) | |
306 self.assertEqual('0100644', oct(ai.mode)) | |
307 self.assertEqual(u'\U0001f4a9', af.read(ai.size).decode('utf-8')) | |
308 | |
309 | |
310 class TestArFileWriter(unittest.TestCase): | |
311 | |
312 def testSimple1(self): | |
313 fileobj = ClosesSaveIOBytes() | |
314 | |
315 afw = arfile.ArFileWriter(fileobj) | |
316 ai = arfile.ArInfo( | |
317 arfile.AR_FORMAT_SIMPLE, 'filename1', 6, 123, 1000, 1000, 0100640) | |
318 afw.addfile(ai, io.BytesIO('abc123')) | |
319 afw.close() | |
320 | |
321 self.assertMultiLineEqual(AR_TEST_SIMPLE1, fileobj.getvalue()) | |
322 | |
323 def testSimpleUTF(self): | |
324 fileobj = ClosesSaveIOBytes() | |
325 | |
326 afw = arfile.ArFileWriter(fileobj) | |
327 ai = arfile.ArInfo( | |
328 arfile.AR_FORMAT_SIMPLE, u'\u2603', 4, 123, 1000, 1000, 0100640) | |
329 afw.addfile(ai, io.BytesIO(u'\U0001f4a9'.encode('utf-8'))) | |
330 afw.close() | |
331 | |
332 self.assertMultiLineEqual(AR_TEST_SIMPLE_UTF, fileobj.getvalue()) | |
333 | |
334 def testBSD1(self): | |
335 fileobj = ClosesSaveIOBytes() | |
336 | |
337 afw = arfile.ArFileWriter(fileobj) | |
338 ai = arfile.ArInfo( | |
339 arfile.AR_FORMAT_BSD, 'filename1', 6, 1234, 1001, 1001, 0100644) | |
340 afw.addfile(ai, io.BytesIO('abc123')) | |
341 afw.close() | |
342 | |
343 self.assertMultiLineEqual(AR_TEST_BSD1, fileobj.getvalue()) | |
344 | |
345 def testBSD2(self): | |
346 fileobj = ClosesSaveIOBytes() | |
347 | |
348 afw = arfile.ArFileWriter(fileobj) | |
349 afw.addfile( | |
350 arfile.ArInfo.fromdefault( | |
351 'file1', 8, arformat=arfile.AR_FORMAT_BSD), | |
352 io.BytesIO('contents')) | |
353 afw.addfile( | |
354 arfile.ArInfo.fromdefault( | |
355 'fileabc', 3, arformat=arfile.AR_FORMAT_BSD), | |
356 io.BytesIO('123')) | |
357 afw.addfile( | |
358 arfile.ArInfo.fromdefault( | |
359 'dir1/file1', 6, arformat=arfile.AR_FORMAT_BSD), | |
360 io.BytesIO('123abc')) | |
361 afw.close() | |
362 | |
363 self.assertMultiLineEqual(AR_TEST_BSD2, fileobj.getvalue()) | |
364 | |
365 def testBSDUTF(self): | |
366 fileobj = ClosesSaveIOBytes() | |
367 | |
368 afw = arfile.ArFileWriter(fileobj) | |
369 ai = arfile.ArInfo( | |
370 arfile.AR_FORMAT_BSD, u'\u2603', 4, 1234, 1001, 1001, 0100644) | |
371 afw.addfile(ai, io.BytesIO(u'\U0001f4a9'.encode('utf-8'))) | |
372 afw.close() | |
373 | |
374 self.assertMultiLineEqual(AR_TEST_BSD_UTF, fileobj.getvalue()) | |
375 | |
376 | |
377 class BaseTestSuite(object): | |
378 | |
379 def testSimple1(self): | |
380 self.assertWorking( | |
381 ( | |
382 arfile.ArInfo( | |
383 arfile.AR_FORMAT_SIMPLE, 'filename1', | |
384 6, 123, 1000, 1000, 0100640), | |
385 'abc123')) | |
386 | |
387 def testSimpleUTF(self): | |
388 self.assertWorking( | |
389 ( | |
390 arfile.ArInfo( | |
391 arfile.AR_FORMAT_SIMPLE, u'\u2603', | |
392 4, 123, 1000, 1000, 0100640), | |
393 u'\U0001f4a9'.encode('utf-8'))) | |
394 | |
395 def testBSD1(self): | |
396 self.assertWorking( | |
397 ( | |
398 arfile.ArInfo( | |
399 arfile.AR_FORMAT_BSD, 'filename1', | |
400 6, 123, 1000, 1000, 0100640), | |
401 'abc123')) | |
402 | |
403 def testBSD2(self): | |
404 self.assertWorking( | |
405 ( | |
406 arfile.ArInfo.fromdefault( | |
407 'file1', 8, arformat=arfile.AR_FORMAT_BSD), | |
408 'contents'), | |
409 ( | |
410 arfile.ArInfo.fromdefault( | |
411 'fileabc', 3, arformat=arfile.AR_FORMAT_BSD), | |
412 '123'), | |
413 ( | |
414 arfile.ArInfo.fromdefault( | |
415 'dir1/file1', 6, arformat=arfile.AR_FORMAT_BSD), | |
416 '123abc')) | |
417 | |
418 def testBSDUTF(self): | |
419 self.assertWorking( | |
420 ( | |
421 arfile.ArInfo( | |
422 arfile.AR_FORMAT_BSD, u'\u2603', | |
423 4, 123, 1000, 1000, 0100640), | |
424 u'\U0001f4a9'.encode('utf-8'))) | |
425 | |
426 def testMixed(self): | |
427 self.assertWorking( | |
428 (arfile.ArInfo.fromdefault('file1', 0), ''), | |
429 (arfile.ArInfo.fromdefault('f f', 1), 'a'), | |
430 (arfile.ArInfo.fromdefault('123456789abcedefa', 1), 'a')) | |
431 | |
432 | |
433 class TestArRoundTrip(BaseTestSuite, unittest.TestCase): | |
434 | |
435 def assertWorking(self, *initems): | |
436 outfile = ClosesSaveIOBytes() | |
437 | |
438 afw = arfile.ArFileWriter(outfile) | |
439 for ai, data in initems: | |
440 assert ai.size == len(data) | |
441 afw.addfile(ai, io.BytesIO(data)) | |
442 afw.close() | |
443 | |
444 infile = io.BytesIO(outfile.getvalue()) | |
445 afr = arfile.ArFileReader(infile) | |
446 | |
447 outitems = [] | |
448 for ai, fd in afr: | |
449 data = fd.read(ai.size) | |
450 outitems.append((ai, data)) | |
451 | |
452 self.assertSequenceEqual(initems, outitems) | |
453 | |
454 | |
455 def system_has_ar(): | |
456 retcode = subprocess.call( | |
457 'ar', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
458 return retcode == 1 | |
459 | |
460 | |
461 @unittest.skipIf(not system_has_ar(), 'no ar binary found.') | |
462 class TestArExternal(BaseTestSuite, unittest.TestCase): | |
463 | |
464 def assertWorking(self, *initems): | |
465 tf = tempfile.NamedTemporaryFile(mode='wb') | |
466 afw = arfile.ArFileWriter(tf) | |
467 | |
468 files = [] | |
469 for ai, data in initems: | |
470 files.append(ai.name) | |
471 assert ai.size == len(data) | |
472 afw.addfile(ai, io.BytesIO(data)) | |
473 afw.flush() | |
474 | |
475 output = subprocess.check_output(['ar', 't', tf.name]) | |
476 self.assertMultiLineEqual('\n'.join(files), output.decode('utf-8').strip()) | |
477 tf.close() | |
478 | |
479 | |
480 class TestCLI(unittest.TestCase): | |
481 | |
482 def runCLI(self, args): | |
483 orig_stdout = sys.stdout | |
484 orig_stderr = sys.stderr | |
485 try: | |
486 sys.stdout = io.StringIO() | |
487 sys.stderr = io.StringIO() | |
488 cli.main('artool', args) | |
489 return sys.stdout.getvalue(), sys.stderr.getvalue() | |
490 finally: | |
491 sys.stdout = orig_stdout | |
492 sys.stderr = orig_stderr | |
493 | |
494 def assertCLI(self, *initems): | |
495 indir = None | |
496 ardir = None | |
497 outdir = None | |
498 try: | |
499 indir = tempfile.mkdtemp().decode(sys.getfilesystemencoding()) | |
500 ardir = tempfile.mkdtemp().decode(sys.getfilesystemencoding()) | |
501 outdir = tempfile.mkdtemp().decode(sys.getfilesystemencoding()) | |
502 | |
503 arp = os.path.join(ardir, 'out.ar') | |
504 assert not os.path.exists(arp) | |
505 | |
506 # Write out a directory tree | |
507 files = [] | |
508 for fp, contents in initems: | |
509 fn = os.path.join(indir, fp) | |
510 dn = os.path.dirname(fn) | |
511 if not os.path.exists(dn): | |
512 os.makedirs(dn) | |
513 | |
514 with file(fn, 'wb') as f: | |
515 f.write(contents) | |
516 | |
517 files.append(fp) | |
518 | |
519 files.sort() | |
520 fileslist = '\n'.join(files) | |
521 | |
522 # Create an archive from a directory | |
523 self.runCLI(['create', '--filename', arp, indir]) | |
524 self.assertTrue( | |
525 os.path.exists(arp), '%s file should exists' % arp) | |
526 | |
527 # List the archive contents | |
528 output, _ = self.runCLI(['list', '--filename', arp]) | |
529 filesoutput = '\n'.join(sorted(output[:-1].split('\n'))) | |
530 self.assertMultiLineEqual(fileslist, filesoutput) | |
531 | |
532 # Extract the archive | |
533 os.chdir(outdir) | |
534 self.runCLI(['extract', '--filename', arp]) | |
535 | |
536 # Walk the directory tree and collect the extracted output | |
537 outitems = [] | |
538 for root, _, files in os.walk(outdir): | |
539 for fn in files: | |
540 fp = os.path.join(root, fn) | |
541 outitems.append([fp[len(outdir)+1:], file(fp, 'rb').read()]) | |
542 | |
543 # Check the two are equal | |
544 self.assertSequenceEqual(sorted(initems), sorted(outitems)) | |
545 | |
546 finally: | |
547 if indir: | |
548 shutil.rmtree(indir, ignore_errors=True) | |
549 if ardir: | |
550 shutil.rmtree(ardir, ignore_errors=True) | |
551 if outdir: | |
552 shutil.rmtree(outdir, ignore_errors=True) | |
553 | |
554 def testSimple1(self): | |
555 self.assertCLI( | |
M-A Ruel
2016/06/22 14:21:32
please move these that fits 80 cols as one line, n
mithro
2016/06/23 07:10:54
Done.
| |
556 ['file1', 'contents1'], | |
557 ) | |
558 | |
559 def testMultiple(self): | |
560 self.assertCLI( | |
561 ['file1', 'contents1'], | |
562 ['dir1/file2', 'contents2'], | |
563 ['dir2/dir3/file3', 'contents3'], | |
564 ['file4', 'contents4'], | |
565 ) | |
566 | |
567 def testUnicodeContents(self): | |
568 self.assertCLI( | |
569 ['file1', u'\u2603'.encode('utf-8')], | |
570 ) | |
571 | |
572 def testFilenameSpaces(self): | |
573 self.assertCLI( | |
574 ['f f1', 'contents1'], | |
575 ['d d1/file2', 'contents2'], | |
576 ['d d1/f f3', 'contents3'], | |
577 ['file4', 'contents4'], | |
578 ) | |
579 | |
580 def testBigFile(self): | |
581 self.assertCLI( | |
582 ['bigfile', 'data'*1024*1024*10], | |
583 ) | |
584 | |
585 def testUnicode(self): | |
586 self.assertCLI( | |
587 [u'\u2603', u'\U0001f4a9'.encode('utf-8')], | |
588 ) | |
589 | |
590 if __name__ == '__main__': | |
591 import doctest | |
M-A Ruel
2016/06/22 14:21:32
please import at file level at the top
mithro
2016/06/23 07:10:54
Done.
| |
592 doctest.testmod(arfile) | |
593 unittest.main() | |
OLD | NEW |