Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Side by Side Diff: client/libs/arfile/arfile_test.py

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

Powered by Google App Engine
This is Rietveld 408576698