def get(self, build_id): build = Build.query.get(build_id) if build is None: return '', 404 args = self.parser.parse_args() results = get_coverage_by_build_id(build.id) if args.diff: diff = build.source.generate_diff() if not diff: return self.respond({}) diff_parser = DiffParser(diff) lines_by_file = diff_parser.get_lines_by_file() results = [r for r in results if r.filename in lines_by_file] coverage_data = merged_coverage_data(results) coverage_stats = {} for filename in lines_by_file: if filename in coverage_data and filename in lines_by_file: stats = get_coverage_stats(lines_by_file[filename], coverage_data[filename]) coverage_stats[filename] = { 'linesCovered': stats.lines_covered, 'linesUncovered': stats.lines_uncovered, 'diffLinesCovered': stats.diff_lines_covered, 'diffLinesUncovered': stats.diff_lines_uncovered, } else: # NOTE: Without a diff, the stats may be off if there are # multiple job steps. (Each job step can potentially # return a separate FileCoverage row for the same file.) # For each file, we return the best metrics using # min()/max(); if you want more correct metrics, pass # diff=1. coverage_stats = {} for r in results: if r.filename not in coverage_stats: coverage_stats[r.filename] = { 'linesCovered': r.lines_covered, 'linesUncovered': r.lines_uncovered, 'diffLinesCovered': r.diff_lines_covered, 'diffLinesUncovered': r.diff_lines_uncovered, } else: # Combine metrics using max() for [diff] lines # covered, min() for [diff] lines uncovered. stats = coverage_stats[r.filename] coverage_stats[r.filename] = { 'linesCovered': max(stats['linesCovered'], r.lines_covered), 'linesUncovered': min(stats['linesUncovered'], r.lines_uncovered), 'diffLinesCovered': max(stats['diffLinesCovered'], r.diff_lines_covered), 'diffLinesUncovered': min(stats['diffLinesUncovered'], r.diff_lines_uncovered), } return self.respond(coverage_stats)
def test_get_changed_files_simple_diff(self): parser = DiffParser(SIMPLE_DIFF) files = parser.get_changed_files() assert files == set([ 'changes/utils/diff_parser.py', ]) lines_by_file = parser.get_lines_by_file() assert lines_by_file == {'changes/utils/diff_parser.py': {74}}
def test_get_changed_files_simple_diff(self): parser = DiffParser(SIMPLE_DIFF) files = parser.get_changed_files() assert files == set([ 'changes/utils/diff_parser.py', ]) lines_by_file = parser.get_lines_by_file() assert lines_by_file == {'changes/utils/diff_parser.py': {74}}
def test_get_changed_files_complex_diff(self): parser = DiffParser(COMPLEX_DIFF) files = parser.get_changed_files() assert files == set(["ci/run_with_retries.py", "ci/server-collect", "ci/not-real"]) lines_by_file = parser.get_lines_by_file() assert set(lines_by_file) == files assert lines_by_file["ci/not-real"] == {1} assert lines_by_file["ci/server-collect"] == {24, 31, 39, 46} assert lines_by_file["ci/run_with_retries.py"] == {2, 45} | set(range(53, 63)) | set(range(185, 192))
def test_add_empty_file(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py new file mode 100644 index 0000000..e69de29 """ parser = DiffParser(patch) (file_dict,) = parser.parse() diff = parser.reconstruct_file_diff(file_dict) assert diff == "" assert file_dict["old_filename"] is None assert parser.get_changed_files() == set(["diff-from/__init__.py"]) assert parser.get_lines_by_file() == {}
def test_add_empty_file(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py new file mode 100644 index 0000000..e69de29 """ parser = DiffParser(patch) (file_dict, ) = parser.parse() diff = parser.reconstruct_file_diff(file_dict) assert diff == "" assert file_dict.old_filename is None assert parser.get_changed_files() == set(['diff-from/__init__.py']) assert parser.get_lines_by_file() == {}
def test_remove_empty_file(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py deleted file mode 100644 index e69de29..0000000 """ parser = DiffParser(patch) (file_info,) = parser.parse() diff = parser.reconstruct_file_diff(file_info) assert diff == "" assert file_info.new_filename is None assert parser.get_changed_files() == set(['diff-from/__init__.py']) assert parser.get_lines_by_file() == {}
def test_remove_empty_file(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py deleted file mode 100644 index e69de29..0000000 """ parser = DiffParser(patch) (file_info, ) = parser.parse() diff = parser.reconstruct_file_diff(file_info) assert diff == "" assert file_info.new_filename is None assert parser.get_changed_files() == set(['diff-from/__init__.py']) assert parser.get_lines_by_file() == {}
def process_diff(self): lines_by_file = defaultdict(set) try: source = self.step.job.build.source except AttributeError: return lines_by_file diff = source.generate_diff() if not diff: return lines_by_file diff_parser = DiffParser(diff) return diff_parser.get_lines_by_file()
def process_diff(self): lines_by_file = defaultdict(set) try: source = self.step.job.build.source except AttributeError: return lines_by_file diff = source.generate_diff() if not diff: return lines_by_file diff_parser = DiffParser(diff) return diff_parser.get_lines_by_file()
def test_get_changed_files_complex_diff(self): parser = DiffParser(COMPLEX_DIFF) files = parser.get_changed_files() assert files == set([ 'ci/run_with_retries.py', 'ci/server-collect', 'ci/not-real', ]) lines_by_file = parser.get_lines_by_file() assert set(lines_by_file) == files assert lines_by_file['ci/not-real'] == {1} assert lines_by_file['ci/server-collect'] == {24, 31, 39, 46} assert lines_by_file['ci/run_with_retries.py'] == {2, 45} | set( range(53, 63)) | set(range(185, 192))
def test_add_multiple_empty_files(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diff-from/other.py b/diff-from/other.py new file mode 100644 index 0000000..e69de29 """ parser = DiffParser(patch) (first_dict, second_dict) = parser.parse() assert first_dict["new_filename"] == "b/diff-from/__init__.py" assert first_dict["old_filename"] is None assert second_dict["new_filename"] == "b/diff-from/other.py" assert second_dict["old_filename"] is None assert parser.get_changed_files() == set(["diff-from/__init__.py", "diff-from/other.py"]) assert parser.get_lines_by_file() == {}
def test_add_multiple_empty_files(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diff-from/other.py b/diff-from/other.py new file mode 100644 index 0000000..e69de29 """ parser = DiffParser(patch) (first_info, second_info,) = parser.parse() assert first_info.new_filename == 'b/diff-from/__init__.py' assert first_info.old_filename is None assert second_info.new_filename == 'b/diff-from/other.py' assert second_info.old_filename is None assert parser.get_changed_files() == set(['diff-from/__init__.py', 'diff-from/other.py']) assert parser.get_lines_by_file() == {}
def test_add_multiple_empty_files(self): patch = """diff --git a/diff-from/__init__.py b/diff-from/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/diff-from/other.py b/diff-from/other.py new file mode 100644 index 0000000..e69de29 """ parser = DiffParser(patch) ( first_info, second_info, ) = parser.parse() assert first_info.new_filename == 'b/diff-from/__init__.py' assert first_info.old_filename is None assert second_info.new_filename == 'b/diff-from/other.py' assert second_info.old_filename is None assert parser.get_changed_files() == set( ['diff-from/__init__.py', 'diff-from/other.py']) assert parser.get_lines_by_file() == {}
def test_dev_null_target(self): patch = """diff --git a/whitelist/blacklist/b.txt b/whitelist/blacklist/b.txt deleted file mode 100644 index 038d718..0000000 --- a/whitelist/blacklist/b.txt +++ /dev/null @@ -1 +0,0 @@ -testing """ parser = DiffParser(patch) (file_dict,) = parser.parse() diff = parser.reconstruct_file_diff(file_dict) assert diff == """ --- a/whitelist/blacklist/b.txt +++ /dev/null @@ -1 +0,0 @@ -testing """ assert file_dict['new_filename'] is None assert parser.get_changed_files() == set(['whitelist/blacklist/b.txt']) assert parser.get_lines_by_file() == {}
def test_dev_null_source(self): patch = """diff --git a/whitelist/blacklist/a.txt b/whitelist/blacklist/a.txt new file mode 100644 index 0000000..038d718 --- /dev/null +++ b/whitelist/blacklist/a.txt @@ -0,0 +1 @@ +testing """ parser = DiffParser(patch) (file_dict,) = parser.parse() diff = parser.reconstruct_file_diff(file_dict) assert diff == """ --- /dev/null +++ b/whitelist/blacklist/a.txt @@ -0,0 +1 @@ +testing """ assert file_dict['old_filename'] is None assert parser.get_changed_files() == set(['whitelist/blacklist/a.txt']) assert parser.get_lines_by_file() == {'whitelist/blacklist/a.txt': {1}}
def test_dev_null_source(self): patch = """diff --git a/whitelist/blacklist/a.txt b/whitelist/blacklist/a.txt new file mode 100644 index 0000000..038d718 --- /dev/null +++ b/whitelist/blacklist/a.txt @@ -0,0 +1 @@ +testing """ parser = DiffParser(patch) (file_info, ) = parser.parse() diff = parser.reconstruct_file_diff(file_info) assert diff == """ --- /dev/null +++ b/whitelist/blacklist/a.txt @@ -0,0 +1 @@ +testing """ assert file_info.old_filename is None assert parser.get_changed_files() == set(['whitelist/blacklist/a.txt']) assert parser.get_lines_by_file() == {'whitelist/blacklist/a.txt': {1}}
def test_dev_null_target(self): patch = """diff --git a/whitelist/blacklist/b.txt b/whitelist/blacklist/b.txt deleted file mode 100644 index 038d718..0000000 --- a/whitelist/blacklist/b.txt +++ /dev/null @@ -1 +0,0 @@ -testing """ parser = DiffParser(patch) (file_info, ) = parser.parse() diff = parser.reconstruct_file_diff(file_info) assert diff == """ --- a/whitelist/blacklist/b.txt +++ /dev/null @@ -1 +0,0 @@ -testing """ assert file_info.new_filename is None assert parser.get_changed_files() == set(['whitelist/blacklist/b.txt']) assert parser.get_lines_by_file() == {}
def get(self, build_id): build = Build.query.get(build_id) if build is None: return '', 404 args = self.parser.parse_args() results = get_coverage_by_build_id(build.id) if args.diff: diff = build.source.generate_diff() if not diff: return self.respond({}) diff_parser = DiffParser(diff) lines_by_file = diff_parser.get_lines_by_file() results = [r for r in results if r.filename in lines_by_file] coverage_data = merged_coverage_data(results) coverage_stats = {} for filename in lines_by_file: if filename in coverage_data and filename in lines_by_file: stats = get_coverage_stats(lines_by_file[filename], coverage_data[filename]) coverage_stats[filename] = { 'linesCovered': stats.lines_covered, 'linesUncovered': stats.lines_uncovered, 'diffLinesCovered': stats.diff_lines_covered, 'diffLinesUncovered': stats.diff_lines_uncovered, } else: # NOTE: Without a diff, the stats may be off if there are # multiple job steps. (Each job step can potentially # return a separate FileCoverage row for the same file.) # For each file, we return the best metrics using # min()/max(); if you want more correct metrics, pass # diff=1. coverage_stats = {} for r in results: if r.filename not in coverage_stats: coverage_stats[r.filename] = { 'linesCovered': r.lines_covered, 'linesUncovered': r.lines_uncovered, 'diffLinesCovered': r.diff_lines_covered, 'diffLinesUncovered': r.diff_lines_uncovered, } else: # Combine metrics using max() for [diff] lines # covered, min() for [diff] lines uncovered. stats = coverage_stats[r.filename] coverage_stats[r.filename] = { 'linesCovered': max(stats['linesCovered'], r.lines_covered), 'linesUncovered': min(stats['linesUncovered'], r.lines_uncovered), 'diffLinesCovered': max(stats['diffLinesCovered'], r.diff_lines_covered), 'diffLinesUncovered': min(stats['diffLinesUncovered'], r.diff_lines_uncovered), } return self.respond(coverage_stats)