blob: 446b61ab439ffef7bdbdfac0a45d738dc2bfc882 [file] [log] [blame]
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +00001"""Test script for the gzip module.
2"""
3
Stéphane Wirtel84eec112018-10-09 23:16:43 +02004import array
5import functools
6import io
Christian Heimes05e8be12008-02-23 18:30:17 +00007import os
Berker Peksag03020cf2016-10-02 13:47:58 +03008import pathlib
Antoine Pitrou42db3ef2009-01-04 21:37:59 +00009import struct
Stéphane Wirtel84eec112018-10-09 23:16:43 +020010import sys
11import unittest
12from subprocess import PIPE, Popen
13from test import support
Hai Shibb0424b2020-08-04 00:47:42 +080014from test.support import import_helper
15from test.support import os_helper
Stéphane Wirtel84eec112018-10-09 23:16:43 +020016from test.support import _4G, bigmemtest
Stéphane Wirtel3e28eed2018-11-03 16:24:23 +010017from test.support.script_helper import assert_python_ok, assert_python_failure
Stéphane Wirtel84eec112018-10-09 23:16:43 +020018
Hai Shibb0424b2020-08-04 00:47:42 +080019gzip = import_helper.import_module('gzip')
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000020
Walter Dörwald5b1284d2007-06-06 16:43:59 +000021data1 = b""" int length=DEFAULTALLOC, err = Z_OK;
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000022 PyObject *RetVal;
23 int flushmode = Z_FINISH;
24 unsigned long start_total_out;
25
26"""
27
Walter Dörwald5b1284d2007-06-06 16:43:59 +000028data2 = b"""/* zlibmodule.c -- gzip-compatible data compression */
Neal Norwitz014f1032004-07-29 03:55:56 +000029/* See http://www.gzip.org/zlib/
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000030/* See http://www.winimage.com/zLibDll for Windows */
31"""
32
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000033
Hai Shibb0424b2020-08-04 00:47:42 +080034TEMPDIR = os.path.abspath(os_helper.TESTFN) + '-gzdir'
Stéphane Wirtel84eec112018-10-09 23:16:43 +020035
36
Antoine Pitrou7b969842010-09-23 16:22:51 +000037class UnseekableIO(io.BytesIO):
38 def seekable(self):
39 return False
40
41 def tell(self):
42 raise io.UnsupportedOperation
43
44 def seek(self, *args):
45 raise io.UnsupportedOperation
46
47
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +020048class BaseTest(unittest.TestCase):
Hai Shibb0424b2020-08-04 00:47:42 +080049 filename = os_helper.TESTFN
Tim Peters5cfb05e2004-07-27 21:02:02 +000050
Georg Brandlb533e262008-05-25 18:19:30 +000051 def setUp(self):
Hai Shibb0424b2020-08-04 00:47:42 +080052 os_helper.unlink(self.filename)
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000053
Georg Brandlb533e262008-05-25 18:19:30 +000054 def tearDown(self):
Hai Shibb0424b2020-08-04 00:47:42 +080055 os_helper.unlink(self.filename)
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +000056
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +000057
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +020058class TestGzip(BaseTest):
Serhiy Storchakabca63b32015-03-23 14:59:48 +020059 def write_and_read_back(self, data, mode='b'):
60 b_data = bytes(data)
61 with gzip.GzipFile(self.filename, 'w'+mode) as f:
62 l = f.write(data)
63 self.assertEqual(l, len(b_data))
64 with gzip.GzipFile(self.filename, 'r'+mode) as f:
65 self.assertEqual(f.read(), b_data)
66
Georg Brandlb533e262008-05-25 18:19:30 +000067 def test_write(self):
Brian Curtin28f96b52010-10-13 02:21:42 +000068 with gzip.GzipFile(self.filename, 'wb') as f:
69 f.write(data1 * 50)
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +000070
Brian Curtin28f96b52010-10-13 02:21:42 +000071 # Try flush and fileno.
72 f.flush()
73 f.fileno()
74 if hasattr(os, 'fsync'):
75 os.fsync(f.fileno())
76 f.close()
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +000077
Georg Brandlb533e262008-05-25 18:19:30 +000078 # Test multiple close() calls.
79 f.close()
80
Berker Peksag03020cf2016-10-02 13:47:58 +030081 def test_write_read_with_pathlike_file(self):
82 filename = pathlib.Path(self.filename)
83 with gzip.GzipFile(filename, 'w') as f:
84 f.write(data1 * 50)
85 self.assertIsInstance(f.name, str)
86 with gzip.GzipFile(filename, 'a') as f:
87 f.write(data1)
88 with gzip.GzipFile(filename) as f:
89 d = f.read()
90 self.assertEqual(d, data1 * 51)
91 self.assertIsInstance(f.name, str)
92
Serhiy Storchakabca63b32015-03-23 14:59:48 +020093 # The following test_write_xy methods test that write accepts
94 # the corresponding bytes-like object type as input
95 # and that the data written equals bytes(xy) in all cases.
96 def test_write_memoryview(self):
97 self.write_and_read_back(memoryview(data1 * 50))
98 m = memoryview(bytes(range(256)))
99 data = m.cast('B', shape=[8,8,4])
100 self.write_and_read_back(data)
101
102 def test_write_bytearray(self):
103 self.write_and_read_back(bytearray(data1 * 50))
104
105 def test_write_array(self):
106 self.write_and_read_back(array.array('I', data1 * 40))
107
108 def test_write_incompatible_type(self):
109 # Test that non-bytes-like types raise TypeError.
110 # Issue #21560: attempts to write incompatible types
111 # should not affect the state of the fileobject
112 with gzip.GzipFile(self.filename, 'wb') as f:
113 with self.assertRaises(TypeError):
114 f.write('')
115 with self.assertRaises(TypeError):
116 f.write([])
117 f.write(data1)
118 with gzip.GzipFile(self.filename, 'rb') as f:
119 self.assertEqual(f.read(), data1)
120
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000121 def test_read(self):
122 self.test_write()
123 # Try reading.
Brian Curtin28f96b52010-10-13 02:21:42 +0000124 with gzip.GzipFile(self.filename, 'r') as f:
125 d = f.read()
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000126 self.assertEqual(d, data1*50)
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +0000127
Antoine Pitrou4ec4b0c2011-04-04 21:00:37 +0200128 def test_read1(self):
129 self.test_write()
130 blocks = []
131 nread = 0
132 with gzip.GzipFile(self.filename, 'r') as f:
133 while True:
134 d = f.read1()
135 if not d:
136 break
137 blocks.append(d)
138 nread += len(d)
139 # Check that position was updated correctly (see issue10791).
140 self.assertEqual(f.tell(), nread)
141 self.assertEqual(b''.join(blocks), data1 * 50)
142
Martin Pantere99e9772015-11-20 08:13:35 +0000143 @bigmemtest(size=_4G, memuse=1)
144 def test_read_large(self, size):
145 # Read chunk size over UINT_MAX should be supported, despite zlib's
146 # limitation per low-level call
147 compressed = gzip.compress(data1, compresslevel=1)
148 f = gzip.GzipFile(fileobj=io.BytesIO(compressed), mode='rb')
149 self.assertEqual(f.read(size), data1)
150
Antoine Pitrou7980eaa2010-10-06 21:21:18 +0000151 def test_io_on_closed_object(self):
152 # Test that I/O operations on closed GzipFile objects raise a
153 # ValueError, just like the corresponding functions on file objects.
154
155 # Write to a file, open it for reading, then close it.
156 self.test_write()
157 f = gzip.GzipFile(self.filename, 'r')
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200158 fileobj = f.fileobj
159 self.assertFalse(fileobj.closed)
Antoine Pitrou7980eaa2010-10-06 21:21:18 +0000160 f.close()
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200161 self.assertTrue(fileobj.closed)
Antoine Pitrou7980eaa2010-10-06 21:21:18 +0000162 with self.assertRaises(ValueError):
163 f.read(1)
164 with self.assertRaises(ValueError):
165 f.seek(0)
166 with self.assertRaises(ValueError):
167 f.tell()
168 # Open the file for writing, then close it.
169 f = gzip.GzipFile(self.filename, 'w')
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200170 fileobj = f.fileobj
171 self.assertFalse(fileobj.closed)
Antoine Pitrou7980eaa2010-10-06 21:21:18 +0000172 f.close()
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200173 self.assertTrue(fileobj.closed)
Antoine Pitrou7980eaa2010-10-06 21:21:18 +0000174 with self.assertRaises(ValueError):
175 f.write(b'')
176 with self.assertRaises(ValueError):
177 f.flush()
178
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000179 def test_append(self):
180 self.test_write()
181 # Append to the previous file
Brian Curtin28f96b52010-10-13 02:21:42 +0000182 with gzip.GzipFile(self.filename, 'ab') as f:
183 f.write(data2 * 15)
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +0000184
Brian Curtin28f96b52010-10-13 02:21:42 +0000185 with gzip.GzipFile(self.filename, 'rb') as f:
186 d = f.read()
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000187 self.assertEqual(d, (data1*50) + (data2*15))
Andrew M. Kuchling85ab7382000-07-29 20:18:34 +0000188
Andrew M. Kuchling01cb47b2005-06-09 14:19:32 +0000189 def test_many_append(self):
190 # Bug #1074261 was triggered when reading a file that contained
191 # many, many members. Create such a file and verify that reading it
192 # works.
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200193 with gzip.GzipFile(self.filename, 'wb', 9) as f:
Walter Dörwald5b1284d2007-06-06 16:43:59 +0000194 f.write(b'a')
Brian Curtin28f96b52010-10-13 02:21:42 +0000195 for i in range(0, 200):
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200196 with gzip.GzipFile(self.filename, "ab", 9) as f: # append
Brian Curtin28f96b52010-10-13 02:21:42 +0000197 f.write(b'a')
Andrew M. Kuchling01cb47b2005-06-09 14:19:32 +0000198
199 # Try reading the file
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200200 with gzip.GzipFile(self.filename, "rb") as zgfile:
Brian Curtin28f96b52010-10-13 02:21:42 +0000201 contents = b""
202 while 1:
203 ztxt = zgfile.read(8192)
204 contents += ztxt
205 if not ztxt: break
Ezio Melottib3aedd42010-11-20 19:04:17 +0000206 self.assertEqual(contents, b'a'*201)
Andrew M. Kuchling01cb47b2005-06-09 14:19:32 +0000207
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200208 def test_exclusive_write(self):
209 with gzip.GzipFile(self.filename, 'xb') as f:
210 f.write(data1 * 50)
211 with gzip.GzipFile(self.filename, 'rb') as f:
212 self.assertEqual(f.read(), data1 * 50)
213 with self.assertRaises(FileExistsError):
214 gzip.GzipFile(self.filename, 'xb')
215
Antoine Pitroub1f88352010-01-03 22:37:40 +0000216 def test_buffered_reader(self):
217 # Issue #7471: a GzipFile can be wrapped in a BufferedReader for
218 # performance.
219 self.test_write()
220
Brian Curtin28f96b52010-10-13 02:21:42 +0000221 with gzip.GzipFile(self.filename, 'rb') as f:
222 with io.BufferedReader(f) as r:
223 lines = [line for line in r]
Antoine Pitroub1f88352010-01-03 22:37:40 +0000224
Ezio Melottid8b509b2011-09-28 17:37:55 +0300225 self.assertEqual(lines, 50 * data1.splitlines(keepends=True))
Andrew M. Kuchling01cb47b2005-06-09 14:19:32 +0000226
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000227 def test_readline(self):
228 self.test_write()
229 # Try .readline() with varying line lengths
Martin v. Löwis8cc965c2001-08-09 07:21:56 +0000230
Brian Curtin28f96b52010-10-13 02:21:42 +0000231 with gzip.GzipFile(self.filename, 'rb') as f:
232 line_length = 0
233 while 1:
234 L = f.readline(line_length)
235 if not L and line_length != 0: break
236 self.assertTrue(len(L) <= line_length)
237 line_length = (line_length + 1) % 50
Martin v. Löwis8cc965c2001-08-09 07:21:56 +0000238
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000239 def test_readlines(self):
240 self.test_write()
241 # Try .readlines()
Andrew M. Kuchling605ebdd1999-03-25 21:50:27 +0000242
Brian Curtin28f96b52010-10-13 02:21:42 +0000243 with gzip.GzipFile(self.filename, 'rb') as f:
244 L = f.readlines()
Skip Montanaro12424bc2002-05-23 01:43:05 +0000245
Brian Curtin28f96b52010-10-13 02:21:42 +0000246 with gzip.GzipFile(self.filename, 'rb') as f:
247 while 1:
248 L = f.readlines(150)
249 if L == []: break
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000250
251 def test_seek_read(self):
252 self.test_write()
253 # Try seek, read test
254
Brian Curtin28f96b52010-10-13 02:21:42 +0000255 with gzip.GzipFile(self.filename) as f:
256 while 1:
257 oldpos = f.tell()
258 line1 = f.readline()
259 if not line1: break
260 newpos = f.tell()
261 f.seek(oldpos) # negative seek
262 if len(line1)>10:
263 amount = 10
264 else:
265 amount = len(line1)
266 line2 = f.read(amount)
267 self.assertEqual(line1[:amount], line2)
268 f.seek(newpos) # positive seek
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000269
Thomas Wouters89f507f2006-12-13 04:49:30 +0000270 def test_seek_whence(self):
271 self.test_write()
272 # Try seek(whence=1), read test
273
Brian Curtin28f96b52010-10-13 02:21:42 +0000274 with gzip.GzipFile(self.filename) as f:
275 f.read(10)
276 f.seek(10, whence=1)
277 y = f.read(10)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000278 self.assertEqual(y, data1[20:30])
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000279
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000280 def test_seek_write(self):
281 # Try seek, write test
Brian Curtin28f96b52010-10-13 02:21:42 +0000282 with gzip.GzipFile(self.filename, 'w') as f:
283 for pos in range(0, 256, 16):
284 f.seek(pos)
285 f.write(b'GZ\n')
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000286
287 def test_mode(self):
288 self.test_write()
Brian Curtin28f96b52010-10-13 02:21:42 +0000289 with gzip.GzipFile(self.filename, 'r') as f:
290 self.assertEqual(f.myfileobj.mode, 'rb')
Hai Shibb0424b2020-08-04 00:47:42 +0800291 os_helper.unlink(self.filename)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200292 with gzip.GzipFile(self.filename, 'x') as f:
293 self.assertEqual(f.myfileobj.mode, 'xb')
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000294
Thomas Wouterscf297e42007-02-23 15:07:44 +0000295 def test_1647484(self):
296 for mode in ('wb', 'rb'):
Brian Curtin28f96b52010-10-13 02:21:42 +0000297 with gzip.GzipFile(self.filename, mode) as f:
298 self.assertTrue(hasattr(f, "name"))
299 self.assertEqual(f.name, self.filename)
Thomas Wouterscf297e42007-02-23 15:07:44 +0000300
Georg Brandl9f1c1dc2010-11-20 11:25:01 +0000301 def test_paddedfile_getattr(self):
302 self.test_write()
303 with gzip.GzipFile(self.filename, 'rb') as f:
304 self.assertTrue(hasattr(f.fileobj, "name"))
305 self.assertEqual(f.fileobj.name, self.filename)
306
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000307 def test_mtime(self):
308 mtime = 123456789
Brian Curtin28f96b52010-10-13 02:21:42 +0000309 with gzip.GzipFile(self.filename, 'w', mtime = mtime) as fWrite:
310 fWrite.write(data1)
311 with gzip.GzipFile(self.filename) as fRead:
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200312 self.assertTrue(hasattr(fRead, 'mtime'))
313 self.assertIsNone(fRead.mtime)
Brian Curtin28f96b52010-10-13 02:21:42 +0000314 dataRead = fRead.read()
315 self.assertEqual(dataRead, data1)
Brian Curtin28f96b52010-10-13 02:21:42 +0000316 self.assertEqual(fRead.mtime, mtime)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000317
318 def test_metadata(self):
319 mtime = 123456789
320
Brian Curtin28f96b52010-10-13 02:21:42 +0000321 with gzip.GzipFile(self.filename, 'w', mtime = mtime) as fWrite:
322 fWrite.write(data1)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000323
Brian Curtin28f96b52010-10-13 02:21:42 +0000324 with open(self.filename, 'rb') as fRead:
325 # see RFC 1952: http://www.faqs.org/rfcs/rfc1952.html
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000326
Brian Curtin28f96b52010-10-13 02:21:42 +0000327 idBytes = fRead.read(2)
328 self.assertEqual(idBytes, b'\x1f\x8b') # gzip ID
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000329
Brian Curtin28f96b52010-10-13 02:21:42 +0000330 cmByte = fRead.read(1)
331 self.assertEqual(cmByte, b'\x08') # deflate
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000332
Serhiy Storchaka700cfa82020-06-25 17:56:31 +0300333 try:
334 expectedname = self.filename.encode('Latin-1') + b'\x00'
335 expectedflags = b'\x08' # only the FNAME flag is set
336 except UnicodeEncodeError:
337 expectedname = b''
338 expectedflags = b'\x00'
339
Brian Curtin28f96b52010-10-13 02:21:42 +0000340 flagsByte = fRead.read(1)
Serhiy Storchaka700cfa82020-06-25 17:56:31 +0300341 self.assertEqual(flagsByte, expectedflags)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000342
Brian Curtin28f96b52010-10-13 02:21:42 +0000343 mtimeBytes = fRead.read(4)
344 self.assertEqual(mtimeBytes, struct.pack('<i', mtime)) # little-endian
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000345
Brian Curtin28f96b52010-10-13 02:21:42 +0000346 xflByte = fRead.read(1)
347 self.assertEqual(xflByte, b'\x02') # maximum compression
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000348
Brian Curtin28f96b52010-10-13 02:21:42 +0000349 osByte = fRead.read(1)
350 self.assertEqual(osByte, b'\xff') # OS "unknown" (OS-independent)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000351
Brian Curtin28f96b52010-10-13 02:21:42 +0000352 # Since the FNAME flag is set, the zero-terminated filename follows.
353 # RFC 1952 specifies that this is the name of the input file, if any.
354 # However, the gzip module defaults to storing the name of the output
355 # file in this field.
Serhiy Storchaka700cfa82020-06-25 17:56:31 +0300356 nameBytes = fRead.read(len(expectedname))
357 self.assertEqual(nameBytes, expectedname)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000358
Brian Curtin28f96b52010-10-13 02:21:42 +0000359 # Since no other flags were set, the header ends here.
360 # Rather than process the compressed data, let's seek to the trailer.
361 fRead.seek(os.stat(self.filename).st_size - 8)
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000362
Brian Curtin28f96b52010-10-13 02:21:42 +0000363 crc32Bytes = fRead.read(4) # CRC32 of uncompressed data [data1]
364 self.assertEqual(crc32Bytes, b'\xaf\xd7d\x83')
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000365
Brian Curtin28f96b52010-10-13 02:21:42 +0000366 isizeBytes = fRead.read(4)
367 self.assertEqual(isizeBytes, struct.pack('<i', len(data1)))
Antoine Pitrou42db3ef2009-01-04 21:37:59 +0000368
Serhiy Storchaka700cfa82020-06-25 17:56:31 +0300369 def test_metadata_ascii_name(self):
Hai Shibb0424b2020-08-04 00:47:42 +0800370 self.filename = os_helper.TESTFN_ASCII
Serhiy Storchaka700cfa82020-06-25 17:56:31 +0300371 self.test_metadata()
372
William Chargineab3b3f2020-01-21 03:25:24 -0800373 def test_compresslevel_metadata(self):
374 # see RFC 1952: http://www.faqs.org/rfcs/rfc1952.html
375 # specifically, discussion of XFL in section 2.3.1
376 cases = [
377 ('fast', 1, b'\x04'),
378 ('best', 9, b'\x02'),
379 ('tradeoff', 6, b'\x00'),
380 ]
381 xflOffset = 8
382
383 for (name, level, expectedXflByte) in cases:
384 with self.subTest(name):
385 fWrite = gzip.GzipFile(self.filename, 'w', compresslevel=level)
386 with fWrite:
387 fWrite.write(data1)
388 with open(self.filename, 'rb') as fRead:
389 fRead.seek(xflOffset)
390 xflByte = fRead.read(1)
391 self.assertEqual(xflByte, expectedXflByte)
392
Antoine Pitrou308705e2009-01-10 16:22:51 +0000393 def test_with_open(self):
394 # GzipFile supports the context management protocol
395 with gzip.GzipFile(self.filename, "wb") as f:
396 f.write(b"xxx")
397 f = gzip.GzipFile(self.filename, "rb")
398 f.close()
399 try:
400 with f:
401 pass
402 except ValueError:
403 pass
404 else:
405 self.fail("__enter__ on a closed file didn't raise an exception")
406 try:
407 with gzip.GzipFile(self.filename, "wb") as f:
408 1/0
409 except ZeroDivisionError:
410 pass
411 else:
412 self.fail("1/0 didn't raise an exception")
413
Antoine Pitrou8e33fd72010-01-13 14:37:26 +0000414 def test_zero_padded_file(self):
415 with gzip.GzipFile(self.filename, "wb") as f:
416 f.write(data1 * 50)
417
418 # Pad the file with zeroes
419 with open(self.filename, "ab") as f:
420 f.write(b"\x00" * 50)
421
422 with gzip.GzipFile(self.filename, "rb") as f:
423 d = f.read()
424 self.assertEqual(d, data1 * 50, "Incorrect data in file")
425
Zackery Spytzcf599f62019-05-13 01:50:52 -0600426 def test_gzip_BadGzipFile_exception(self):
427 self.assertTrue(issubclass(gzip.BadGzipFile, OSError))
428
429 def test_bad_gzip_file(self):
430 with open(self.filename, 'wb') as file:
431 file.write(data1 * 50)
432 with gzip.GzipFile(self.filename, 'r') as file:
433 self.assertRaises(gzip.BadGzipFile, file.readlines)
434
Antoine Pitrou7b969842010-09-23 16:22:51 +0000435 def test_non_seekable_file(self):
436 uncompressed = data1 * 50
437 buf = UnseekableIO()
438 with gzip.GzipFile(fileobj=buf, mode="wb") as f:
439 f.write(uncompressed)
440 compressed = buf.getvalue()
441 buf = UnseekableIO(compressed)
442 with gzip.GzipFile(fileobj=buf, mode="rb") as f:
443 self.assertEqual(f.read(), uncompressed)
444
Antoine Pitrouc3ed2e72010-09-29 10:49:46 +0000445 def test_peek(self):
446 uncompressed = data1 * 200
447 with gzip.GzipFile(self.filename, "wb") as f:
448 f.write(uncompressed)
449
450 def sizes():
451 while True:
452 for n in range(5, 50, 10):
453 yield n
454
455 with gzip.GzipFile(self.filename, "rb") as f:
456 f.max_read_chunk = 33
457 nread = 0
458 for n in sizes():
459 s = f.peek(n)
460 if s == b'':
461 break
462 self.assertEqual(f.read(len(s)), s)
463 nread += len(s)
464 self.assertEqual(f.read(100), b'')
465 self.assertEqual(nread, len(uncompressed))
466
Antoine Pitrou4ec4b0c2011-04-04 21:00:37 +0200467 def test_textio_readlines(self):
468 # Issue #10791: TextIOWrapper.readlines() fails when wrapping GzipFile.
Ezio Melottid8b509b2011-09-28 17:37:55 +0300469 lines = (data1 * 50).decode("ascii").splitlines(keepends=True)
Antoine Pitrou4ec4b0c2011-04-04 21:00:37 +0200470 self.test_write()
471 with gzip.GzipFile(self.filename, 'r') as f:
472 with io.TextIOWrapper(f, encoding="ascii") as t:
473 self.assertEqual(t.readlines(), lines)
474
Nadeem Vawda892b0b92012-01-18 09:25:58 +0200475 def test_fileobj_from_fdopen(self):
476 # Issue #13781: Opening a GzipFile for writing fails when using a
477 # fileobj created with os.fdopen().
478 fd = os.open(self.filename, os.O_WRONLY | os.O_CREAT)
479 with os.fdopen(fd, "wb") as f:
480 with gzip.GzipFile(fileobj=f, mode="w") as g:
481 pass
482
Serhiy Storchakabcbdd2f2017-10-22 13:18:21 +0300483 def test_fileobj_mode(self):
484 gzip.GzipFile(self.filename, "wb").close()
485 with open(self.filename, "r+b") as f:
486 with gzip.GzipFile(fileobj=f, mode='r') as g:
487 self.assertEqual(g.mode, gzip.READ)
488 with gzip.GzipFile(fileobj=f, mode='w') as g:
489 self.assertEqual(g.mode, gzip.WRITE)
490 with gzip.GzipFile(fileobj=f, mode='a') as g:
491 self.assertEqual(g.mode, gzip.WRITE)
492 with gzip.GzipFile(fileobj=f, mode='x') as g:
493 self.assertEqual(g.mode, gzip.WRITE)
494 with self.assertRaises(ValueError):
495 gzip.GzipFile(fileobj=f, mode='z')
496 for mode in "rb", "r+b":
497 with open(self.filename, mode) as f:
498 with gzip.GzipFile(fileobj=f) as g:
499 self.assertEqual(g.mode, gzip.READ)
500 for mode in "wb", "ab", "xb":
501 if "x" in mode:
Hai Shibb0424b2020-08-04 00:47:42 +0800502 os_helper.unlink(self.filename)
Serhiy Storchakabcbdd2f2017-10-22 13:18:21 +0300503 with open(self.filename, mode) as f:
Serhiy Storchakaa0652322019-11-16 18:56:57 +0200504 with self.assertWarns(FutureWarning):
505 g = gzip.GzipFile(fileobj=f)
506 with g:
Serhiy Storchakabcbdd2f2017-10-22 13:18:21 +0300507 self.assertEqual(g.mode, gzip.WRITE)
508
Nadeem Vawda103e8112012-06-20 01:35:22 +0200509 def test_bytes_filename(self):
510 str_filename = self.filename
511 try:
512 bytes_filename = str_filename.encode("ascii")
513 except UnicodeEncodeError:
514 self.skipTest("Temporary file name needs to be ASCII")
515 with gzip.GzipFile(bytes_filename, "wb") as f:
516 f.write(data1 * 50)
517 with gzip.GzipFile(bytes_filename, "rb") as f:
518 self.assertEqual(f.read(), data1 * 50)
519 # Sanity check that we are actually operating on the right file.
520 with gzip.GzipFile(str_filename, "rb") as f:
521 self.assertEqual(f.read(), data1 * 50)
522
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200523 def test_decompress_limited(self):
524 """Decompressed data buffering should be limited"""
Serhiy Storchaka5f1a5182016-09-11 14:41:02 +0300525 bomb = gzip.compress(b'\0' * int(2e6), compresslevel=9)
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200526 self.assertLess(len(bomb), io.DEFAULT_BUFFER_SIZE)
527
528 bomb = io.BytesIO(bomb)
529 decomp = gzip.GzipFile(fileobj=bomb)
Serhiy Storchaka5f1a5182016-09-11 14:41:02 +0300530 self.assertEqual(decomp.read(1), b'\0')
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200531 max_decomp = 1 + io.DEFAULT_BUFFER_SIZE
532 self.assertLessEqual(decomp._buffer.raw.tell(), max_decomp,
533 "Excessive amount of data was decompressed")
534
Antoine Pitrou79c5ef12010-08-17 21:10:05 +0000535 # Testing compress/decompress shortcut functions
536
537 def test_compress(self):
538 for data in [data1, data2]:
539 for args in [(), (1,), (6,), (9,)]:
540 datac = gzip.compress(data, *args)
541 self.assertEqual(type(datac), bytes)
542 with gzip.GzipFile(fileobj=io.BytesIO(datac), mode="rb") as f:
543 self.assertEqual(f.read(), data)
544
guoci0e7497c2018-11-07 04:50:23 -0500545 def test_compress_mtime(self):
546 mtime = 123456789
547 for data in [data1, data2]:
548 for args in [(), (1,), (6,), (9,)]:
549 with self.subTest(data=data, args=args):
550 datac = gzip.compress(data, *args, mtime=mtime)
551 self.assertEqual(type(datac), bytes)
552 with gzip.GzipFile(fileobj=io.BytesIO(datac), mode="rb") as f:
553 f.read(1) # to set mtime attribute
554 self.assertEqual(f.mtime, mtime)
555
Antoine Pitrou79c5ef12010-08-17 21:10:05 +0000556 def test_decompress(self):
557 for data in (data1, data2):
558 buf = io.BytesIO()
559 with gzip.GzipFile(fileobj=buf, mode="wb") as f:
560 f.write(data)
561 self.assertEqual(gzip.decompress(buf.getvalue()), data)
562 # Roundtrip with compress
563 datac = gzip.compress(data)
564 self.assertEqual(gzip.decompress(datac), data)
565
Serhiy Storchaka7c3922f2013-01-22 17:01:59 +0200566 def test_read_truncated(self):
567 data = data1*50
568 # Drop the CRC (4 bytes) and file size (4 bytes).
569 truncated = gzip.compress(data)[:-8]
570 with gzip.GzipFile(fileobj=io.BytesIO(truncated)) as f:
571 self.assertRaises(EOFError, f.read)
572 with gzip.GzipFile(fileobj=io.BytesIO(truncated)) as f:
573 self.assertEqual(f.read(len(data)), data)
574 self.assertRaises(EOFError, f.read, 1)
575 # Incomplete 10-byte header.
576 for i in range(2, 10):
577 with gzip.GzipFile(fileobj=io.BytesIO(truncated[:i])) as f:
578 self.assertRaises(EOFError, f.read, 1)
579
Serhiy Storchaka7e69f002013-04-08 22:35:02 +0300580 def test_read_with_extra(self):
581 # Gzip data with an extra field
582 gzdata = (b'\x1f\x8b\x08\x04\xb2\x17cQ\x02\xff'
583 b'\x05\x00Extra'
584 b'\x0bI-.\x01\x002\xd1Mx\x04\x00\x00\x00')
585 with gzip.GzipFile(fileobj=io.BytesIO(gzdata)) as f:
586 self.assertEqual(f.read(), b'Test')
Nadeem Vawda7e126202012-05-06 15:04:01 +0200587
Ned Deily61207392014-03-09 14:44:34 -0700588 def test_prepend_error(self):
589 # See issue #20875
590 with gzip.open(self.filename, "wb") as f:
591 f.write(data1)
592 with gzip.open(self.filename, "rb") as f:
Antoine Pitrou2dbc6e62015-04-11 00:31:01 +0200593 f._buffer.raw._fp.prepend()
Ned Deily61207392014-03-09 14:44:34 -0700594
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200595class TestOpen(BaseTest):
596 def test_binary_modes(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200597 uncompressed = data1 * 50
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200598
Nadeem Vawda7e126202012-05-06 15:04:01 +0200599 with gzip.open(self.filename, "wb") as f:
600 f.write(uncompressed)
601 with open(self.filename, "rb") as f:
602 file_data = gzip.decompress(f.read())
603 self.assertEqual(file_data, uncompressed)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200604
Nadeem Vawda7e126202012-05-06 15:04:01 +0200605 with gzip.open(self.filename, "rb") as f:
606 self.assertEqual(f.read(), uncompressed)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200607
Nadeem Vawda7e126202012-05-06 15:04:01 +0200608 with gzip.open(self.filename, "ab") as f:
609 f.write(uncompressed)
610 with open(self.filename, "rb") as f:
611 file_data = gzip.decompress(f.read())
612 self.assertEqual(file_data, uncompressed * 2)
613
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200614 with self.assertRaises(FileExistsError):
615 gzip.open(self.filename, "xb")
Hai Shibb0424b2020-08-04 00:47:42 +0800616 os_helper.unlink(self.filename)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200617 with gzip.open(self.filename, "xb") as f:
618 f.write(uncompressed)
619 with open(self.filename, "rb") as f:
620 file_data = gzip.decompress(f.read())
621 self.assertEqual(file_data, uncompressed)
622
Berker Peksag03020cf2016-10-02 13:47:58 +0300623 def test_pathlike_file(self):
624 filename = pathlib.Path(self.filename)
625 with gzip.open(filename, "wb") as f:
626 f.write(data1 * 50)
627 with gzip.open(filename, "ab") as f:
628 f.write(data1)
629 with gzip.open(filename) as f:
630 self.assertEqual(f.read(), data1 * 51)
631
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200632 def test_implicit_binary_modes(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200633 # Test implicit binary modes (no "b" or "t" in mode string).
634 uncompressed = data1 * 50
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200635
Nadeem Vawda7e126202012-05-06 15:04:01 +0200636 with gzip.open(self.filename, "w") as f:
637 f.write(uncompressed)
638 with open(self.filename, "rb") as f:
639 file_data = gzip.decompress(f.read())
640 self.assertEqual(file_data, uncompressed)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200641
Nadeem Vawda7e126202012-05-06 15:04:01 +0200642 with gzip.open(self.filename, "r") as f:
643 self.assertEqual(f.read(), uncompressed)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200644
Nadeem Vawda7e126202012-05-06 15:04:01 +0200645 with gzip.open(self.filename, "a") as f:
646 f.write(uncompressed)
647 with open(self.filename, "rb") as f:
648 file_data = gzip.decompress(f.read())
649 self.assertEqual(file_data, uncompressed * 2)
650
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200651 with self.assertRaises(FileExistsError):
652 gzip.open(self.filename, "x")
Hai Shibb0424b2020-08-04 00:47:42 +0800653 os_helper.unlink(self.filename)
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200654 with gzip.open(self.filename, "x") as f:
655 f.write(uncompressed)
656 with open(self.filename, "rb") as f:
657 file_data = gzip.decompress(f.read())
658 self.assertEqual(file_data, uncompressed)
659
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200660 def test_text_modes(self):
Nadeem Vawda11328e42012-05-06 19:24:18 +0200661 uncompressed = data1.decode("ascii") * 50
662 uncompressed_raw = uncompressed.replace("\n", os.linesep)
Inada Naoki3caea9a2021-04-04 17:01:10 +0900663 with gzip.open(self.filename, "wt", encoding="ascii") as f:
Nadeem Vawda7e126202012-05-06 15:04:01 +0200664 f.write(uncompressed)
665 with open(self.filename, "rb") as f:
666 file_data = gzip.decompress(f.read()).decode("ascii")
Nadeem Vawda11328e42012-05-06 19:24:18 +0200667 self.assertEqual(file_data, uncompressed_raw)
Inada Naoki3caea9a2021-04-04 17:01:10 +0900668 with gzip.open(self.filename, "rt", encoding="ascii") as f:
Nadeem Vawda7e126202012-05-06 15:04:01 +0200669 self.assertEqual(f.read(), uncompressed)
Inada Naoki3caea9a2021-04-04 17:01:10 +0900670 with gzip.open(self.filename, "at", encoding="ascii") as f:
Nadeem Vawda7e126202012-05-06 15:04:01 +0200671 f.write(uncompressed)
672 with open(self.filename, "rb") as f:
673 file_data = gzip.decompress(f.read()).decode("ascii")
Nadeem Vawda11328e42012-05-06 19:24:18 +0200674 self.assertEqual(file_data, uncompressed_raw * 2)
Nadeem Vawda7e126202012-05-06 15:04:01 +0200675
Nadeem Vawda68721012012-06-04 23:21:38 +0200676 def test_fileobj(self):
677 uncompressed_bytes = data1 * 50
678 uncompressed_str = uncompressed_bytes.decode("ascii")
679 compressed = gzip.compress(uncompressed_bytes)
680 with gzip.open(io.BytesIO(compressed), "r") as f:
681 self.assertEqual(f.read(), uncompressed_bytes)
682 with gzip.open(io.BytesIO(compressed), "rb") as f:
683 self.assertEqual(f.read(), uncompressed_bytes)
Inada Naoki3caea9a2021-04-04 17:01:10 +0900684 with gzip.open(io.BytesIO(compressed), "rt", encoding="ascii") as f:
Nadeem Vawda68721012012-06-04 23:21:38 +0200685 self.assertEqual(f.read(), uncompressed_str)
686
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200687 def test_bad_params(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200688 # Test invalid parameter combinations.
Nadeem Vawda68721012012-06-04 23:21:38 +0200689 with self.assertRaises(TypeError):
690 gzip.open(123.456)
Nadeem Vawda7e126202012-05-06 15:04:01 +0200691 with self.assertRaises(ValueError):
692 gzip.open(self.filename, "wbt")
693 with self.assertRaises(ValueError):
Nadeem Vawdaee1be992013-10-19 00:11:13 +0200694 gzip.open(self.filename, "xbt")
695 with self.assertRaises(ValueError):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200696 gzip.open(self.filename, "rb", encoding="utf-8")
697 with self.assertRaises(ValueError):
698 gzip.open(self.filename, "rb", errors="ignore")
699 with self.assertRaises(ValueError):
700 gzip.open(self.filename, "rb", newline="\n")
701
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200702 def test_encoding(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200703 # Test non-default encoding.
Nadeem Vawda11328e42012-05-06 19:24:18 +0200704 uncompressed = data1.decode("ascii") * 50
705 uncompressed_raw = uncompressed.replace("\n", os.linesep)
Nadeem Vawda7e126202012-05-06 15:04:01 +0200706 with gzip.open(self.filename, "wt", encoding="utf-16") as f:
707 f.write(uncompressed)
708 with open(self.filename, "rb") as f:
709 file_data = gzip.decompress(f.read()).decode("utf-16")
Nadeem Vawda11328e42012-05-06 19:24:18 +0200710 self.assertEqual(file_data, uncompressed_raw)
Nadeem Vawda7e126202012-05-06 15:04:01 +0200711 with gzip.open(self.filename, "rt", encoding="utf-16") as f:
712 self.assertEqual(f.read(), uncompressed)
713
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200714 def test_encoding_error_handler(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200715 # Test with non-default encoding error handler.
716 with gzip.open(self.filename, "wb") as f:
717 f.write(b"foo\xffbar")
718 with gzip.open(self.filename, "rt", encoding="ascii", errors="ignore") \
719 as f:
720 self.assertEqual(f.read(), "foobar")
721
Nadeem Vawda1b8a14d2012-05-06 15:17:52 +0200722 def test_newline(self):
Nadeem Vawda7e126202012-05-06 15:04:01 +0200723 # Test with explicit newline (universal newline mode disabled).
724 uncompressed = data1.decode("ascii") * 50
Inada Naoki3caea9a2021-04-04 17:01:10 +0900725 with gzip.open(self.filename, "wt", encoding="ascii", newline="\n") as f:
Nadeem Vawda7e126202012-05-06 15:04:01 +0200726 f.write(uncompressed)
Inada Naoki3caea9a2021-04-04 17:01:10 +0900727 with gzip.open(self.filename, "rt", encoding="ascii", newline="\r") as f:
Nadeem Vawda7e126202012-05-06 15:04:01 +0200728 self.assertEqual(f.readlines(), [uncompressed])
729
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200730
731def create_and_remove_directory(directory):
732 def decorator(function):
733 @functools.wraps(function)
734 def wrapper(*args, **kwargs):
735 os.makedirs(directory)
736 try:
737 return function(*args, **kwargs)
738 finally:
Hai Shibb0424b2020-08-04 00:47:42 +0800739 os_helper.rmtree(directory)
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200740 return wrapper
741 return decorator
742
743
744class TestCommandLine(unittest.TestCase):
745 data = b'This is a simple test with gzip'
746
747 def test_decompress_stdin_stdout(self):
748 with io.BytesIO() as bytes_io:
749 with gzip.GzipFile(fileobj=bytes_io, mode='wb') as gzip_file:
750 gzip_file.write(self.data)
751
752 args = sys.executable, '-m', 'gzip', '-d'
753 with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
754 out, err = proc.communicate(bytes_io.getvalue())
755
756 self.assertEqual(err, b'')
757 self.assertEqual(out, self.data)
758
759 @create_and_remove_directory(TEMPDIR)
760 def test_decompress_infile_outfile(self):
761 gzipname = os.path.join(TEMPDIR, 'testgzip.gz')
762 self.assertFalse(os.path.exists(gzipname))
763
764 with gzip.open(gzipname, mode='wb') as fp:
765 fp.write(self.data)
766 rc, out, err = assert_python_ok('-m', 'gzip', '-d', gzipname)
767
768 with open(os.path.join(TEMPDIR, "testgzip"), "rb") as gunziped:
769 self.assertEqual(gunziped.read(), self.data)
770
771 self.assertTrue(os.path.exists(gzipname))
772 self.assertEqual(rc, 0)
773 self.assertEqual(out, b'')
774 self.assertEqual(err, b'')
775
776 def test_decompress_infile_outfile_error(self):
Ruben Vordermancc3df632021-02-25 12:30:24 +0100777 rc, out, err = assert_python_failure('-m', 'gzip', '-d', 'thisisatest.out')
Inada Naoki9525a182021-02-26 11:09:06 +0900778 self.assertEqual(b"filename doesn't end in .gz: 'thisisatest.out'", err.strip())
Ruben Vordermancc3df632021-02-25 12:30:24 +0100779 self.assertEqual(rc, 1)
780 self.assertEqual(out, b'')
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200781
782 @create_and_remove_directory(TEMPDIR)
783 def test_compress_stdin_outfile(self):
784 args = sys.executable, '-m', 'gzip'
785 with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
786 out, err = proc.communicate(self.data)
787
788 self.assertEqual(err, b'')
789 self.assertEqual(out[:2], b"\x1f\x8b")
790
791 @create_and_remove_directory(TEMPDIR)
Gregory P. Smithcd466552019-04-14 10:32:07 -0700792 def test_compress_infile_outfile_default(self):
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200793 local_testgzip = os.path.join(TEMPDIR, 'testgzip')
794 gzipname = local_testgzip + '.gz'
795 self.assertFalse(os.path.exists(gzipname))
796
797 with open(local_testgzip, 'wb') as fp:
798 fp.write(self.data)
799
800 rc, out, err = assert_python_ok('-m', 'gzip', local_testgzip)
801
802 self.assertTrue(os.path.exists(gzipname))
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200803 self.assertEqual(out, b'')
804 self.assertEqual(err, b'')
805
Stéphane Wirtel3e28eed2018-11-03 16:24:23 +0100806 @create_and_remove_directory(TEMPDIR)
807 def test_compress_infile_outfile(self):
808 for compress_level in ('--fast', '--best'):
809 with self.subTest(compress_level=compress_level):
810 local_testgzip = os.path.join(TEMPDIR, 'testgzip')
811 gzipname = local_testgzip + '.gz'
812 self.assertFalse(os.path.exists(gzipname))
813
814 with open(local_testgzip, 'wb') as fp:
815 fp.write(self.data)
816
817 rc, out, err = assert_python_ok('-m', 'gzip', compress_level, local_testgzip)
818
819 self.assertTrue(os.path.exists(gzipname))
820 self.assertEqual(out, b'')
821 self.assertEqual(err, b'')
822 os.remove(gzipname)
823 self.assertFalse(os.path.exists(gzipname))
824
825 def test_compress_fast_best_are_exclusive(self):
826 rc, out, err = assert_python_failure('-m', 'gzip', '--fast', '--best')
827 self.assertIn(b"error: argument --best: not allowed with argument --fast", err)
828 self.assertEqual(out, b'')
829
830 def test_decompress_cannot_have_flags_compression(self):
831 rc, out, err = assert_python_failure('-m', 'gzip', '--fast', '-d')
832 self.assertIn(b'error: argument -d/--decompress: not allowed with argument --fast', err)
833 self.assertEqual(out, b'')
834
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200835
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000836def test_main(verbose=None):
Stéphane Wirtel84eec112018-10-09 23:16:43 +0200837 support.run_unittest(TestGzip, TestOpen, TestCommandLine)
838
Andrew M. Kuchlinga6f68e12005-06-09 14:12:36 +0000839
840if __name__ == "__main__":
841 test_main(verbose=True)