def generate(changeset, path): ''' This function generates a report containing the coverage information for a given file at a given revision. ''' # If the file is not a source file, we can return early (as we already know # we have no coverage information for it). if not coverage_supported(path): return {} _, build_changeset, _ = get_coverage_build(changeset) coverage = coverage_service.get_file_coverage(build_changeset, path) return coverage if coverage is not None else {}
def generate(changeset): ''' This function generates a report containing the coverage information of the diff introduced by a changeset. ''' build_changeset, overall = get_coverage_build(changeset) r = requests.get('https://hg.mozilla.org/mozilla-central/raw-rev/%s' % changeset) patch = r.text diffs = [] def parse_diff(diff): # Get old and new path, for files that have been renamed. new_path = diff.header.new_path[2:] if diff.header.new_path.startswith('b/') else diff.header.new_path # If the diff doesn't contain any changes, we skip it. if diff.changes is None: return None # If the file is not a source file, we skip it (as we already know # we have no coverage information for it). if not coverage_supported(new_path): return None # Retrieve coverage of added lines. coverage = coverage_service.get_file_coverage(build_changeset, new_path) # If we don't have coverage for this file, we skip it. if coverage is None: return None changes = [] for old_line, new_line, _ in diff.changes: # Only consider added lines. if old_line is not None or new_line is None: continue if new_line not in coverage or coverage[new_line] is None: # We have no coverage information for this line (e.g. a definition, like # a variable in a header file). covered = '?' elif coverage[new_line] > 0: covered = 'Y' else: covered = 'N' changes.append({ 'coverage': covered, 'line': new_line, }) return { 'name': new_path, 'changes': changes, } def parse_diff_task(diff): return lambda: parse_diff(diff) with ThreadPoolExecutor(max_workers=4) as executor: futures = [] for diff in whatthepatch.parse_patch(patch): futures.append(executor.submit(parse_diff_task(diff))) for future in concurrent.futures.as_completed(futures): res = future.result() if res is not None: diffs.append(res) return { 'build_changeset': build_changeset, 'git_build_changeset': get_github_commit(build_changeset), 'overall_cur': overall['cur'], 'overall_prev': overall['prev'], 'diffs': diffs, }
def generate(changeset): ''' This function generates a report containing the coverage information of the diff introduced by a changeset. ''' changeset_data, build_changeset, overall = get_coverage_build(changeset) if 'merge' in changeset_data: raise Exception( 'Retrieving coverage for merge commits is not supported.') diffs = [] def retrieve_coverage(path): # If the file is not a source file, we skip it (as we already know # we have no coverage information for it). if not coverage_supported(path): return None # Use hg annotate to report lines in their correct positions and to avoid # reporting lines that have been modified by a successive patch in the same push. r = requests.get( 'https://hg.mozilla.org/mozilla-central/json-annotate/{}/{}'. format(build_changeset, path)) data = r.json() if 'not found in manifest' in data: # The file was removed. return None annotate = r.json()['annotate'] # Retrieve coverage of added lines. coverage = coverage_service.get_file_coverage(build_changeset, path) # If we don't have coverage for this file, we skip it. if coverage is None: return None changes = [] for data in annotate: # Skip lines that were not added by this changeset or were overwritten by # another changeset. if data['node'][:len(changeset)] != changeset: continue new_line = data['lineno'] if new_line not in coverage or coverage[new_line] is None: # We have no coverage information for this line (e.g. a definition, like # a variable in a header file). covered = '?' elif coverage[new_line] > 0: covered = 'Y' else: covered = 'N' changes.append({ 'coverage': covered, 'line': data['targetline'], }) return { 'name': path, 'changes': changes, } def retrieve_coverage_task(path): return lambda: retrieve_coverage(path) with ThreadPoolExecutor(max_workers=4) as executor: futures = [] for path in changeset_data['files']: futures.append(executor.submit(retrieve_coverage_task(path))) for future in concurrent.futures.as_completed(futures): res = future.result() if res is not None: diffs.append(res) return { 'build_changeset': build_changeset, 'git_build_changeset': get_github_commit(build_changeset), 'overall_cur': overall['cur'], 'overall_prev': overall['prev'], 'diffs': diffs, }
def generate(changeset): ''' This function generates a report containing the coverage information of the diff introduced by a changeset. ''' desc, build_changeset, overall = get_coverage_build(changeset) if any(text in desc for text in ['r=merge', 'a=merge']): raise Exception('Retrieving coverage for merge commits is not supported.') r = requests.get('https://hg.mozilla.org/mozilla-central/raw-rev/%s' % changeset) patch = r.text diffs = [] def parse_diff(diff): # Get old and new path, for files that have been renamed. new_path = diff.header.new_path[2:] if diff.header.new_path.startswith('b/') else diff.header.new_path # If the diff doesn't contain any changes, we skip it. if diff.changes is None: return None # If the file is not a source file, we skip it (as we already know # we have no coverage information for it). if not coverage_supported(new_path): return None # Retrieve coverage of added lines. coverage = coverage_service.get_file_coverage(build_changeset, new_path) # If we don't have coverage for this file, we skip it. if coverage is None: return None # Use hg annotate to report lines in their correct positions and to avoid # reporting lines that have been modified by a successive patch in the same push. r = requests.get('https://hg.mozilla.org/mozilla-central/json-annotate/%s/%s' % (build_changeset, new_path)) annotate = r.json()['annotate'] changes = [] for data in annotate: # Skip lines that were not added by this changeset or were overwritten by # another changeset. if data['node'][:len(changeset)] != changeset: continue new_line = data['lineno'] if new_line not in coverage or coverage[new_line] is None: # We have no coverage information for this line (e.g. a definition, like # a variable in a header file). covered = '?' elif coverage[new_line] > 0: covered = 'Y' else: covered = 'N' changes.append({ 'coverage': covered, 'line': data['targetline'], }) return { 'name': new_path, 'changes': changes, } def parse_diff_task(diff): return lambda: parse_diff(diff) with ThreadPoolExecutor(max_workers=4) as executor: futures = [] for diff in whatthepatch.parse_patch(patch): futures.append(executor.submit(parse_diff_task(diff))) for future in concurrent.futures.as_completed(futures): res = future.result() if res is not None: diffs.append(res) return { 'build_changeset': build_changeset, 'git_build_changeset': get_github_commit(build_changeset), 'overall_cur': overall['cur'], 'overall_prev': overall['prev'], 'diffs': diffs, }