def make_html(modelA, modelB, f): sh = SourceHighlighter() # Approach: find peer files: sourcesA = modelA.get_source_files() sourcesB = modelB.get_source_files() #pprint(sourcesA) #pprint(sourcesB) # how to match? givenpath may be good enough for our example sourcesA_by_internal_path = {} for fileA in sourcesA: sourcesA_by_internal_path[get_internal_filename(fileA)] = fileA sourcesB_by_internal_path = {} for fileB in sourcesB: sourcesB_by_internal_path[get_internal_filename(fileB)] = fileB internal_paths = set(sourcesA_by_internal_path.keys() + sourcesB_by_internal_path.keys()) sutA = list(modelA.iter_analyses())[0].metadata.sut sutB = list(modelB.iter_analyses())[0].metadata.sut title = '%s - comparison view' % sutA.name f.write('<html>\n') write_common_meta(f) f.write('<head><title>%s</title>\n' % title) f.write(' <style type="text/css">\n') write_common_css(f) f.write(sh.formatter.get_style_defs()) f.write(' </style>\n') f.write('</head>\n') f.write(' <body>\n') generatorsA = modelA.get_generators() generatorsB = modelB.get_generators() generators = sorted(set(generatorsA + generatorsB)) aisA_by_source_and_generator = modelA.get_analysis_issues_by_source_and_generator() aisB_by_source_and_generator = modelB.get_analysis_issues_by_source_and_generator() afsA_by_source = modelA.get_analysis_failures_by_source() afsB_by_source = modelB.get_analysis_failures_by_source() f.write('<p>Old build: <b>%s</b></p>' % sutA) f.write('<p>New build: <b>%s</b></p>' % sutB) f.write(' <table>\n') if 1: f.write(' <tr>\n') f.write(' <th>Old file</th>\n') f.write(' <th>New file</th>\n') for generator in generators: f.write(' <th>%s</th>\n' % generator.name) f.write(' <th>Notes</th>\n') f.write(' </tr>\n') for internal_path in sorted(internal_paths): fileA = sourcesA_by_internal_path.get(internal_path, None) fileB = sourcesB_by_internal_path.get(internal_path, None) f.write(' <tr>\n') if fileA: f.write(' <td><a href="#file-%s">%s</a></td>\n' % (fileA.hash_.hexdigest, get_filename(fileA))) else: f.write(' <td></td>\n') if fileB: f.write(' <td><a href="#file-%s">%s</a></td>\n' % (fileB.hash_.hexdigest, get_filename(fileB))) else: f.write(' <td></td>\n') for generator in generators: keyA = (fileA, generator) aisA = aisA_by_source_and_generator.get(keyA, set()) keyB = (fileB, generator) aisB = aisB_by_source_and_generator.get(keyB, set()) class_ = 'has_issues' if aisA or aisB else 'no_issues' f.write(' <td class="%s">%s / %s</td>\n' % (class_, len(aisA), len(aisB))) afsA = afsA_by_source.get(fileA, []) afsB = afsB_by_source.get(fileB, []) if afsA or afsB: f.write(' <td>Incomplete coverage: old has %i analysis failure(s), new has %i analysis failure(s)</td>\n' % (len(afsA), len(afsB))) else: f.write(' <td></td>\n') f.write(' </tr>\n') f.write(' </table>\n') for internal_path in sorted(internal_paths): fileA = sourcesA_by_internal_path.get(internal_path, None) fileB = sourcesB_by_internal_path.get(internal_path, None) aisA = modelA.get_analysis_issues_by_source().get(fileA, set()) aisB = modelB.get_analysis_issues_by_source().get(fileB, set()) afsA = afsA_by_source.get(fileA, []) afsB = afsB_by_source.get(fileB, []) if fileA is not None: f.write('<a id="file-%s"/>' % fileA.hash_.hexdigest) if fileB is not None: f.write('<a id="file-%s"/>' % fileB.hash_.hexdigest) f.write('<h1>Comparison of old/new %s</h1>\n' % get_internal_filename(fileA)) else: f.write('<h1>Removed file: %s</h1>\n' % get_internal_filename(fileA)) else: assert fileB is not None f.write('<a id="file-%s"/>' % fileB.hash_.hexdigest) f.write('<h1>Added file: %s</h1>\n' % get_internal_filename(fileB)) ci = ComparativeIssues(aisA, aisB) if ci.new: f.write('<h2>New issues</h2>') write_issue_table_for_file(f, fileB, ci.new) if ci.fixed: f.write('<h2>Fixed issues</h2>') write_issue_table_for_file(f, fileA, ci.fixed) if ci.inboth: f.write('<h2>Issues in both old/new</h2>') f.write(' <table>\n') f.write(' <tr>\n') f.write(' <th>Old location</th>\n') f.write(' <th>New location</th>\n') f.write(' <th>Tool</th>\n') f.write(' <th>Test ID</th>\n') f.write(' <th>Function</th>\n') f.write(' <th>Issue</th>\n') f.write(' </tr>\n') for aiA, aiB in sorted(ci.inboth, lambda ab1, ab2: AnalysisIssue.cmp(ab1[1], ab2[1])): f.write(' <tr>\n') f.write(' <td>%s:%i:%i</td>\n' % (aiA.givenpath, aiA.line, aiA.column)) f.write(' <td>%s:%i:%i</td>\n' % (aiB.givenpath, aiB.line, aiB.column)) f.write(' <td>%s</td>\n' % aiB.generator.name) f.write(' <td>%s</td>\n' % (aiB.testid if aiB.testid else '')) f.write(' <td>%s</td>\n' % (aiB.function.name if aiB.function else '')), f.write(' <td><a href="%s">%s</a></td>\n' % ('#file-%s-line-%i' % (fileB.hash_.hexdigest, aiB.line), aiB.message.text)) f.write(' </tr>\n') f.write(' </table>\n') cf = ComparativeFailures(afsA, afsB) if cf.new: f.write('<h2>New failures</h2>') write_failure_table_for_file(f, fileB, cf.new) if cf.fixed: f.write('<h2>Fixed failures</h2>') write_failure_table_for_file(f, fileA, cf.fixed) if cf.inboth: f.write('<h2>Failures in both old/new</h2>') f.write(' <table>\n') f.write(' <tr>\n') f.write(' <th>Tool</th>\n') f.write(' <th>Failure ID</th>\n') f.write(' <th>Old location</th>\n') f.write(' <th>New location</th>\n') f.write(' <th>Function</th>\n') f.write(' <th>Message</th>\n') f.write(' <th>Data</th>\n') f.write(' </tr>\n') for afA, afB in sorted(cf.inboth, lambda ab1, ab2: AnalysisFailure.cmp(ab1[1], ab2[1])): f.write(' <tr>\n') f.write(' <td>%s</td>\n' % afB.generator.name) f.write(' <td>%s</td>\n' % afB.failureid) f.write(' <td>%s:%i:%i</td>\n' % (afA.givenpath, afA.line, afA.column)) f.write(' <td>%s:%i:%i</td>\n' % (afB.givenpath, afB.line, afB.column)) f.write(' <td>%s</td>\n' % (afB.function.name if afB.function else '')), f.write(' <td><a href="%s">%s</a></td>\n' % ('#file-%s-line-%i' % (fileB.hash_.hexdigest, afB.line), html_escape(str(afB.message)))) f.write(' <td>%s</td>\n' % (html_escape(afB.customfields)) if afB.customfields else '') f.write(' </tr>\n') f.write(' </table>\n') write_html_diff(f, modelA, modelB, fileA, fileB, aisA, aisB, afsA, afsB, sh) f.write(' </body>\n') f.write('</html>\n')
def make_html(model, f): sh = SourceHighlighter() analyses = list(model.iter_analyses()) title = '' f.write('<html>\n') write_common_meta(f) f.write('<head><title>%s</title>\n' % title) f.write(' <style type="text/css">\n') write_common_css(f) f.write(sh.formatter.get_style_defs()) f.write(' </style>\n') f.write('</head>\n') f.write(' <body>\n') sources = model.get_source_files() generators = model.get_generators() ais_by_source = model.get_analysis_issues_by_source() ais_by_source_and_generator = model.get_analysis_issues_by_source_and_generator() afs_by_source = model.get_analysis_failures_by_source() f.write(' <table>\n') if 1: f.write(' <tr>\n') f.write(' <th>Source file</th>\n') for generator in generators: f.write(' <th>%s</th>\n' % generator.name) f.write(' <th>Notes</th>\n') f.write(' </tr>\n') for file_ in sources: f.write(' <tr>\n') f.write(' <td><a href="#file-%s">%s</a></td>\n' % (file_.hash_.hexdigest, get_filename(file_))) for generator in generators: key = (file_, generator) ais = ais_by_source_and_generator.get(key, set()) class_ = 'has_issues' if ais else 'no_issues' f.write(' <td class="%s">%s</td>\n' % (class_, len(ais))) afs = afs_by_source.get(file_, []) if afs: f.write(' <td>Incomplete coverage: %i analysis failure(s)</td>\n' % len(afs)) else: f.write(' <td></td>\n') f.write(' </tr>\n') f.write(' </table>\n') for file_ in sources: f.write('<h2><a id="file-%s">%s</h2>\n' % (file_.hash_.hexdigest, get_filename(file_))) ais = ais_by_source.get(file_, set()) if ais: write_issue_table_for_file(f, file_, ais) else: f.write('<p>No issues found</p>') afs = afs_by_source.get(file_, []) if afs: write_failure_table_for_file(f, file_, afs) # Include source inline: code = model.get_file_content(file_) # Write any lineless issues/failures at the start of the file: f.write('<a id="file-%s-line-0"/>' % (file_.hash_.hexdigest, )) for ai in ais: if ai.line is None: f.write(make_issue_note(ai)) for af in afs: if af.line is None: f.write(make_failure_note(af)) for i, line in enumerate(sh.highlight(code).splitlines()): f.write('<a id="file-%s-line-%i"/>' % (file_.hash_.hexdigest, i + 1)) f.write(line) f.write('\n') for ai in ais: if ai.line == i + 1: f.write(make_issue_note(ai)) for af in afs: if af.line == i + 1: f.write(make_failure_note(af)) f.write(' </body>\n') f.write('</html>\n')