def test_multiple_source_files(self): """ Verify invocations that cover multiple source files, linking to an executable. """ driver = self.make_driver() args = ['gcc', 'test-sources/multiple-1.c', 'test-sources/multiple-2.c', '-o', 'multiple.exe'] r = driver.invoke(args) self.assertEqual(driver.ctxt.stderr.getvalue(), '') self.assertEqual(driver.returncode, 0) # Verify that it wrote out "firehose" XML files to 'outputdir': analyses = [] for xmlpath in glob.glob(os.path.join(driver.outputdir, '*.xml')): with open(xmlpath) as f: analysis = Analysis.from_xml(f) analyses.append(analysis) # Verify that none of the side-effects failed (e.g. with # linker errors): self.assertEqual(analysis.customfields['returncode'], 0) os.unlink(xmlpath) self.assertEqual(len(analyses), 8) # We should have a ./multiple.exe that we can run self.assertTrue(os.path.exists('./multiple.exe')) os.unlink('./multiple.exe') os.rmdir(driver.outputdir)
def invoke(self, sourcefile, extraargs=None): driver = self.make_driver() args = ['gcc', '-c', sourcefile] if extraargs: args += extraargs r = driver.invoke(args) if sourcefile and os.path.exists(sourcefile): with open(sourcefile) as f: content = f.read() expected_hexdigest = hashlib.sha1(content).hexdigest() else: expected_hexdigest = None # Verify that it wrote out "firehose" XML files to 'outputdir': analyses = [] for xmlpath in glob.glob(os.path.join(driver.outputdir, '*.xml')): with open(xmlpath) as f: analysis = Analysis.from_xml(f) if 0: print(analysis) if expected_hexdigest: self.assertEqual(analysis.metadata.file_.hash_.alg, 'sha1') self.assertEqual(analysis.metadata.file_.hash_.hexdigest, expected_hexdigest) analyses.append(analysis) os.unlink(xmlpath) self.assertEqual(len(analyses), 4) os.rmdir(driver.outputdir) return driver
def parse_coccinelle(lines): # we have to emulate a file for from_xml() firehose = StringIO.StringIO(lines) analysis = Analysis.from_xml(firehose) for result in analysis.results: yield result
def invoke_side_effects(argv): log("invoke_side_effects: %s" % ' '.join(sys.argv)) gccinv = GccInvocation(argv) # Try to run each side effect in a subprocess, passing in a path # for the XML results to be written to. # Cover a multitude of possible failures by detecting if no output # was written, and capturing *that* as a failure for sourcefile in gccinv.sources: if sourcefile.endswith('.c'): # FIXME: other extensions? for script, genname in [('invoke-cppcheck', 'cppcheck'), ('invoke-clang-analyzer', 'clang-analyzer'), ('invoke-cpychecker', 'cpychecker'), # Uncomment the following to test a # checker that fails to write any XML: # ('echo', 'failing-checker'), ]: with tempfile.NamedTemporaryFile() as f: dstxmlpath = f.name assert not os.path.exists(dstxmlpath) # Restrict the invocation to just one source file at a # time: singleinv = gccinv.restrict_to_one_source(sourcefile) singleargv = singleinv.argv TIMEOUT=60 t = Timer() args = [script, dstxmlpath] + singleargv log('invoking args: %r' % args) p = Popen(args, stdout=PIPE, stderr=PIPE) try: out, err = p.communicate(timeout=TIMEOUT) write_streams(script, out, err) if os.path.exists(dstxmlpath): with open(dstxmlpath) as f: analysis = Analysis.from_xml(f) else: analysis = make_failed_analysis(genname, sourcefile, t, msgtext=('Unable to locate XML output from %s' % script), failureid='no-output-found') analysis.set_custom_field('stdout', out) analysis.set_custom_field('stderr', err) analysis.set_custom_field('returncode', p.returncode) except TimeoutExpired: analysis = make_failed_analysis(genname, sourcefile, t, msgtext='Timeout running %s' % genname, failureid='timeout') analysis.set_custom_field('timeout', TIMEOUT) analysis.set_custom_field('gcc-invocation', ' '.join(argv)) write_analysis_as_xml(analysis)
def test_from_xml(self): num_analyses = 0 for filename in sorted(glob.glob('examples/example-*.xml')): with open(filename) as f: r = Analysis.from_xml(f) num_analyses += 1 # Ensure that all of the reports were indeed parsed: self.assertEqual(num_analyses, 9)
def test_from_xml(self): num_analyses = 0 for filename in sorted(glob.glob('examples/example-*.xml')): with open(filename) as f: r = Analysis.from_xml(f) num_analyses += 1 # Ensure that all of the reports were indeed parsed: self.assertEqual(num_analyses, 10)
def get_labeled_reports(*exclude): results = [] firehose_reports = glob.glob(os.path.join('reports', 'firehose', 'labeled_reports', '*.xml')) for analyzer in exclude: if analyzer == 'scan-build': analyzer = 'clang-analyzer' firehose_reports.remove('reports/firehose/labeled_reports/%s.xml' % analyzer) for fh_xml_file in firehose_reports: results.append(Analysis.from_xml(fh_xml_file)) return results
def roundtrip_example(self, filename): fullpath = os.path.join('../firehose/examples', filename) # FIXME a = Analysis.from_xml(fullpath) self.session.add(a) self.session.commit() session2 = self.Session() b = session2.query(Analysis).first() #print(b) self.assertEqual(a, b)
def test_debian_source(self): """ Test to ensure that Debian source package Sut loading works. """ with open('examples/example-debian-source.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'handmade') self.assertEqual(a.metadata.generator.version, '0.1') self.assertIsInstance(a.metadata.sut, DebianSource) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.1+b1') self.assertFalse(hasattr(a.metadata.sut, 'buildarch'))
def test_debian_binary(self): """ Test to ensure that Debian binary package Sut loading works. """ with open('examples/example-debian-binary.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'handmade') self.assertEqual(a.metadata.generator.version, '0.1') self.assertIsInstance(a.metadata.sut, DebianBinary) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.buildarch, 'amd64') self.assertEqual(a.metadata.sut.release, '1.1')
def simple_tests(): ex = Analysis.from_xml('../firehose/examples/example-1.xml') print(ex) session.add(ex) print(ex.id) print(session.query(Analysis).first()) print(session.query(Failure).first()) print(ex.id) session.commit() # Do it from a new session: session2 = Session() print(session2.query(Analysis).first()) print(session2.query(Failure).first())
def test_example_6(self): with open('examples/example-6.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Failure) self.assertEqual(w.failureid, 'too-complicated') self.assertEqual(w.message.text, 'this function is too complicated for the' ' reference-count checker to fully analyze:' ' not all paths were analyzed') self.assertEqual(w.customfields, None)
def test_example_6(self): with open('examples/example-6.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Failure) self.assertEqual(w.failureid, 'too-complicated') self.assertEqual( w.message.text, 'this function is too complicated for the' ' reference-count checker to fully analyze:' ' not all paths were analyzed') self.assertEqual(w.customfields, None)
def test_example_2(self): # Verify that the parser works: with open('examples/example-2.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(a.metadata.generator.version, '0.11') self.assertIsInstance(a.metadata.sut, SourceRpm) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.fc19') self.assertEqual(a.metadata.sut.buildarch, 'x86_64') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Issue) self.assertEqual(w.cwe, 401) self.assertEqual(w.testid, 'refcount-too-high') self.assertEqual(w.location.file.givenpath, 'examples/python-src-example.c') self.assertEqual(w.location.file.abspath, None) self.assertEqual(w.location.file.hash_.alg, 'sha1') self.assertEqual(w.location.file.hash_.hexdigest, '6ba29daa94d64b48071e299a79f2a00dcd99eeb1') self.assertEqual(w.location.function.name, 'make_a_list_of_random_ints_badly') self.assertEqual(w.location.line, 40) self.assertEqual(w.location.column, 4) self.assertEqual(w.message.text, "ob_refcnt of '*item' is 1 too high") self.assertMultiLineEqual(w.notes.text, ( "was expecting final item->ob_refcnt to be N + 1 (for some unknown N)\n" "due to object being referenced by: PyListObject.ob_item[0]\n" "but final item->ob_refcnt is N + 2")) self.assertIsInstance(w.trace, Trace) self.assertEqual(len(w.trace.states), 3) s0 = w.trace.states[0] self.assertIsInstance(s0, State) self.assertEqual(s0.location.file.givenpath, 'examples/python-src-example.c') self.assertEqual(s0.location.function.name, 'make_a_list_of_random_ints_badly') self.assertEqual(s0.location.line, 36) self.assertEqual(s0.location.column, 14) self.assertEqual( s0.notes.text, 'PyLongObject allocated at: item = PyLong_FromLong(random());' )
def handle_output(self, result): if os.path.exists(self.outputxmlpath): with open(self.outputxmlpath) as f: analysis = Analysis.from_xml(f) analysis.metadata.file_ = make_file(result.sourcefile) analysis.metadata.stats = make_stats(result.timer) else: analysis = \ self._make_failed_analysis( result.sourcefile, result.timer, msgtext=('Unable to locate XML output from %s' % self.name), failureid='no-output-found') analysis.set_custom_field('cpychecker-invocation', ' '.join(result.argv)) result.set_custom_fields(analysis) return analysis
def test_example_3(self): # Verify that the parser works: with open('examples/example-3.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(a.metadata.generator.version, '0.11') self.assertIsInstance(a.metadata.sut, SourceRpm) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.fc19') self.assertEqual(a.metadata.sut.buildarch, 'x86_64') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Failure) self.assertEqual(w.failureid, 'bad-exit-code') self.assertEqual(w.customfields['returncode'], -11)
def test_example_5(self): # Ensure that we can load range information from XML with open('examples/example-5.xml') as f: a = Analysis.from_xml(f) self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Issue) self.assertEqual(w.location.range_.start.line, 10) self.assertEqual(w.location.range_.start.column, 9) self.assertEqual(w.location.range_.end.line, 10) self.assertEqual(w.location.range_.end.column, 44) self.assertEqual(w.location.point, None) # The line/column getters use the start: self.assertEqual(w.location.line, 10) self.assertEqual(w.location.column, 9)
def test_json_roundtrip(self): verbose = False def roundtrip_through_json(a): jsondict = a.to_json() if verbose: from pprint import pprint pprint(jsondict) return Analysis.from_json(jsondict) a1, w = self.make_simple_analysis() a2 = roundtrip_through_json(a1) self.assertEqual(a1.metadata, a2.metadata) self.assertEqual(a1.results, a2.results) self.assertEqual(a1, a2) a3, w = self.make_complex_analysis() a4 = roundtrip_through_json(a3) self.assertEqual(a3.metadata, a4.metadata) self.assertEqual(a3.results, a4.results) self.assertEqual(a3, a4) a5, f = self.make_failed_analysis() a6 = roundtrip_through_json(a5) self.assertEqual(a5.metadata, a6.metadata) self.assertEqual(a5.results, a6.results) self.assertEqual(a5, a6) a7, info = self.make_info() a8 = roundtrip_through_json(a7) self.assertEqual(a7.metadata, a8.metadata) self.assertEqual(a7.results, a8.results) self.assertEqual(a7, a8) a9 = Analysis.from_xml('examples/example-non-ascii.xml') a10 = roundtrip_through_json(a9) self.assertEqual(a9, a10)
def test_example_2(self): # Verify that the parser works: with open('examples/example-2.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(a.metadata.generator.version, '0.11') self.assertIsInstance(a.metadata.sut, SourceRpm) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.fc19') self.assertEqual(a.metadata.sut.buildarch, 'x86_64') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Issue) self.assertEqual(w.cwe, 401) self.assertEqual(w.testid, 'refcount-too-high') self.assertEqual(w.location.file.givenpath, 'examples/python-src-example.c') self.assertEqual(w.location.file.abspath, None) self.assertEqual(w.location.file.hash_.alg, 'sha1') self.assertEqual(w.location.file.hash_.hexdigest, '6ba29daa94d64b48071e299a79f2a00dcd99eeb1') self.assertEqual(w.location.function.name, 'make_a_list_of_random_ints_badly') self.assertEqual(w.location.line, 40) self.assertEqual(w.location.column, 4) self.assertEqual(w.message.text, "ob_refcnt of '*item' is 1 too high") self.assertMultiLineEqual(w.notes.text, ("was expecting final item->ob_refcnt to be N + 1 (for some unknown N)\n" "due to object being referenced by: PyListObject.ob_item[0]\n" "but final item->ob_refcnt is N + 2")) self.assertIsInstance(w.trace, Trace) self.assertEqual(len(w.trace.states), 3) s0 = w.trace.states[0] self.assertIsInstance(s0, State) self.assertEqual(s0.location.file.givenpath, 'examples/python-src-example.c') self.assertEqual(s0.location.function.name, 'make_a_list_of_random_ints_badly') self.assertEqual(s0.location.line, 36) self.assertEqual(s0.location.column, 14) self.assertEqual(s0.notes.text, 'PyLongObject allocated at: item = PyLong_FromLong(random());')
def test_non_ascii_example(self): with open('examples/example-non-ascii.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, u('\u2620') * 8) self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Issue) # Verify the Japanese version of # "comparison between signed and unsigned integer expressions" # within the message: self.assertEqual(w.message.text, (u('\u7b26\u53f7\u4ed8\u304d\u3068\u7b26\u53f7' '\u7121\u3057\u306e\u6574\u6570\u5f0f\u306e' '\u9593\u3067\u306e\u6bd4\u8f03\u3067\u3059'))) # Verify the "mojibake" Kanji/Hiragana within the notes: self.assertIn(u('\u6587\u5b57\u5316\u3051'), w.notes.text) self.assertEqual(w.location.function.name, u('oo\u025f'))
def test_example_4(self): with open('examples/example-4.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(a.metadata.generator.version, '0.11') self.assertIsInstance(a.metadata.sut, SourceRpm) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.fc19') self.assertEqual(a.metadata.sut.buildarch, 'x86_64') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Failure) self.assertEqual(w.failureid, 'python-exception') self.assertEqual(w.location.file.givenpath, 'wspy_register.c') self.assertEqual(w.location.function.name, 'register_all_py_protocols_func') self.assertEqual(w.location.line, 159) self.assertEqual(w.location.column, 42) self.assert_(w.customfields['traceback'].startswith('wspy_register.c: In function \'register_all_py_protocols_func\':\n'))
def test_json_roundtrip(self): def roundtrip_through_json(a): jsondict = a.to_json() from pprint import pprint pprint(jsondict) return Analysis.from_json(jsondict) a1, w = self.make_simple_analysis() a2 = roundtrip_through_json(a1) self.assertEqual(a1.metadata, a2.metadata) self.assertEqual(a1.results, a2.results) self.assertEqual(a1, a2) a3, w = self.make_complex_analysis() a4 = roundtrip_through_json(a3) self.assertEqual(a3.metadata, a4.metadata) self.assertEqual(a3.results, a4.results) self.assertEqual(a3, a4) a5, f = self.make_failed_analysis() a6 = roundtrip_through_json(a5) self.assertEqual(a5.metadata, a6.metadata) self.assertEqual(a5.results, a6.results) self.assertEqual(a5, a6) a7, info = self.make_info() a8 = roundtrip_through_json(a7) self.assertEqual(a7.metadata, a8.metadata) self.assertEqual(a7.results, a8.results) self.assertEqual(a7, a8) a9 = Analysis.from_xml('examples/example-non-ascii.xml') a10 = roundtrip_through_json(a9) self.assertEqual(a9, a10)
def test_xml_roundtrip(self): def roundtrip_through_xml(a): xmlbytes = a.to_xml_bytes() buf = BytesIO(xmlbytes) return Analysis.from_xml(buf) a1, w = self.make_simple_analysis() a2 = roundtrip_through_xml(a1) self.assertEqual(a1.metadata, a2.metadata) self.assertEqual(a1.results, a2.results) self.assertEqual(a1, a2) a3, w = self.make_complex_analysis() a4 = roundtrip_through_xml(a3) self.assertEqual(a3.metadata, a4.metadata) self.assertEqual(a3.results, a4.results) self.assertEqual(a3, a4) a5, f = self.make_failed_analysis() a6 = roundtrip_through_xml(a5) self.assertEqual(a5.metadata, a6.metadata) self.assertEqual(a5.results, a6.results) self.assertEqual(a5, a6) a7, info = self.make_info() a8 = roundtrip_through_xml(a7) self.assertEqual(a7.metadata, a8.metadata) self.assertEqual(a7.results, a8.results) self.assertEqual(a7, a8) a9 = Analysis.from_xml('examples/example-non-ascii.xml') a10 = roundtrip_through_xml(a9) self.assertEqual(a9, a10)
def test_example_4(self): with open('examples/example-4.xml') as f: a = Analysis.from_xml(f) self.assertEqual(a.metadata.generator.name, 'cpychecker') self.assertEqual(a.metadata.generator.version, '0.11') self.assertIsInstance(a.metadata.sut, SourceRpm) self.assertEqual(a.metadata.sut.name, 'python-ethtool') self.assertEqual(a.metadata.sut.version, '0.7') self.assertEqual(a.metadata.sut.release, '4.fc19') self.assertEqual(a.metadata.sut.buildarch, 'x86_64') self.assertEqual(len(a.results), 1) w = a.results[0] self.assertIsInstance(w, Failure) self.assertEqual(w.failureid, 'python-exception') self.assertEqual(w.location.file.givenpath, 'wspy_register.c') self.assertEqual(w.location.function.name, 'register_all_py_protocols_func') self.assertEqual(w.location.line, 159) self.assertEqual(w.location.column, 42) self.assertTrue(w.customfields['traceback'].startswith( 'wspy_register.c: In function \'register_all_py_protocols_func\':\n' ))
def roundtrip_through_xml(a): xmlbytes = a.to_xml_bytes() buf = BytesIO(xmlbytes) return Analysis.from_xml(buf)
def parse_xml_bytes(self, xmlbytes): f = BytesIO(xmlbytes) a = Analysis.from_xml(f) f.close() return a
def get_analyses(self): analyses = [] for filename in glob.glob(os.path.join(self.get_reports_dir(), '*.xml')): r = Analysis.from_xml(filename) analyses.append( (filename, r) ) return analyses
def get_reports(): results = [] for fh_xml_file in glob.glob(os.path.join('reports', 'firehose', '*.xml')): results.append(Analysis.from_xml(fh_xml_file)) return results
def get_analyses(mockdir): analyses = [] for filename in glob.glob(os.path.join(mockdir, 'reports', '*.xml')): r = Analysis.from_xml(filename) analyses.append( (filename, r) ) return analyses