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

Side by Side Diff: client/libs/arfile/tests.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 Marc's 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
« client/libs/arfile/cli.py ('K') | « client/libs/arfile/cli.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The LUCI Authors. All rights reserved.
2 # Use of this source code is governed under the Apache License, Version 2.0
3 # that can be found in the LICENSE file.
4
5 import StringIO as SlowStringIO
6 import cStringIO as StringIO
7 import os
8 import shutil
9 import subprocess
10 import sys
11 import tempfile
12 import unittest
13
14 import arfile
15 import cli
16
17 if not hasattr(subprocess, 'DEVNULL'):
18 subprocess.DEVNULL = file(os.devnull, 'wb')
19
20
21 class CloseSaveStringIO(SlowStringIO.StringIO):
22 def close(self):
23 _value = self.getvalue()
24 self.getvalue = lambda: _value
25 SlowStringIO.StringIO.close(self)
26
27
28 AR_TEST_SIMPLE1 = (
29 # ar file header
30 '!<arch>\n'
31 # File 1
32 # ----------------------
33 # (16 bytes) simple file
34 'filename1 '
35 # (12 bytes) modification time
36 '123 '
37 # (6 bytes) user id
38 '1000 '
39 # (6 bytes) group id
40 '1000 '
41 # (8 bytes) file mode
42 '100640 '
43 # (10 bytes) data size
44 '6 '
45 # (2 bytes) file magic
46 '\x60\n'
47 # File data
48 'abc123'
49 # Finished
50 '')
51
52 AR_TEST_BSD1 = (
53 # ar file header
54 '!<arch>\n'
55 # File 1
56 # ----------------------
57 # (16 bytes) BSD style filename length
58 '#1/9 '
59 # (12 bytes) modification time
60 '1234 '
61 # (6 bytes) user id
62 '1001 '
63 # (6 bytes) group id
64 '1001 '
65 # (8 bytes) file mode
66 '100644 '
67 # (10 bytes) data size
68 '15 '
69 # (2 bytes) file magic
70 '\x60\n'
71 # BSD style filename
72 'filename1'
73 # File data
74 'abc123'
75 # Padding
76 '\n'
77 # Finished
78 '')
79
80 AR_TEST_BSD2 = (
81 # ar file header
82 '!<arch>\n'
83
84 # File 1
85 # ----------------------
86 # (16 bytes) filename len
87 '#1/5 '
88 # (12 bytes) mtime
89 '1447140471 '
90 # (6 bytes) owner id
91 '1000 '
92 # (6 bytes) group id
93 '1000 '
94 # (8 bytes) file mode
95 '100640 '
96 # (10 bytes) Data size
97 '13 '
98 # (2 bytes) File magic
99 '\x60\n'
100 # (9 bytes) File name
101 'file1'
102 # (6 bytes) File data
103 'contents'
104 # (1 byte) Padding
105 '\n'
106
107 # File 2
108 # ----------------------
109 # (16 bytes) filename len
110 '#1/7 '
111 # (12 bytes) mtime
112 '1447140471 '
113 # (6 bytes) owner id
114 '1000 '
115 # (6 bytes) group id
116 '1000 '
117 # (8 bytes) file mode
118 '100640 '
119 # (10 bytes) Data size
120 '10 '
121 # (2 bytes) File magic
122 '\x60\n'
123 # (9 bytes) File name
124 'fileabc'
125 # (6 bytes) File data
126 '123'
127 # (0 byte) No padding
128 ''
129
130 # File 3
131 # ----------------------
132 # (16 bytes) filename len
133 '#1/10 '
134 # (12 bytes) mtime
135 '1447140471 '
136 # (6 bytes) owner id
137 '1000 '
138 # (6 bytes) group id
139 '1000 '
140 # (8 bytes) file mode
141 '100640 '
142 # (10 bytes) Data size
143 '16 '
144 # (2 bytes) File magic
145 '\x60\n'
146 # (9 bytes) File name
147 'dir1/file1'
148 # (6 bytes) File data
149 '123abc'
150 # (0 byte) No padding
151 ''
152
153 # Finished
154 '')
155
156
157 class TestArFileReader(unittest.TestCase):
158
159 def testSimple1(self):
160 fileobj = StringIO.StringIO(AR_TEST_SIMPLE1)
161
162 afri = iter(arfile.ArFileReader(fileobj))
163 ai, af = afri.next()
164 self.assertIs(arfile.AR_FORMAT_SIMPLE, ai.format)
165 self.assertEqual('filename1', ai.name)
166 self.assertEqual(6, ai.size)
167 self.assertEqual(123, ai.mtime)
168 self.assertEqual(1000, ai.uid)
169 self.assertEqual(1000, ai.gid)
170 self.assertEqual('0100640', oct(ai.mode))
171 self.assertEqual('abc123', af.read(ai.size))
172
173 def testBSD1(self):
174 fileobj = StringIO.StringIO(AR_TEST_BSD1)
175
176 afri = iter(arfile.ArFileReader(fileobj))
177 ai, af = afri.next()
178 self.assertIs(arfile.AR_FORMAT_BSD, ai.format)
179 self.assertEqual('filename1', ai.name)
180 self.assertEqual(6, ai.size)
181 self.assertEqual(1234, ai.mtime)
182 self.assertEqual(1001, ai.uid)
183 self.assertEqual(1001, ai.gid)
184 self.assertEqual('0100644', oct(ai.mode))
185 self.assertEqual('abc123', af.read(ai.size))
186
187 def testBSD2(self):
188 fileobj = StringIO.StringIO(AR_TEST_BSD2)
189
190 afri = iter(arfile.ArFileReader(fileobj))
191 ai, af = afri.next()
192 self.assertIs(arfile.AR_FORMAT_BSD, ai.format)
193 self.assertEqual('file1', ai.name)
194 self.assertEqual(8, ai.size)
195 self.assertEqual(1447140471, ai.mtime)
196 self.assertEqual(1000, ai.uid)
197 self.assertEqual(1000, ai.gid)
198 self.assertEqual('0100640', oct(ai.mode))
199 self.assertEqual('contents', af.read(ai.size))
200
201 ai, af = afri.next()
202 self.assertIs(arfile.AR_FORMAT_BSD, ai.format)
203 self.assertEqual('fileabc', ai.name)
204 self.assertEqual(3, ai.size)
205 self.assertEqual(1447140471, ai.mtime)
206 self.assertEqual(1000, ai.uid)
207 self.assertEqual(1000, ai.gid)
208 self.assertEqual('0100640', oct(ai.mode))
209 self.assertEqual('123', af.read(ai.size))
210
211 ai, af = afri.next()
212 self.assertIs(arfile.AR_FORMAT_BSD, ai.format)
213 self.assertEqual('dir1/file1', ai.name)
214 self.assertEqual(6, ai.size)
215 self.assertEqual(1447140471, ai.mtime)
216 self.assertEqual(1000, ai.uid)
217 self.assertEqual(1000, ai.gid)
218 self.assertEqual('0100640', oct(ai.mode))
219 self.assertEqual('123abc', af.read(ai.size))
220
221
222 class TestArFileWriter(unittest.TestCase):
223
224 def testSimple1(self):
225 fileobj = CloseSaveStringIO()
226
227 afw = arfile.ArFileWriter(fileobj)
228 ai = arfile.ArInfo(
229 arfile.AR_FORMAT_SIMPLE, 'filename1', 6, 123, 1000, 1000, 0100640)
230 afw.addfile(ai, StringIO.StringIO('abc123'))
231 afw.close()
232
233 self.assertMultiLineEqual(AR_TEST_SIMPLE1, fileobj.getvalue())
234
235 def testBSD1(self):
236 fileobj = CloseSaveStringIO()
237
238 afw = arfile.ArFileWriter(fileobj)
239 ai = arfile.ArInfo(
240 arfile.AR_FORMAT_BSD, 'filename1', 6, 1234, 1001, 1001, 0100644)
241 afw.addfile(ai, StringIO.StringIO('abc123'))
242 afw.close()
243
244 self.assertMultiLineEqual(AR_TEST_BSD1, fileobj.getvalue())
245
246 def testBSD2(self):
247 fileobj = CloseSaveStringIO()
248
249 afw = arfile.ArFileWriter(fileobj)
250 afw.addfile(
251 arfile.ArInfo.fromdefault(
252 'file1', 8, arformat=arfile.AR_FORMAT_BSD),
253 StringIO.StringIO('contents'))
254 afw.addfile(
255 arfile.ArInfo.fromdefault(
256 'fileabc', 3, arformat=arfile.AR_FORMAT_BSD),
257 StringIO.StringIO('123'))
258 afw.addfile(
259 arfile.ArInfo.fromdefault(
260 'dir1/file1', 6, arformat=arfile.AR_FORMAT_BSD),
261 StringIO.StringIO('123abc'))
262 afw.close()
263
264 self.assertMultiLineEqual(AR_TEST_BSD2, fileobj.getvalue())
265
M-A Ruel 2016/06/17 13:26:49 consistent spacing
266
267
268 class BaseTestSuite(object):
269
270 def testSimple1(self):
271 self.assertWorking(
272 (
273 arfile.ArInfo(
274 arfile.AR_FORMAT_SIMPLE, 'filename1',
275 6, 123, 1000, 1000, 0100640),
276 'abc123'))
277
278 def testBSD1(self):
279 self.assertWorking(
280 (
281 arfile.ArInfo(
282 arfile.AR_FORMAT_BSD, 'filename1',
283 6, 123, 1000, 1000, 0100640),
284 'abc123'))
285
286 def testBSD2(self):
287 self.assertWorking(
288 (
289 arfile.ArInfo.fromdefault(
290 'file1', 8, arformat=arfile.AR_FORMAT_BSD),
291 'contents'),
292 (
293 arfile.ArInfo.fromdefault(
294 'fileabc', 3, arformat=arfile.AR_FORMAT_BSD),
295 '123'),
296 (
297 arfile.ArInfo.fromdefault(
298 'dir1/file1', 6, arformat=arfile.AR_FORMAT_BSD),
299 '123abc'))
300
301 def testMixed(self):
302 self.assertWorking(
303 (arfile.ArInfo.fromdefault('file1', 0), ''),
304 (arfile.ArInfo.fromdefault('f f', 1), 'a'),
305 (arfile.ArInfo.fromdefault('123456789abcedefa', 1), 'a'))
306
307
308 class TestArRoundTrip(BaseTestSuite, unittest.TestCase):
309
310 def assertWorking(self, *initems):
311 outfile = CloseSaveStringIO()
312
313 afw = arfile.ArFileWriter(outfile)
314 for ai, data in initems:
315 assert ai.size == len(data)
316 afw.addfile(ai, StringIO.StringIO(data))
317 afw.close()
318
319 infile = StringIO.StringIO(outfile.getvalue())
320 afr = arfile.ArFileReader(infile)
321
322 outitems = []
323 for ai, fd in afr:
324 data = fd.read(ai.size)
325 outitems.append((ai, data))
326
327 self.assertSequenceEqual(initems, outitems)
328
329
330 def system_has_ar():
331 retcode = subprocess.call(
332 'ar', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
333 return retcode == 1
334
335
336 @unittest.skipIf(not system_has_ar(), 'no ar binary found.')
337 class TestArExternal(BaseTestSuite, unittest.TestCase):
338
339 def assertWorking(self, *initems):
340 tf = tempfile.NamedTemporaryFile(mode='wb')
341 afw = arfile.ArFileWriter(tf)
342
343 files = []
344 for ai, data in initems:
345 files.append(ai.name)
346 assert ai.size == len(data)
347 afw.addfile(ai, StringIO.StringIO(data))
348 afw.flush()
349
350 output = subprocess.check_output(['ar', 't', tf.name])
351 self.assertMultiLineEqual('\n'.join(files), output.strip())
352 tf.close()
353
354
355 class TestCLI(unittest.TestCase):
356
357 def runCLI(self, args):
358 orig_stdout = sys.stdout
359 orig_stderr = sys.stderr
360 try:
361 sys.stdout = StringIO.StringIO()
362 sys.stderr = StringIO.StringIO()
363 cli.main('artool', args)
364 return sys.stdout.getvalue(), sys.stderr.getvalue()
365 finally:
366 sys.stdout = orig_stdout
367 sys.stderr = orig_stderr
368
369 def assertCLI(self, *initems):
370 indir = None
371 ardir = None
372 outdir = None
373 try:
374 indir = tempfile.mkdtemp()
375 ardir = tempfile.mkdtemp()
376 outdir = tempfile.mkdtemp()
377
378 arp = os.path.join(ardir, 'out.ar')
379 assert not os.path.exists(arp)
380
381 # Write out a directory tree
382 files = []
383 for fp, contents in initems:
384 fn = os.path.join(indir, fp)
385 dn = os.path.dirname(fn)
386 if not os.path.exists(dn):
387 os.makedirs(dn)
388
389 with file(fn, 'wb') as f:
390 f.write(contents)
391
392 files.append(fp)
393
394 files.sort()
395 fileslist = '\n'.join(files)
396
397 # Create an archive from a directory
398 self.runCLI(['create', '--filename', arp, indir])
399 self.assertTrue(
400 os.path.exists(arp), '%s file should exists' % arp)
401
402 # List the archive contents
403 output, _ = self.runCLI(['list', '--filename', arp])
404 filesoutput = '\n'.join(sorted(output[:-1].split('\n')))
405 self.assertMultiLineEqual(fileslist, filesoutput)
406
407 # Extract the archive
408 os.chdir(outdir)
409 self.runCLI(['extract', '--filename', arp])
410
411 # Walk the directory tree and collect the extracted output
412 outitems = []
413 for root, _, files in os.walk(outdir):
414 for fn in files:
415 fp = os.path.join(root, fn)
416 outitems.append([fp[len(outdir)+1:], file(fp, 'rb').read()])
417
418 # Check the two are equal
419 self.assertSequenceEqual(sorted(initems), sorted(outitems))
420
421 finally:
422 if indir:
423 shutil.rmtree(indir, ignore_errors=True)
424 if ardir:
425 shutil.rmtree(ardir, ignore_errors=True)
426 if outdir:
427 shutil.rmtree(outdir, ignore_errors=True)
428
429
430 def testSimple1(self):
431 self.assertCLI(
432 ['file1', 'contents1'],
433 )
434
435 def testMultiple(self):
436 self.assertCLI(
437 ['file1', 'contents1'],
438 ['dir1/file2', 'contents2'],
439 ['dir2/dir3/file3', 'contents3'],
440 ['file4', 'contents4'],
441 )
442
443 def testUnicodeContents(self):
444 self.assertCLI(
445 ['file1', u'\u2603'.encode('utf-8')],
446 )
447
448 def testFilenameSpaces(self):
449 self.assertCLI(
450 ['f f1', 'contents1'],
451 ['d d1/file2', 'contents2'],
452 ['d d1/f f3', 'contents3'],
453 ['file4', 'contents4'],
454 )
455
456 def testBigFile(self):
457 self.assertCLI(
M-A Ruel 2016/06/17 13:26:49 you know this fits one line, right? :)
mithro 2016/06/22 13:05:30 Just making all the functions have the same format
458 ['bigfile', 'data'*1024*1024*10],
459 )
460
M-A Ruel 2016/06/17 13:26:49 2 empty lines consistently
mithro 2016/06/22 13:05:30 Done.
461 if __name__ == '__main__':
462 import subprocess
463 subprocess.call('python arfile.py', shell=True)
M-A Ruel 2016/06/17 13:26:49 I don't understand why this is not a test case
mithro 2016/06/22 13:05:30 This is running the doctests in the arfile.py
464 unittest.main()
OLDNEW
« client/libs/arfile/cli.py ('K') | « client/libs/arfile/cli.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698