From 701aa3ba952fb1519630c46cb943c33ec926cabc Mon Sep 17 00:00:00 2001 From: jevans Date: Thu, 21 Aug 2014 08:44:20 -0400 Subject: [PATCH] Starting to clearly separate warning testing from any other testing. --- glymur/codestream.py | 2 +- ...am_warnings.py => test_glymur_warnings.py} | 29 +++- glymur/test/test_opj_suite.py | 126 +++++++++++++++++ glymur/test/test_opj_suite_dump.py | 15 +- glymur/test/test_opj_suite_warn.py | 133 ------------------ glymur/test/test_printing.py | 34 +++-- 6 files changed, 187 insertions(+), 152 deletions(-) rename glymur/test/{test_codestream_warnings.py => test_glymur_warnings.py} (75%) diff --git a/glymur/codestream.py b/glymur/codestream.py index acc7791..88babe5 100644 --- a/glymur/codestream.py +++ b/glymur/codestream.py @@ -202,7 +202,7 @@ class Codestream(object): msg = 'Invalid marker id encountered at byte {0:d} ' msg += 'in codestream: "0x{1:x}"' msg = msg.format(self._offset, self._marker_id) - warnings.warn(msg) + warnings.warn(msg, UserWarning) break self.segment.append(segment) diff --git a/glymur/test/test_codestream_warnings.py b/glymur/test/test_glymur_warnings.py similarity index 75% rename from glymur/test/test_codestream_warnings.py rename to glymur/test/test_glymur_warnings.py index 7fc79f6..1f65cc8 100644 --- a/glymur/test/test_codestream_warnings.py +++ b/glymur/test/test_glymur_warnings.py @@ -1,5 +1,5 @@ """ -Test suite for codestream parsing. +Test suite for warnings issued by glymur. """ # unittest doesn't work well with R0904. @@ -9,6 +9,7 @@ Test suite for codestream parsing. # pylint: disable=E1101 import os +import re import struct import sys import tempfile @@ -24,8 +25,30 @@ from .fixtures import opj_data_file, OPJ_DATA_ROOT "assertWarn methods introduced in 3.x") @unittest.skipIf(OPJ_DATA_ROOT is None, "OPJ_DATA_ROOT environment variable not set") -class TestCodestreamOpjDataWarnings(unittest.TestCase): - """Test suite for unusual codestream cases. Uses OPJ_DATA_ROOT""" +class TestWarnings(unittest.TestCase): + """Test suite for warnings issued by glymur.""" + + def test_NR_broken_jp2_dump(self): + """ + The colr box has a ridiculously incorrect box length. + """ + jfile = opj_data_file('input/nonregression/broken.jp2') + regex = re.compile(r'''b'colr'\sbox\shas\sincorrect\sbox\slength\s + \(\d+\)''', + re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): + jp2 = Jp2k(jfile) + + def test_NR_broken2_jp2_dump(self): + """ + Invalid marker ID on codestream. + """ + jfile = opj_data_file('input/nonregression/broken2.jp2') + regex = re.compile(r'''Invalid\smarker\sid\sencountered\sat\sbyte\s + \d+\sin\scodestream:\s*"0x[a-fA-F0-9]{4}"''', + re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): + jp2 = Jp2k(jfile) def test_bad_rsiz(self): """Should warn if RSIZ is bad. Issue196""" diff --git a/glymur/test/test_opj_suite.py b/glymur/test/test_opj_suite.py index 1e658a3..9c725c4 100644 --- a/glymur/test/test_opj_suite.py +++ b/glymur/test/test_opj_suite.py @@ -287,6 +287,132 @@ class TestSuite(unittest.TestCase): jpdata = jp2k.read() self.assertEqual(jpdata.shape, (512, 768, 3)) + def test_NR_broken_jp2_dump(self): + jfile = opj_data_file('input/nonregression/broken.jp2') + + with warnings.catch_warnings(): + # colr box has bad length. + warnings.simplefilter("ignore") + jp2 = Jp2k(jfile) + + ids = [box.box_id for box in jp2.box] + self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) + + ids = [box.box_id for box in jp2.box[2].box] + self.assertEqual(ids, ['ihdr', 'colr']) + + # Signature box. Check for corruption. + self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10)) + + # File type box. + self.assertEqual(jp2.box[1].brand, 'jp2 ') + self.assertEqual(jp2.box[1].minor_version, 0) + self.assertEqual(jp2.box[1].compatibility_list[0], 'jp2 ') + + # Jp2 Header + # Image header + self.assertEqual(jp2.box[2].box[0].height, 152) + self.assertEqual(jp2.box[2].box[0].width, 203) + self.assertEqual(jp2.box[2].box[0].num_components, 3) + self.assertEqual(jp2.box[2].box[0].bits_per_component, 8) + self.assertEqual(jp2.box[2].box[0].signed, False) + self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet + self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False) + self.assertEqual(jp2.box[2].box[0].ip_provided, False) + + # Jp2 Header + # Colour specification + self.assertEqual(jp2.box[2].box[1].method, + glymur.core.ENUMERATED_COLORSPACE) + self.assertEqual(jp2.box[2].box[1].precedence, 0) + self.assertEqual(jp2.box[2].box[1].approximation, 0) # not allowed? + self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) + + c = jp2.box[3].main_header + + ids = [x.marker_id for x in c.segment] + expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] + self.assertEqual(ids, expected) + + # SIZ: Image and tile size + # Profile: + self.assertEqual(c.segment[1].rsiz, 0) + # Reference grid size + self.assertEqual(c.segment[1].xsiz, 203) + self.assertEqual(c.segment[1].ysiz, 152) + # Reference grid offset + self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0)) + # Tile size + self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz), (203, 152)) + # Tile offset + self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz), (0, 0)) + # bitdepth + self.assertEqual(c.segment[1].bitdepth, (8, 8, 8)) + # signed + self.assertEqual(c.segment[1].signed, (False, False, False)) + # subsampling + self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)), + [(1, 1)] * 3) + + # COM: comment + # Registration + self.assertEqual(c.segment[2].rcme, glymur.core.RCME_ISO_8859_1) + # Comment value + self.assertEqual(c.segment[2].ccme.decode('latin-1'), + "Creator: JasPer Version 1.701.0") + + # COD: Coding style default + self.assertFalse(c.segment[3].scod & 2) # no sop + self.assertFalse(c.segment[3].scod & 4) # no eph + self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP) + self.assertEqual(c.segment[3].layers, 1) # layers = 1 + self.assertEqual(c.segment[3].spcod[3], 1) # mct + self.assertEqual(c.segment[3].spcod[4], 5) # level + self.assertEqual(tuple(c.segment[3].code_block_size), + (64, 64)) # cblk + # Selective arithmetic coding bypass + self.assertFalse(c.segment[3].spcod[7] & 0x01) + # Reset context probabilities + self.assertFalse(c.segment[3].spcod[7] & 0x02) + # Termination on each coding pass + self.assertFalse(c.segment[3].spcod[7] & 0x04) + # Vertically causal context + self.assertFalse(c.segment[3].spcod[7] & 0x08) + # Predictable termination + self.assertFalse(c.segment[3].spcod[7] & 0x0010) + # Segmentation symbols + self.assertFalse(c.segment[3].spcod[7] & 0x0020) + self.assertEqual(c.segment[3].spcod[8], + glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) + self.assertEqual(len(c.segment[3].spcod), 9) + + # QCD: Quantization default + self.assertEqual(c.segment[4].sqcd & 0x1f, 0) + self.assertEqual(c.segment[4].guard_bits, 2) + self.assertEqual(c.segment[4].mantissa, [0] * 16) + self.assertEqual(c.segment[4].exponent, + [8] + [9, 9, 10] * 5) + + # QCC: Quantization component + # associated component + self.assertEqual(c.segment[5].cqcc, 1) + self.assertEqual(c.segment[5].guard_bits, 2) + # quantization type + self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none + self.assertEqual(c.segment[5].mantissa, [0] * 16) + self.assertEqual(c.segment[5].exponent, + [8] + [9, 9, 10] * 5) + + # QCC: Quantization component + # associated component + self.assertEqual(c.segment[6].cqcc, 2) + self.assertEqual(c.segment[6].guard_bits, 2) + # quantization type + self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none + self.assertEqual(c.segment[6].mantissa, [0] * 16) + self.assertEqual(c.segment[6].exponent, + [8] + [9, 9, 10] * 5) + def test_NR_DEC_Bretagne2_j2k_1_decode(self): jfile = opj_data_file('input/nonregression/Bretagne2.j2k') jp2 = Jp2k(jfile) diff --git a/glymur/test/test_opj_suite_dump.py b/glymur/test/test_opj_suite_dump.py index e24f53f..138f72b 100644 --- a/glymur/test/test_opj_suite_dump.py +++ b/glymur/test/test_opj_suite_dump.py @@ -52,6 +52,18 @@ class TestSuiteDump(unittest.TestCase): def tearDown(self): pass + def test_NR_broken2_jp2_dump(self): + """ + Invalid marker ID in the codestream. + """ + jfile = opj_data_file('input/nonregression/broken2.jp2') + with warnings.catch_warnings(): + # Invalid marker ID on codestream. + warnings.simplefilter("ignore") + jp2 = Jp2k(jfile) + + self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') + def test_NR_file409752(self): jfile = opj_data_file('input/nonregression/file409752.jp2') jp2 = Jp2k(jfile) @@ -4594,8 +4606,7 @@ class TestSuiteDump(unittest.TestCase): lst = ['input', 'nonregression', 'issue188_beach_64bitsbox.jp2'] jfile = opj_data_file('/'.join(lst)) with warnings.catch_warnings(): - # There's a warning for an unknown box. We explicitly test for - # that down below. + # There's a warning for an unknown box. warnings.simplefilter("ignore") jp2 = Jp2k(jfile) diff --git a/glymur/test/test_opj_suite_warn.py b/glymur/test/test_opj_suite_warn.py index f63e9d0..d5ed4aa 100644 --- a/glymur/test/test_opj_suite_warn.py +++ b/glymur/test/test_opj_suite_warn.py @@ -54,139 +54,6 @@ class TestSuiteDumpWarnings(unittest.TestCase): def tearDown(self): pass - def test_NR_broken_jp2_dump(self): - jfile = opj_data_file('input/nonregression/broken.jp2') - with self.assertWarnsRegex(UserWarning, 'incorrect box length'): - # colr box has bad length. - jp2 = Jp2k(jfile) - - ids = [box.box_id for box in jp2.box] - self.assertEqual(ids, ['jP ', 'ftyp', 'jp2h', 'jp2c']) - - ids = [box.box_id for box in jp2.box[2].box] - self.assertEqual(ids, ['ihdr', 'colr']) - - # Signature box. Check for corruption. - self.assertEqual(jp2.box[0].signature, (13, 10, 135, 10)) - - # File type box. - self.assertEqual(jp2.box[1].brand, 'jp2 ') - self.assertEqual(jp2.box[1].minor_version, 0) - self.assertEqual(jp2.box[1].compatibility_list[0], 'jp2 ') - - # Jp2 Header - # Image header - self.assertEqual(jp2.box[2].box[0].height, 152) - self.assertEqual(jp2.box[2].box[0].width, 203) - self.assertEqual(jp2.box[2].box[0].num_components, 3) - self.assertEqual(jp2.box[2].box[0].bits_per_component, 8) - self.assertEqual(jp2.box[2].box[0].signed, False) - self.assertEqual(jp2.box[2].box[0].compression, 7) # wavelet - self.assertEqual(jp2.box[2].box[0].colorspace_unknown, False) - self.assertEqual(jp2.box[2].box[0].ip_provided, False) - - # Jp2 Header - # Colour specification - self.assertEqual(jp2.box[2].box[1].method, - glymur.core.ENUMERATED_COLORSPACE) - self.assertEqual(jp2.box[2].box[1].precedence, 0) - self.assertEqual(jp2.box[2].box[1].approximation, 0) # not allowed? - self.assertEqual(jp2.box[2].box[1].colorspace, glymur.core.SRGB) - - c = jp2.box[3].main_header - - ids = [x.marker_id for x in c.segment] - expected = ['SOC', 'SIZ', 'CME', 'COD', 'QCD', 'QCC', 'QCC'] - self.assertEqual(ids, expected) - - # SIZ: Image and tile size - # Profile: - self.assertEqual(c.segment[1].rsiz, 0) - # Reference grid size - self.assertEqual(c.segment[1].xsiz, 203) - self.assertEqual(c.segment[1].ysiz, 152) - # Reference grid offset - self.assertEqual((c.segment[1].xosiz, c.segment[1].yosiz), (0, 0)) - # Tile size - self.assertEqual((c.segment[1].xtsiz, c.segment[1].ytsiz), (203, 152)) - # Tile offset - self.assertEqual((c.segment[1].xtosiz, c.segment[1].ytosiz), (0, 0)) - # bitdepth - self.assertEqual(c.segment[1].bitdepth, (8, 8, 8)) - # signed - self.assertEqual(c.segment[1].signed, (False, False, False)) - # subsampling - self.assertEqual(list(zip(c.segment[1].xrsiz, c.segment[1].yrsiz)), - [(1, 1)] * 3) - - # COM: comment - # Registration - self.assertEqual(c.segment[2].rcme, glymur.core.RCME_ISO_8859_1) - # Comment value - self.assertEqual(c.segment[2].ccme.decode('latin-1'), - "Creator: JasPer Version 1.701.0") - - # COD: Coding style default - self.assertFalse(c.segment[3].scod & 2) # no sop - self.assertFalse(c.segment[3].scod & 4) # no eph - self.assertEqual(c.segment[3].spcod[0], glymur.core.LRCP) - self.assertEqual(c.segment[3].layers, 1) # layers = 1 - self.assertEqual(c.segment[3].spcod[3], 1) # mct - self.assertEqual(c.segment[3].spcod[4], 5) # level - self.assertEqual(tuple(c.segment[3].code_block_size), - (64, 64)) # cblk - # Selective arithmetic coding bypass - self.assertFalse(c.segment[3].spcod[7] & 0x01) - # Reset context probabilities - self.assertFalse(c.segment[3].spcod[7] & 0x02) - # Termination on each coding pass - self.assertFalse(c.segment[3].spcod[7] & 0x04) - # Vertically causal context - self.assertFalse(c.segment[3].spcod[7] & 0x08) - # Predictable termination - self.assertFalse(c.segment[3].spcod[7] & 0x0010) - # Segmentation symbols - self.assertFalse(c.segment[3].spcod[7] & 0x0020) - self.assertEqual(c.segment[3].spcod[8], - glymur.core.WAVELET_XFORM_5X3_REVERSIBLE) - self.assertEqual(len(c.segment[3].spcod), 9) - - # QCD: Quantization default - self.assertEqual(c.segment[4].sqcd & 0x1f, 0) - self.assertEqual(c.segment[4].guard_bits, 2) - self.assertEqual(c.segment[4].mantissa, [0] * 16) - self.assertEqual(c.segment[4].exponent, - [8] + [9, 9, 10] * 5) - - # QCC: Quantization component - # associated component - self.assertEqual(c.segment[5].cqcc, 1) - self.assertEqual(c.segment[5].guard_bits, 2) - # quantization type - self.assertEqual(c.segment[5].sqcc & 0x1f, 0) # none - self.assertEqual(c.segment[5].mantissa, [0] * 16) - self.assertEqual(c.segment[5].exponent, - [8] + [9, 9, 10] * 5) - - # QCC: Quantization component - # associated component - self.assertEqual(c.segment[6].cqcc, 2) - self.assertEqual(c.segment[6].guard_bits, 2) - # quantization type - self.assertEqual(c.segment[6].sqcc & 0x1f, 0) # none - self.assertEqual(c.segment[6].mantissa, [0] * 16) - self.assertEqual(c.segment[6].exponent, - [8] + [9, 9, 10] * 5) - - def test_NR_broken2_jp2_dump(self): - # Invalid marker ID on codestream. - jfile = opj_data_file('input/nonregression/broken2.jp2') - msg = 'Invalid marker id encountered at byte' - with self.assertWarnsRegex(UserWarning, msg): - jp2 = Jp2k(jfile) - - self.assertEqual(jp2.box[-1].main_header.segment[-1].marker_id, 'QCC') - @unittest.skipIf(sys.maxsize < 2**32, 'Do not run on 32-bit platforms') def test_NR_broken3_jp2_dump(self): """ diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index efd1ccf..10e3e54 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -11,6 +11,7 @@ # pylint: disable=R0904 import os +import re import struct import sys import tempfile @@ -70,6 +71,8 @@ class TestPrinting(unittest.TestCase): self.assertTrue(True) + @unittest.skipIf(sys.hexversion < 0x03030000, + "assertWarn methods introduced in 3.x") def test_unknown_superbox(self): """Verify that we can handle an unknown superbox.""" with tempfile.NamedTemporaryFile(suffix='.jpx') as tfile: @@ -86,10 +89,10 @@ class TestPrinting(unittest.TestCase): tfile.write(write_buffer) tfile.flush() - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') + regex = re.compile(r'''Unrecognized\sbox\s\(b'grp\s'\)\s + encountered.''', re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): jpx = Jp2k(tfile.name) - self.assertTrue(len(w), 1) glymur.set_printoptions(short=True) with patch('sys.stdout', new=StringIO()) as fake_out: @@ -725,8 +728,6 @@ class TestPrintingOpjDataRoot(unittest.TestCase): # Reset printoptions for every test. glymur.set_printoptions(short=False, xml=True, codestream=True) - warnings.resetwarnings() - def tearDown(self): pass @@ -744,33 +745,43 @@ class TestPrintingOpjDataRoot(unittest.TestCase): """An invalid colorspace shouldn't cause an error.""" filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') with warnings.catch_warnings(): + # Bad compatibility list item and bad colorspace warnings. Just + # suppress the warnings. warnings.simplefilter("ignore") jp2 = Jp2k(filename) with patch('sys.stdout', new=StringIO()) as fake_out: print(jp2) + @unittest.skipIf(sys.hexversion < 0x03030000, + "assertWarn methods introduced in 3.x") def test_bad_rsiz(self): """Should still be able to print if rsiz is bad, issue196""" filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2') - with warnings.catch_warnings(): - warnings.simplefilter("ignore") + regex = re.compile(r'''Invalid\sprofile:\s\(Rsiz=\d+\)\.''', re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): j = Jp2k(filename) with patch('sys.stdout', new=StringIO()) as fake_out: print(j) + @unittest.skipIf(sys.hexversion < 0x03030000, + "assertWarn methods introduced in 3.x") def test_bad_wavelet_transform(self): """Should still be able to print if wavelet xform is bad, issue195""" filename = opj_data_file('input/nonregression/edf_c2_10025.jp2') - with warnings.catch_warnings(): - warnings.simplefilter("ignore") + regex = re.compile(r'''Invalid\swavelet\stransform\sin\sCOD\ssegment:\s + \d+\.''', re.VERBOSE) + with self.assertWarnsRegex(UserWarning, regex): j = Jp2k(filename) with patch('sys.stdout', new=StringIO()) as fake_out: print(j) + @unittest.skipIf(sys.hexversion < 0x03030000, + "assertWarn methods introduced in 3.x") def test_invalid_progression_order(self): """Should still be able to print even if prog order is invalid.""" jfile = opj_data_file('input/nonregression/2977.pdf.asan.67.2198.jp2') with warnings.catch_warnings(): + # Multiple warnings, actually. warnings.simplefilter("ignore") jp2 = Jp2k(jfile) codestream = jp2.get_codestream() @@ -1058,10 +1069,7 @@ class TestPrintingOpjDataRoot(unittest.TestCase): def test_uuid(self): """verify printing of UUID box""" filename = opj_data_file('input/nonregression/text_GBR.jp2') - with warnings.catch_warnings(): - # brand is 'jp2 ', but has any icc profile. - warnings.simplefilter("ignore") - jp2 = Jp2k(filename) + jp2 = Jp2k(filename) with patch('sys.stdout', new=StringIO()) as fake_out: print(jp2.box[4])