def test_ctor_arguments(self): '''non-default constructor arguments.''' pr = problem_report.ProblemReport('KernelCrash') self.assertEqual(pr['ProblemType'], 'KernelCrash') pr = problem_report.ProblemReport(date='19801224 12:34') self.assertEqual(pr['Date'], '19801224 12:34')
def test_size_limit(self): '''writing and a big random file with a size limit key.''' # create 1 MB random file temp = tempfile.NamedTemporaryFile() data = os.urandom(1048576) temp.write(data) temp.flush() # write it into problem report pr = problem_report.ProblemReport() pr['FileSmallLimit'] = (temp.name, True, 100) pr['FileLimitMinus1'] = (temp.name, True, 1048575) pr['FileExactLimit'] = (temp.name, True, 1048576) pr['FileLimitPlus1'] = (temp.name, True, 1048577) pr['FileLimitNone'] = (temp.name, True, None) pr['Before'] = 'xtestx' pr['ZAfter'] = 'ytesty' io = BytesIO() pr.write(io) temp.close() # read it again io.seek(0) pr = problem_report.ProblemReport() pr.load(io) self.assertFalse('FileSmallLimit' in pr) self.assertFalse('FileLimitMinus1' in pr) self.assertTrue(pr['FileExactLimit'] == data) self.assertTrue(pr['FileLimitPlus1'] == data) self.assertTrue(pr['FileLimitNone'] == data) self.assertEqual(pr['Before'], 'xtestx') self.assertEqual(pr['ZAfter'], 'ytesty')
def test_write_delayed_fileobj(self): '''writing a report with file pointers and delayed data.''' (fout, fin) = os.pipe() if os.fork() == 0: os.close(fout) time.sleep(0.3) os.write(fin, b'ab' * 512 * 1024) time.sleep(0.3) os.write(fin, b'hello') time.sleep(0.3) os.write(fin, b' world') os.close(fin) os._exit(0) os.close(fin) pr = problem_report.ProblemReport(date='now!') io = BytesIO() with os.fdopen(fout, 'rb') as f: pr['BinFile'] = (f, ) pr.write(io) assert os.wait()[1] == 0 io.seek(0) pr2 = problem_report.ProblemReport() pr2.load(io) self.assertEqual(pr2['BinFile'], 'ab' * 512 * 1024 + 'hello world')
def test_write_file(self): '''writing a report with binary file data.''' temp = tempfile.NamedTemporaryFile() temp.write(bin_data) temp.flush() pr = problem_report.ProblemReport(date='now!') pr['File'] = (temp.name, ) pr['Afile'] = (temp.name, ) io = BytesIO() pr.write(io) temp.close() self.assertEqual( io.getvalue(), b'''ProblemType: Crash Date: now! Afile: base64 H4sICAAAAAAC/0FmaWxlAA== c3RyhEIGBoYoRiYAM5XUCxAAAAA= File: base64 H4sICAAAAAAC/0ZpbGUA c3RyhEIGBoYoRiYAM5XUCxAAAAA= ''') # force compression/encoding bool temp = tempfile.NamedTemporaryFile() temp.write(b'foo\0bar') temp.flush() pr = problem_report.ProblemReport(date='now!') pr['File'] = (temp.name, False) io = BytesIO() pr.write(io) self.assertEqual(io.getvalue(), b'''ProblemType: Crash Date: now! File: foo\0bar ''') pr['File'] = (temp.name, True) io = BytesIO() pr.write(io) self.assertEqual( io.getvalue(), b'''ProblemType: Crash Date: now! File: base64 H4sICAAAAAAC/0ZpbGUA S8vPZ0hKLAIACq50HgcAAAA= ''') temp.close()
def test_write_empty_fileobj(self): '''writing a report with a pointer to a file-like object with enforcing non-emptyness.''' tempbin = BytesIO(b'') tempasc = BytesIO(b'') pr = problem_report.ProblemReport(date='now!') pr['BinFile'] = (tempbin, True, None, True) io = BytesIO() self.assertRaises(IOError, pr.write, io) pr = problem_report.ProblemReport(date='now!') pr['AscFile'] = (tempasc, False, None, True) io = BytesIO() self.assertRaises(IOError, pr.write, io)
def test_no_argv(self): '''with zapped sys.argv.''' self._test_crash('import sys\nsys.argv = None') # did we get a report? reports = apport.fileutils.get_new_reports() pr = None try: self.assertEqual(len(reports), 1, 'crashed Python program produced a report') self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode), 0600, 'report has correct permissions') pr = problem_report.ProblemReport() pr.load(open(reports[0])) finally: for r in reports: os.unlink(r) # check report contents expected_keys = [ 'InterpreterPath', 'Traceback', 'ProblemType', 'ProcEnviron', 'ProcStatus', 'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps', 'UserGroups' ] self.assert_( set(expected_keys).issubset(set(pr.keys())), 'report has necessary fields') self.assert_('bin/python' in pr['InterpreterPath']) self.assert_(pr['Traceback'].startswith('Traceback'))
def test_general(self): '''general operation of the Python crash hook.''' script = self._test_crash() # did we get a report? reports = apport.fileutils.get_new_reports() pr = None self.assertEqual(len(reports), 1, 'crashed Python program produced a report') self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode), 0o640, 'report has correct permissions') pr = problem_report.ProblemReport() with open(reports[0], 'rb') as f: pr.load(f) # check report contents expected_keys = ['InterpreterPath', 'PythonArgs', 'Traceback', 'ProblemType', 'ProcEnviron', 'ProcStatus', 'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps', 'UserGroups'] self.assertTrue(set(expected_keys).issubset(set(pr.keys())), 'report has necessary fields') self.assertTrue('bin/python' in pr['InterpreterPath']) self.assertEqual(pr['ExecutablePath'], script) self.assertEqual(pr['ExecutableTimestamp'], str(int(os.stat(script).st_mtime))) self.assertEqual(pr['PythonArgs'], "['%s', 'testarg1', 'testarg2']" % script) self.assertTrue(pr['Traceback'].startswith('Traceback')) self.assertTrue("func\n raise Exception(b'This should happen." in pr['Traceback'], pr['Traceback'])
def test_no_argv(self): '''with zapped sys.argv.''' self._test_crash('import sys\nsys.argv = None') # did we get a report? reports = apport.fileutils.get_new_reports() pr = None self.assertEqual(len(reports), 1, 'crashed Python program produced a report') self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode), 0o640, 'report has correct permissions') pr = problem_report.ProblemReport() with open(reports[0], 'rb') as f: pr.load(f) # check report contents expected_keys = [ 'InterpreterPath', 'Traceback', 'ProblemType', 'ProcEnviron', 'ProcStatus', 'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps', 'UserGroups' ] self.assertTrue( set(expected_keys).issubset(set(pr.keys())), 'report has necessary fields') self.assertTrue('bin/python' in pr['InterpreterPath']) # we have no actual executable, so we should fall back to the # interpreter self.assertEqual(pr['ExecutablePath'], pr['InterpreterPath']) if 'ExecutableTimestamp' in pr: self.assertEqual(pr['ExecutableTimestamp'], str(int(os.stat(pr['ExecutablePath']).st_mtime))) self.assertTrue(pr['Traceback'].startswith('Traceback'))
def test_make_report_file(self): '''make_report_file()''' pr = problem_report.ProblemReport() self.assertRaises(ValueError, apport.fileutils.make_report_file, pr) pr['Package'] = 'bash 1' with apport.fileutils.make_report_file(pr) as f: if sys.version >= '3': path = f.name else: path = os.path.join(apport.fileutils.report_dir, os.listdir(apport.fileutils.report_dir)[0]) self.assertTrue( path.startswith('%s/bash' % apport.fileutils.report_dir), path) os.unlink(path) pr['ExecutablePath'] = '/bin/bash' with apport.fileutils.make_report_file(pr) as f: if sys.version >= '3': path = f.name else: path = os.path.join(apport.fileutils.report_dir, os.listdir(apport.fileutils.report_dir)[0]) self.assertTrue( path.startswith('%s/_bin_bash' % apport.fileutils.report_dir), path) # file exists already, should fail now self.assertRaises(OSError, apport.fileutils.make_report_file, pr) # should still fail if it's a dangling symlink os.unlink(path) os.symlink(os.path.join(apport.fileutils.report_dir, 'pwned'), path) self.assertRaises(OSError, apport.fileutils.make_report_file, pr)
def test_read_file_legacy(self): '''reading a report with binary data in legacy format without gzip header.''' bin_report = b'''ProblemType: Crash Date: now! File: base64 eJw= c3RyxIAMcBAFAG55BXk= Foo: Bar ''' # test with reading everything pr = problem_report.ProblemReport() pr.load(BytesIO(bin_report)) self.assertEqual(pr['File'], b'AB' * 10 + b'\0' * 10 + b'Z') self.assertEqual(pr.has_removed_fields(), False) # test with skipping binary data pr.load(BytesIO(bin_report), binary=False) self.assertEqual(pr['File'], '') self.assertEqual(pr.has_removed_fields(), True) # test with keeping CompressedValues pr.load(BytesIO(bin_report), binary='compressed') self.assertEqual(pr.has_removed_fields(), False) self.assertEqual(len(pr['File']), 31) self.assertEqual(pr['File'].get_value(), b'AB' * 10 + b'\0' * 10 + b'Z') io = BytesIO() pr['File'].write(io) io.seek(0) self.assertEqual(io.read(), b'AB' * 10 + b'\0' * 10 + b'Z')
def test_write_mime_extra_headers(self): '''write_mime() with extra headers.''' pr = problem_report.ProblemReport(date='now!') pr['Simple'] = 'bar' pr['TwoLine'] = 'first\nsecond\n' io = BytesIO() pr.write_mime(io, extra_headers={ 'Greeting': 'hello world', 'Foo': 'Bar' }) io.seek(0) msg = email.message_from_binary_file(io) self.assertEqual(msg['Greeting'], 'hello world') self.assertEqual(msg['Foo'], 'Bar') parts = [p for p in msg.walk()] self.assertEqual(len(parts), 2) # first part is the multipart container self.assertTrue(parts[0].is_multipart()) # second part should be an inline text/plain attachments with all short # fields self.assertTrue(not parts[1].is_multipart()) self.assertEqual(parts[1].get_content_type(), 'text/plain') self.assertTrue(b'Simple: bar' in parts[1].get_payload(decode=True))
def test_read_file(self): '''reading a report with binary data.''' bin_report = b'''ProblemType: Crash Date: now! File: base64 H4sICAAAAAAC/0ZpbGUA c3RyhEIGBoYoRiYAM5XUCxAAAAA= Foo: Bar ''' # test with reading everything pr = problem_report.ProblemReport() pr.load(BytesIO(bin_report)) self.assertEqual(pr['File'], bin_data) self.assertEqual(pr.has_removed_fields(), False) # test with skipping binary data pr.load(BytesIO(bin_report), binary=False) self.assertEqual(pr['File'], '') self.assertEqual(pr.has_removed_fields(), True) # test with keeping compressed binary data pr.load(BytesIO(bin_report), binary='compressed') self.assertEqual(pr['Foo'], 'Bar') self.assertEqual(pr.has_removed_fields(), False) self.assertTrue(isinstance(pr['File'], problem_report.CompressedValue)) self.assertEqual(len(pr['File']), len(bin_data)) self.assertEqual(pr['File'].get_value(), bin_data)
def test_write_fileobj(self): '''writing a report with a pointer to a file-like object.''' tempbin = BytesIO(bin_data) tempasc = BytesIO(b'Hello World') pr = problem_report.ProblemReport(date='now!') pr['BinFile'] = (tempbin, ) pr['AscFile'] = (tempasc, False) io = BytesIO() pr.write(io) io.seek(0) pr = problem_report.ProblemReport() pr.load(io) self.assertEqual(pr['BinFile'], tempbin.getvalue()) self.assertEqual(pr['AscFile'], tempasc.getvalue().decode())
def test_write_append(self): '''write() with appending to an existing file.''' pr = problem_report.ProblemReport(date='now!') pr['Simple'] = 'bar' pr['WhiteSpace'] = ' foo bar\nbaz\n blip ' io = BytesIO() pr.write(io) pr.clear() pr['Extra'] = 'appended' pr.write(io) self.assertEqual( io.getvalue(), b'''ProblemType: Crash Date: now! Simple: bar WhiteSpace: foo bar baz blip Extra: appended ''') temp = tempfile.NamedTemporaryFile() temp.write(bin_data) temp.flush() pr = problem_report.ProblemReport(date='now!') pr['File'] = (temp.name, ) io = BytesIO() pr.write(io) temp.close() pr.clear() pr['Extra'] = 'appended' pr.write(io) io.seek(0) pr = problem_report.ProblemReport() pr.load(io) self.assertEqual(pr['Date'], 'now!') self.assertEqual(pr['File'], bin_data) self.assertEqual(pr['Extra'], 'appended')
def _load_report(self): '''Ensure that there is exactly one crash report and load it''' reports = apport.fileutils.get_new_reports() self.assertEqual(len(reports), 1, 'crashed Python program produced a report') pr = problem_report.ProblemReport() with open(reports[0], 'rb') as f: pr.load(f) return pr
def test_mark_hanging_process(self): '''mark_hanging_process()''' pr = problem_report.ProblemReport() pr['ExecutablePath'] = '/bin/bash' apport.fileutils.mark_hanging_process(pr, '1') uid = str(os.getuid()) base = '_bin_bash.%s.1.hanging' % uid expected = os.path.join(apport.fileutils.report_dir, base) self.assertTrue(os.path.exists(expected))
def test_sanity_checks(self): '''various error conditions.''' pr = problem_report.ProblemReport() self.assertRaises(AssertionError, pr.__setitem__, 'a b', '1') self.assertRaises(AssertionError, pr.__setitem__, 'a', 1) self.assertRaises(AssertionError, pr.__setitem__, 'a', 1) self.assertRaises(AssertionError, pr.__setitem__, 'a', (1, )) self.assertRaises(AssertionError, pr.__setitem__, 'a', ('/tmp/nonexistant', '')) self.assertRaises(KeyError, pr.__getitem__, 'Nonexistant')
def test_extract_keys(self): '''extract_keys() with various binary elements.''' # create a test report with binary elements large_val = b'A' * 5000000 pr = problem_report.ProblemReport() pr['Txt'] = 'some text' pr['MoreTxt'] = 'some more text' pr['Foo'] = problem_report.CompressedValue(b'FooFoo!') pr['Uncompressed'] = bin_data pr['Bin'] = problem_report.CompressedValue() pr['Bin'].set_value(bin_data) pr['Large'] = problem_report.CompressedValue(large_val) pr['Multiline'] = problem_report.CompressedValue( b'\1\1\1\n\2\2\n\3\3\3') report = BytesIO() pr.write(report) report.seek(0) self.assertRaises(IOError, pr.extract_keys, report, 'Bin', os.path.join(self.workdir, 'nonexistant')) # Test exception handling: Non-binary and nonexistent key tests = [(ValueError, 'Txt'), (ValueError, ['Foo', 'Txt']), (KeyError, 'Bar'), (KeyError, ['Foo', 'Bar'])] for exc, keys_arg in tests: report.seek(0) self.assertRaises(exc, pr.extract_keys, report, keys_arg, self.workdir) # Check valid single elements tests = { 'Foo': b'FooFoo!', 'Uncompressed': bin_data, 'Bin': bin_data, 'Large': large_val, 'Multiline': b'\1\1\1\n\2\2\n\3\3\3' } for key, expected in tests.items(): report.seek(0) pr.extract_keys(report, key, self.workdir) with open(os.path.join(self.workdir, key), 'rb') as f: self.assertEqual(f.read(), expected) # remove file for next pass os.remove(os.path.join(self.workdir, key)) # Check element list report.seek(0) tests = {'Foo': b'FooFoo!', 'Uncompressed': bin_data} pr.extract_keys(report, tests.keys(), self.workdir) for key, expected in tests.items(): with open(os.path.join(self.workdir, key), 'rb') as f: self.assertEqual(f.read(), expected)
def test_compressed_values(self): '''handling of CompressedValue values.''' large_val = b'A' * 5000000 pr = problem_report.ProblemReport() pr['Foo'] = problem_report.CompressedValue(b'FooFoo!') pr['Bin'] = problem_report.CompressedValue() pr['Bin'].set_value(bin_data) pr['Large'] = problem_report.CompressedValue(large_val) self.assertTrue(isinstance(pr['Foo'], problem_report.CompressedValue)) self.assertTrue(isinstance(pr['Bin'], problem_report.CompressedValue)) self.assertEqual(pr['Foo'].get_value(), b'FooFoo!') self.assertEqual(pr['Bin'].get_value(), bin_data) self.assertEqual(pr['Large'].get_value(), large_val) self.assertEqual(len(pr['Foo']), 7) self.assertEqual(len(pr['Bin']), len(bin_data)) self.assertEqual(len(pr['Large']), len(large_val)) io = BytesIO() pr['Bin'].write(io) self.assertEqual(io.getvalue(), bin_data) io = BytesIO() pr['Large'].write(io) self.assertEqual(io.getvalue(), large_val) pr['Multiline'] = problem_report.CompressedValue( b'\1\1\1\n\2\2\n\3\3\3') self.assertEqual(pr['Multiline'].splitlines(), [b'\1\1\1', b'\2\2', b'\3\3\3']) # test writing of reports with CompressedValues io = BytesIO() pr.write(io) io.seek(0) pr = problem_report.ProblemReport() pr.load(io) self.assertEqual(pr['Foo'], 'FooFoo!') self.assertEqual(pr['Bin'], bin_data) self.assertEqual(pr['Large'], large_val.decode('ASCII'))
def test_sanity_checks(self): '''various error conditions.''' pr = problem_report.ProblemReport() self.assertRaises(ValueError, pr.__setitem__, 'a b', '1') self.assertRaises(TypeError, pr.__setitem__, 'a', 1) self.assertRaises(TypeError, pr.__setitem__, 'a', (1, )) self.assertRaises(TypeError, pr.__setitem__, 'a', ('/tmp/nonexistant', '')) self.assertRaises(TypeError, pr.__setitem__, 'a', ('/tmp/nonexistant', False, 0, True, 'bogus')) self.assertRaises(TypeError, pr.__setitem__, 'a', ['/tmp/nonexistant']) self.assertRaises(KeyError, pr.__getitem__, 'Nonexistant')
def test_iter(self): '''problem_report.ProblemReport iteration.''' pr = problem_report.ProblemReport() pr['foo'] = 'bar' keys = [] for k in pr: keys.append(k) keys.sort() self.assertEqual(' '.join(keys), 'Date ProblemType foo') self.assertEqual(len([k for k in pr if k != 'foo']), 2)
def test_make_report_path(self): '''make_report_path()''' pr = problem_report.ProblemReport() self.assertRaises(ValueError, apport.fileutils.make_report_path, pr) pr['Package'] = 'bash 1' self.assertTrue( apport.fileutils.make_report_path(pr).startswith( '%s/bash' % apport.fileutils.report_dir)) pr['ExecutablePath'] = '/bin/bash' self.assertTrue( apport.fileutils.make_report_path(pr).startswith( '%s/_bin_bash' % apport.fileutils.report_dir))
def test_big_file(self): '''writing and re-decoding a big random file.''' # create 1 MB random file temp = tempfile.NamedTemporaryFile() data = os.urandom(1048576) temp.write(data) temp.flush() # write it into problem report pr = problem_report.ProblemReport() pr['File'] = (temp.name, ) pr['Before'] = 'xtestx' pr['ZAfter'] = 'ytesty' io = BytesIO() pr.write(io) temp.close() # read it again io.seek(0) pr = problem_report.ProblemReport() pr.load(io) self.assertTrue(pr['File'] == data) self.assertEqual(pr['Before'], 'xtestx') self.assertEqual(pr['ZAfter'], 'ytesty') # write it again io2 = BytesIO() pr.write(io2) self.assertTrue(io.getvalue() == io2.getvalue()) # check gzip compatibility io.seek(0) pr = problem_report.ProblemReport() pr.load(io, binary='compressed') self.assertEqual(pr['File'].get_value(), data)
def test_basic_operations(self): '''basic creation and operation.''' pr = problem_report.ProblemReport() pr['foo'] = 'bar' pr['bar'] = ' foo bar\nbaz\n blip ' pr['dash-key'] = '1' pr['dot.key'] = '1' pr['underscore_key'] = '1' self.assertEqual(pr['foo'], 'bar') self.assertEqual(pr['bar'], ' foo bar\nbaz\n blip ') self.assertEqual(pr['ProblemType'], 'Crash') self.assertTrue(time.strptime(pr['Date'])) self.assertEqual(pr['dash-key'], '1') self.assertEqual(pr['dot.key'], '1') self.assertEqual(pr['underscore_key'], '1')
def setUpClass(klass): klass.workdir = tempfile.mkdtemp() # create problem report file with all possible data types r = problem_report.ProblemReport() klass.utf8_str = b'a\xe2\x99\xa5b' klass.bindata = b'\x00\x01\xFF\x40' r['utf8'] = klass.utf8_str r['unicode'] = klass.utf8_str.decode('UTF-8') r['binary'] = klass.bindata r['compressed'] = problem_report.CompressedValue(b'FooFoo!') klass.report_file = os.path.join(klass.workdir, 'test.apport') with open(klass.report_file, 'wb') as f: r.write(f) klass.unpack_dir = os.path.join(klass.workdir, 'un pack')
def test_load_key_filter(self): '''load a report with filtering keys.''' io = BytesIO(b'''ProblemType: Crash DataNo: nonono GoodFile: base64 H4sICAAAAAAC/0FmaWxlAA== c3RyhEIGBoYoRiYAM5XUCxAAAAA= DataYes: yesyes BadFile: base64 H4sICAAAAAAC/0ZpbGUA S8vPZ0hKLAIACq50HgcAAAA= ''') pr = problem_report.ProblemReport() pr.load(io, key_filter=['DataYes', 'GoodFile']) self.assertEqual(pr['DataYes'], 'yesyes') self.assertEqual(pr['GoodFile'], bin_data) self.assertEqual(sorted(pr.keys()), ['DataYes', 'GoodFile'])
def test_python_env(self): '''Python environmental variables appear in report''' self._test_crash() # did we get a report? reports = apport.fileutils.get_new_reports() pr = None self.assertEqual(len(reports), 1, 'crashed Python program produced a report') pr = problem_report.ProblemReport() with open(reports[0], 'rb') as f: pr.load(f) # check report contents self.assertTrue('PYTHONPATH' in pr['ProcEnviron'], 'report contains PYTHONPATH') self.assertTrue('/my/bogus/path' in pr['ProcEnviron'], pr['ProcEnviron'])
def test_import_dict(self): '''importing a dictionary with update().''' pr = problem_report.ProblemReport() pr['oldtext'] = 'Hello world' pr['oldbin'] = bin_data pr['overwrite'] = 'I am crap' d = {} d['newtext'] = 'Goodbye world' d['newbin'] = '11\000\001\002\xFFZZ' d['overwrite'] = 'I am good' pr.update(d) self.assertEqual(pr['oldtext'], 'Hello world') self.assertEqual(pr['oldbin'], bin_data) self.assertEqual(pr['newtext'], 'Goodbye world') self.assertEqual(pr['newbin'], '11\000\001\002\xFFZZ') self.assertEqual(pr['overwrite'], 'I am good')
def test_write(self): '''write() and proper formatting.''' pr = problem_report.ProblemReport(date='now!') pr['Simple'] = 'bar' if sys.version.startswith('2'): pr['SimpleUTF8'] = '1äö2Φ3' pr['SimpleUnicode'] = '1äö2Φ3'.decode('UTF-8') pr['TwoLineUnicode'] = 'pi-π\nnu-η'.decode('UTF-8') pr['TwoLineUTF8'] = 'pi-π\nnu-η' else: pr['SimpleUTF8'] = '1äö2Φ3'.encode('UTF-8') pr['SimpleUnicode'] = '1äö2Φ3' pr['TwoLineUnicode'] = 'pi-π\nnu-η' pr['TwoLineUTF8'] = 'pi-π\nnu-η'.encode('UTF-8') pr['WhiteSpace'] = ' foo bar\nbaz\n blip \n\nafteremptyline' # Unicode with a non-space low ASCII character \x05 in it pr['UnprintableUnicode'] = b'a\xc3\xa4\x05z1\xc3\xa9'.decode('UTF-8') io = BytesIO() pr.write(io) expected = '''ProblemType: Crash Date: now! Simple: bar SimpleUTF8: 1äö2Φ3 SimpleUnicode: 1äö2Φ3 TwoLineUTF8: pi-π nu-η TwoLineUnicode: pi-π nu-η UnprintableUnicode: aä\x05z1é WhiteSpace: foo bar baz blip afteremptyline ''' if sys.version > '3': expected = expected.encode('UTF-8') self.assertEqual(io.getvalue(), expected)
def test_updating(self): '''new_keys() and write() with only_new=True.''' pr = problem_report.ProblemReport() self.assertEqual(pr.new_keys(), set(['ProblemType', 'Date'])) pr.load( BytesIO(b'''ProblemType: Crash Date: now! Foo: bar Baz: blob ''')) self.assertEqual(pr.new_keys(), set()) pr['Foo'] = 'changed' pr['NewKey'] = 'new new' self.assertEqual(pr.new_keys(), set(['NewKey'])) out = BytesIO() pr.write(out, only_new=True) self.assertEqual(out.getvalue(), b'NewKey: new new\n')