#!/usr/bin/env python # # Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import sys import subprocess import os import argparse import errno import shutil def GetLLVMBinDirectory(): buildtool_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../buildtools") platform_dir = "" if sys.platform.startswith('linux'): platform_dir = "linux-x64" elif sys.platform == 'darwin': platform_dir = "mac-x64" else: raise Exception("Unknown/Unsupported platform.") llvm_bin_dir = os.path.abspath(os.path.join(buildtool_dir, platform_dir, "clang/bin")) if not os.path.exists(llvm_bin_dir): raise Exception("LLVM directory %s double not be located." % llvm_bin_dir) return llvm_bin_dir def MakeDirs(new_dir): """A wrapper around os.makedirs() that emulates "mkdir -p".""" try: os.makedirs(new_dir) except OSError as e: if e.errno != errno.EEXIST: raise def RemoveIfExists(path): if os.path.isdir(path) and not os.path.islink(path): shutil.rmtree(path) elif os.path.exists(path): os.remove(path) def main(): parser = argparse.ArgumentParser(); parser.add_argument('-t', '--tests', nargs='+', dest='tests', required=True, help='The unit tests to run and gather coverage data on.') parser.add_argument('-o', '--output', dest='output', required=True, help='The output directory for coverage results.') parser.add_argument('-f', '--format', type=str, choices=['all', 'html', 'summary', 'lcov'], required=True, help='The type of coverage information to be displayed.') args = parser.parse_args() output = os.path.abspath(args.output) MakeDirs(output) generate_all_reports = args.format == "all" raw_profiles = [] binaries = [] # Run all unit tests and collect raw profiles. for test in args.tests: absolute_test_path = os.path.abspath(test) if not os.path.exists(absolute_test_path): print("Path %s does not exist." % absolute_test_path) return -1 binaries.append(absolute_test_path) raw_profile = absolute_test_path + ".rawprofile" RemoveIfExists(raw_profile) print "Running test %s to gather profile." % os.path.basename(absolute_test_path) subprocess.check_call([absolute_test_path], env={ "LLVM_PROFILE_FILE": raw_profile }) if not os.path.exists(raw_profile): print("Could not find raw profile data for unit test run %s." % test) print("Did you build with the --coverage flag?") return -1 raw_profiles.append(raw_profile) if len(raw_profiles) == 0: print("No raw profiles could be generated.") return -1 binaries_flag = [] for binary in binaries: binaries_flag.append('-object') binaries_flag.append(binary) llvm_bin_dir = GetLLVMBinDirectory() # Merge all raw profiles into a single profile. profdata_binary = os.path.join(llvm_bin_dir, "llvm-profdata") print("Merging %d raw profile(s) into single profile." % len(raw_profiles)) merged_profile_path = os.path.join(output, "all.profile") RemoveIfExists(merged_profile_path) merge_command = [profdata_binary, "merge", "-sparse"] + raw_profiles + ["-o", merged_profile_path] subprocess.check_call(merge_command) print("Done.") if not os.path.exists(merged_profile_path): print("Could not generate or find merged profile %s." % merged_profile_path) return -1 llvm_cov_binary = os.path.join(llvm_bin_dir, "llvm-cov") instr_profile_flag = "-instr-profile=%s" % merged_profile_path ignore_flags = "-ignore-filename-regex=third_party|unittest|fixture" # Generate the HTML report if specified. if generate_all_reports or args.format == 'html': print("Generating HTML report.") show_command = [llvm_cov_binary, "show"] + binaries_flag + [ instr_profile_flag, "-format=html", "-output-dir=%s" % output, "-tab-size=2", ignore_flags, ] subprocess.check_call(show_command) print("Done.") # Generate a report summary if specified. if generate_all_reports or args.format == 'summary': print("Generating a summary report.") report_command = [llvm_cov_binary, "report"] + binaries_flag + [ instr_profile_flag, ignore_flags, ] subprocess.check_call(report_command) print("Done.") # Generate a lcov summary if specified. if generate_all_reports or args.format == 'lcov': print("Generating LCOV report.") lcov_file = os.path.join(output, 'coverage.lcov') RemoveIfExists(lcov_file) lcov_command = [llvm_cov_binary, "export"] + binaries_flag + [ instr_profile_flag, ignore_flags, "-format=lcov", ] with open(lcov_file, 'w') as lcov_redirect: subprocess.check_call(lcov_command, stdout=lcov_redirect) print("Done.") return 0 if __name__ == '__main__': sys.exit(main())