def compare_html(expected, actual, extra_scrubs=None): """Specialized compare function for our HTML files.""" __tracebackhide__ = True # pytest, please don't show me this function. scrubs = [ (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), (r'coverage.py v[\d.abc]+', 'coverage.py vVER'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d [-+]\d\d\d\d', 'created at DATE'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d', 'created at DATE'), # Occasionally an absolute path is in the HTML report. (filepath_to_regex(TESTS_DIR), 'TESTS_DIR'), (filepath_to_regex(flat_rootname(str(TESTS_DIR))), '_TESTS_DIR'), # The temp dir the tests make. (filepath_to_regex(os.getcwd()), 'TEST_TMPDIR'), (filepath_to_regex(flat_rootname(str(os.getcwd()))), '_TEST_TMPDIR'), (filepath_to_regex(abs_file(os.getcwd())), 'TEST_TMPDIR'), (filepath_to_regex(flat_rootname(str(abs_file(os.getcwd())))), '_TEST_TMPDIR'), (r'/private/var/[\w/]+/pytest-of-\w+/pytest-\d+/(popen-gw\d+/)?t\d+', 'TEST_TMPDIR'), ] if env.WINDOWS: # For file paths... scrubs += [(r"\\", "/")] if extra_scrubs: scrubs += extra_scrubs compare(expected, actual, file_pattern="*.html", scrubs=scrubs)
def compare_html(expected, actual): """Specialized compare function for our HTML files.""" scrubs = [ (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), (r'coverage.py v[\d.abc]+', 'coverage.py vVER'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d [-+]\d\d\d\d', 'created at DATE'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d', 'created at DATE'), # Some words are identifiers in one version, keywords in another. (r'<span class="(nam|key)">(print|True|False)</span>', r'<span class="nam">\2</span>'), # Occasionally an absolute path is in the HTML report. (filepath_to_regex(TESTS_DIR), 'TESTS_DIR'), (filepath_to_regex(flat_rootname(unicode_class(TESTS_DIR))), '_TESTS_DIR'), # The temp dir the tests make. (filepath_to_regex(os.getcwd()), 'TEST_TMPDIR'), (filepath_to_regex(flat_rootname(unicode_class(os.getcwd()))), '_TEST_TMPDIR'), (filepath_to_regex(abs_file(os.getcwd())), 'TEST_TMPDIR'), (filepath_to_regex(flat_rootname(unicode_class(abs_file( os.getcwd())))), '_TEST_TMPDIR'), (r'/private/var/folders/[\w/]{35}/coverage_test/tests_test_html_\w+_\d{8}', 'TEST_TMPDIR'), (r'_private_var_folders_\w{35}_coverage_test_tests_test_html_\w+_\d{8}', '_TEST_TMPDIR'), ] if env.WINDOWS: # For file paths... scrubs += [(r"\\", "/")] compare(expected, actual, file_pattern="*.html", scrubs=scrubs)
def compare_html(dir1, dir2): """Specialized compare function for HTML files.""" scrubs = [ (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), (r'coverage.py v[\d.abc]+', 'coverage.py vVER'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d', 'created at DATE'), # Some words are identifiers in one version, keywords in another. (r'<span class="(nam|key)">(print|True|False)</span>', r'<span class="nam">\2</span>'), # Occasionally an absolute path is in the HTML report. (re.escape(TESTS_DIR), 'TESTS_DIR'), (r'/Users/ned/coverage/trunk/tests', 'TESTS_DIR'), (flat_rootname(unicode_class(TESTS_DIR)), '_TESTS_DIR'), (flat_rootname(u'/Users/ned/coverage/trunk/tests'), '_TESTS_DIR'), ] if env.WINDOWS: # For file paths... scrubs += [(r"\\", "/")] return compare(dir1, dir2, file_pattern="*.html", scrubs=scrubs)
def annotate_file(self, fr, analysis): """Annotate a single file. `fr` is the FileReporter for the file to annotate. """ statements = sorted(analysis.statements) missing = sorted(analysis.missing) excluded = sorted(analysis.excluded) if self.directory: ensure_dir(self.directory) dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename())) if dest_file.endswith("_py"): dest_file = dest_file[:-3] + ".py" dest_file += ",cover" else: dest_file = fr.filename + ",cover" with io.open(dest_file, 'w', encoding='utf8') as dest: i = 0 j = 0 covered = True source = fr.source() for lineno, line in enumerate(source.splitlines(True), start=1): while i < len(statements) and statements[i] < lineno: i += 1 while j < len(missing) and missing[j] < lineno: j += 1 if i < len(statements) and statements[i] == lineno: covered = j >= len(missing) or missing[j] > lineno if self.blank_re.match(line): dest.write(u' ') elif self.else_re.match(line): # Special logic for lines containing only 'else:'. if i >= len(statements) and j >= len(missing): dest.write(u'! ') elif i >= len(statements) or j >= len(missing): dest.write(u'> ') elif statements[i] == missing[j]: dest.write(u'! ') else: dest.write(u'> ') elif lineno in excluded: dest.write(u'- ') elif covered: dest.write(u'> ') else: dest.write(u'! ') dest.write(line)
def annotate_file(self, fr, analysis): """Annotate a single file. `fr` is the FileReporter for the file to annotate. """ statements = sorted(analysis.statements) missing = sorted(analysis.missing) excluded = sorted(analysis.excluded) if self.directory: dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename())) if dest_file.endswith("_py"): dest_file = dest_file[:-3] + ".py" dest_file += ",cover" else: dest_file = fr.filename + ",cover" with open(dest_file, 'w') as dest: i = 0 j = 0 covered = True source = fr.source() for lineno, line in enumerate(source.splitlines(True), start=1): while i < len(statements) and statements[i] < lineno: i += 1 while j < len(missing) and missing[j] < lineno: j += 1 if i < len(statements) and statements[i] == lineno: covered = j >= len(missing) or missing[j] > lineno if self.blank_re.match(line): dest.write(u' ') elif self.else_re.match(line): # Special logic for lines containing only 'else:'. if i >= len(statements) and j >= len(missing): dest.write(u'! ') elif i >= len(statements) or j >= len(missing): dest.write(u'> ') elif statements[i] == missing[j]: dest.write(u'! ') else: dest.write(u'> ') elif lineno in excluded: dest.write(u'- ') elif covered: dest.write(u'> ') else: dest.write(u'! ') dest.write(line)
def compare_html(expected, actual): """Specialized compare function for our HTML files.""" scrubs = [ (r'/coverage.readthedocs.io/?[-.\w/]*', '/coverage.readthedocs.io/VER'), (r'coverage.py v[\d.abc]+', 'coverage.py vVER'), (r'created at \d\d\d\d-\d\d-\d\d \d\d:\d\d', 'created at DATE'), # Some words are identifiers in one version, keywords in another. (r'<span class="(nam|key)">(print|True|False)</span>', r'<span class="nam">\2</span>'), # Occasionally an absolute path is in the HTML report. (filepath_to_regex(TESTS_DIR), 'TESTS_DIR'), (r'/Users/ned/coverage/trunk/tests', 'TESTS_DIR'), (filepath_to_regex(flat_rootname(unicode_class(TESTS_DIR))), '_TESTS_DIR'), (flat_rootname(u'/Users/ned/coverage/trunk/tests'), '_TESTS_DIR'), # The temp dir the tests make. (filepath_to_regex(os.getcwd()), 'TEST_TMPDIR'), (filepath_to_regex(flat_rootname(unicode_class(os.getcwd()))), '_TEST_TMPDIR'), (r'/private/var/folders/[\w/]{35}/coverage_test/tests_test_html_\w+_\d{8}', 'TEST_TMPDIR'), (r'_private_var_folders_\w{35}_coverage_test_tests_test_html_\w+_\d{8}', '_TEST_TMPDIR'), ] if env.WINDOWS: # For file paths... scrubs += [(r"\\", "/")] compare(expected, actual, file_pattern="*.html", scrubs=scrubs)
def test_flat_rootname(self): self.assertEqual(flat_rootname("a/b/c.py"), "a_b_c_py") self.assertEqual(flat_rootname(r"c:\foo\bar.html"), "c__foo_bar_html")
def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" rootname = flat_rootname(fr.relative_filename()) html_filename = rootname + ".html" ensure_dir(self.directory) html_path = os.path.join(self.directory, html_filename) # Get the numbers for this file. nums = analysis.numbers self.all_files_nums.append(nums) if self.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: # If there's an existing file, remove it. file_be_gone(html_path) self.skipped_covered_count += 1 return if self.skip_empty: # Don't report on empty files. if nums.n_statements == 0: file_be_gone(html_path) self.skipped_empty_count += 1 return # Find out if the file on disk is already correct. if self.incr.can_skip_file(self.data, fr, rootname): self.file_summaries.append(self.incr.index_info(rootname)) return # Write the HTML page for this file. file_data = self.datagen.data_for_file(fr, analysis) for ldata in file_data.lines: # Build the HTML for the line. html = [] for tok_type, tok_text in ldata.tokens: if tok_type == "ws": html.append(escape(tok_text)) else: tok_html = escape(tok_text) or ' ' html.append(f'<span class="{tok_type}">{tok_html}</span>') ldata.html = ''.join(html) if ldata.short_annotations: # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. ldata.annotate = ", ".join( f"{ldata.number} ↛ {d}" for d in ldata.short_annotations) else: ldata.annotate = None if ldata.long_annotations: longs = ldata.long_annotations if len(longs) == 1: ldata.annotate_long = longs[0] else: ldata.annotate_long = "{:d} missed branches: {}".format( len(longs), ", ".join( f"{num:d}) {ann_long}" for num, ann_long in enumerate(longs, start=1)), ) else: ldata.annotate_long = None css_classes = [] if ldata.category: css_classes.append( self.template_globals['category'][ldata.category]) ldata.css_class = ' '.join(css_classes) or "pln" html = self.source_tmpl.render(file_data.__dict__) write_html(html_path, html) # Save this file's information for the index file. index_info = { 'nums': nums, 'html_filename': html_filename, 'relative_filename': fr.relative_filename(), } self.file_summaries.append(index_info) self.incr.set_index_info(rootname, index_info)
def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" source = fr.source() # Find out if the file on disk is already correct. rootname = flat_rootname(fr.relative_filename()) this_hash = self.file_hash(source.encode('utf-8'), fr) that_hash = self.status.file_hash(rootname) if this_hash == that_hash: # Nothing has changed to require the file to be reported again. self.files.append(self.status.index_info(rootname)) return self.status.set_file_hash(rootname, this_hash) # Get the numbers for this file. nums = analysis.numbers if self.has_arcs: missing_branch_arcs = analysis.missing_branch_arcs() arcs_executed = analysis.arcs_executed() # These classes determine which lines are highlighted by default. c_run = "run hide_run" c_exc = "exc" c_mis = "mis" c_par = "par " + c_run lines = [] for lineno, line in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. line_class = [] annotate_html = "" annotate_long = "" if lineno in analysis.statements: line_class.append("stm") if lineno in analysis.excluded: line_class.append(c_exc) elif lineno in analysis.missing: line_class.append(c_mis) elif self.has_arcs and lineno in missing_branch_arcs: line_class.append(c_par) shorts = [] longs = [] for b in missing_branch_arcs[lineno]: if b < 0: shorts.append("exit") else: shorts.append(b) longs.append( fr.missing_arc_description(lineno, b, arcs_executed)) # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. short_fmt = "%s ↛ %s" annotate_html = ", ".join(short_fmt % (lineno, d) for d in shorts) if len(longs) == 1: annotate_long = longs[0] else: annotate_long = "%d missed branches: %s" % ( len(longs), ", ".join( "%d) %s" % (num, ann_long) for num, ann_long in enumerate(longs, start=1)), ) elif lineno in analysis.statements: line_class.append(c_run) # Build the HTML for the line. html = [] for tok_type, tok_text in line: if tok_type == "ws": html.append(escape(tok_text)) else: tok_html = escape(tok_text) or ' ' html.append('<span class="%s">%s</span>' % (tok_type, tok_html)) lines.append({ 'html': ''.join(html), 'number': lineno, 'class': ' '.join(line_class) or "pln", 'annotate': annotate_html, 'annotate_long': annotate_long, }) # Write the HTML page for this file. html = self.source_tmpl.render({ 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run, 'has_arcs': self.has_arcs, 'extra_css': self.extra_css, 'fr': fr, 'nums': nums, 'lines': lines, 'time_stamp': self.time_stamp, }) html_filename = rootname + ".html" html_path = os.path.join(self.directory, html_filename) write_html(html_path, html) # Save this file's information for the index file. index_info = { 'nums': nums, 'html_filename': html_filename, 'relative_filename': fr.relative_filename(), } self.files.append(index_info) self.status.set_index_info(rootname, index_info)
def test_flat_rootname(self): self.assertEqual(flat_rootname("a/b/c.py"), "a_b_c_py") self.assertEqual(flat_rootname(r"c:\foo\bar.html"), "_foo_bar_html")
def get_html_report_content(self, module): """Return the content of the HTML report for `module`.""" filename = flat_rootname(module) + ".html" filename = os.path.join("htmlcov", filename) with open(filename) as f: return f.read()
def test_flat_rootname(original, flat): assert flat_rootname(original) == flat
def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" source = fr.source() # Find out if the file on disk is already correct. rootname = flat_rootname(fr.relative_filename()) this_hash = self.file_hash(source.encode('utf-8'), fr) that_hash = self.status.file_hash(rootname) if this_hash == that_hash: # Nothing has changed to require the file to be reported again. self.files.append(self.status.index_info(rootname)) return self.status.set_file_hash(rootname, this_hash) # Get the numbers for this file. nums = analysis.numbers if self.has_arcs: missing_branch_arcs = analysis.missing_branch_arcs() # These classes determine which lines are highlighted by default. c_run = "run hide_run" c_exc = "exc" c_mis = "mis" c_par = "par " + c_run lines = [] for lineno, line in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. line_class = [] annotate_html = "" annotate_title = "" if lineno in analysis.statements: line_class.append("stm") if lineno in analysis.excluded: line_class.append(c_exc) elif lineno in analysis.missing: line_class.append(c_mis) elif self.has_arcs and lineno in missing_branch_arcs: line_class.append(c_par) shorts = [] longs = [] for b in missing_branch_arcs[lineno]: if b < 0: shorts.append("exit") longs.append("the function exit") else: shorts.append(b) longs.append("line %d" % b) # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. short_fmt = "%s ↛ %s" annotate_html = ", ".join(short_fmt % (lineno, d) for d in shorts) annotate_html += " [?]" annotate_title = "Line %d was executed, but never jumped to " % lineno if len(longs) == 1: annotate_title += longs[0] elif len(longs) == 2: annotate_title += longs[0] + " or " + longs[1] else: annotate_title += ", ".join(longs[:-1]) + ", or " + longs[-1] elif lineno in analysis.statements: line_class.append(c_run) # Build the HTML for the line. html = [] for tok_type, tok_text in line: if tok_type == "ws": html.append(escape(tok_text)) else: tok_html = escape(tok_text) or ' ' html.append( '<span class="%s">%s</span>' % (tok_type, tok_html) ) lines.append({ 'html': ''.join(html), 'number': lineno, 'class': ' '.join(line_class) or "pln", 'annotate': annotate_html, 'annotate_title': annotate_title, }) # Write the HTML page for this file. template_values = { 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run, 'has_arcs': self.has_arcs, 'extra_css': self.extra_css, 'fr': fr, 'nums': nums, 'lines': lines, 'time_stamp': self.time_stamp, } html = spaceless(self.source_tmpl.render(template_values)) html_filename = rootname + ".html" html_path = os.path.join(self.directory, html_filename) self.write_html(html_path, html) # Save this file's information for the index file. index_info = { 'nums': nums, 'html_filename': html_filename, 'relative_filename': fr.relative_filename(), } self.files.append(index_info) self.status.set_index_info(rootname, index_info)
def html_file(self, fr, analysis): """Generate an HTML file for one source file.""" rootname = flat_rootname(fr.relative_filename()) html_filename = rootname + ".html" html_path = os.path.join(self.directory, html_filename) # Get the numbers for this file. nums = analysis.numbers self.all_files_nums.append(nums) if self.config.skip_covered: # Don't report on 100% files. no_missing_lines = (nums.n_missing == 0) no_missing_branches = (nums.n_partial_branches == 0) if no_missing_lines and no_missing_branches: # If there's an existing file, remove it. file_be_gone(html_path) return source = fr.source() # Find out if the file on disk is already correct. this_hash = self.file_hash(source.encode('utf-8'), fr) that_hash = self.status.file_hash(rootname) if this_hash == that_hash: # Nothing has changed to require the file to be reported again. self.files.append(self.status.index_info(rootname)) return self.status.set_file_hash(rootname, this_hash) if self.has_arcs: missing_branch_arcs = analysis.missing_branch_arcs() arcs_executed = analysis.arcs_executed() # These classes determine which lines are highlighted by default. c_run = "run hide_run" c_exc = "exc" c_mis = "mis" c_par = "par " + c_run lines = [] for lineno, line in enumerate(fr.source_token_lines(), start=1): # Figure out how to mark this line. line_class = [] annotate_html = "" annotate_long = "" if lineno in analysis.statements: line_class.append("stm") if lineno in analysis.excluded: line_class.append(c_exc) elif lineno in analysis.missing: line_class.append(c_mis) elif self.has_arcs and lineno in missing_branch_arcs: line_class.append(c_par) shorts = [] longs = [] for b in missing_branch_arcs[lineno]: if b < 0: shorts.append("exit") else: shorts.append(b) longs.append(fr.missing_arc_description(lineno, b, arcs_executed)) # 202F is NARROW NO-BREAK SPACE. # 219B is RIGHTWARDS ARROW WITH STROKE. short_fmt = "%s ↛ %s" annotate_html = ", ".join(short_fmt % (lineno, d) for d in shorts) if len(longs) == 1: annotate_long = longs[0] else: annotate_long = "%d missed branches: %s" % ( len(longs), ", ".join("%d) %s" % (num, ann_long) for num, ann_long in enumerate(longs, start=1)), ) elif lineno in analysis.statements: line_class.append(c_run) # Build the HTML for the line. html = [] for tok_type, tok_text in line: if tok_type == "ws": html.append(escape(tok_text)) else: tok_html = escape(tok_text) or ' ' html.append( '<span class="%s">%s</span>' % (tok_type, tok_html) ) lines.append({ 'html': ''.join(html), 'number': lineno, 'class': ' '.join(line_class) or "pln", 'annotate': annotate_html, 'annotate_long': annotate_long, }) # Write the HTML page for this file. html = self.source_tmpl.render({ 'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run, 'has_arcs': self.has_arcs, 'extra_css': self.extra_css, 'fr': fr, 'nums': nums, 'lines': lines, 'time_stamp': self.time_stamp, }) write_html(html_path, html) # Save this file's information for the index file. index_info = { 'nums': nums, 'html_filename': html_filename, 'relative_filename': fr.relative_filename(), } self.files.append(index_info) self.status.set_index_info(rootname, index_info)