#!/usr/bin/env python2

import os
import glob
import subprocess

COVERAGE_OPTIONS = '-Ccodegen-units=1 -Clink-dead-code -Zprofile -Cpanic=abort -Zpanic-abort-tests -Zinstrument-coverage'
LCOVOPTS = '--gcov-tool ./admin/llvm-gcov --rc lcov_excl_line=assert'.split()

def check_call(*args, **kwargs):
    subprocess.check_call(*args, **kwargs)

def lcov_exe(*args):
    return ['lcov'] + LCOVOPTS + list(args)

def sh(cmd):
    check_call(cmd, shell = True)

def cleanup():
    sh('cargo clean')
    sh("rm -rf *.gcda *.gcno")

def run(which):
    exe = filter(lambda x: '.' not in x, glob.glob('target/debug/deps/' + which + '-*'))[0]
    sh('./' + exe)

def rustc(*args):
    # `--profile` is unstable, so pass `-Zunstable-options` for now
    exe = ['cargo', 'rustc', '--all-features', '-Zunstable-options'] + list(args)
    env = dict(os.environ)
    env.update(RUSTC_WRAPPER = './admin/coverage-rustc',
               COVERAGE_OPTIONS = COVERAGE_OPTIONS,
               CARGO_INCREMENTAL = '0')
    check_call(exe, env = env)

def lcov(outfile):
    exe = lcov_exe('--capture', '--directory', '.', '--base-directory', '.', '-o', outfile)
    check_call(exe)
    return outfile

def merge(outfile, infiles):
    arg = []
    for i in infiles:
        arg.append('--add')
        arg.append(i)
    arg.append('-o')
    arg.append(outfile)
    check_call(lcov_exe(*arg))

def remove(outfile, infile, pattern):
    check_call(lcov_exe('--remove', infile, os.getcwd() + pattern, '-o', outfile))

def extract(outfile, infile):
    check_call(lcov_exe('--extract', infile, os.getcwd() + '/rustls/src/*', '-o', outfile))

def genhtml(outdir, infile):
    check_call(['genhtml', '--branch-coverage', '--demangle-cpp', '--legend',
        infile, '-o', outdir, '--ignore-errors', 'source'])

def rustc_llvm_version():
    lines = subprocess.check_output(['rustc', '-vV']).decode('ascii').splitlines()
    ln = [ln for ln in lines if ln.split(':', 1)[0] == 'LLVM version'][0]
    return int(ln.split(':')[1].split('.')[0].strip())

LLVM_VERSION = 14
llvm_version = rustc_llvm_version()
if llvm_version != LLVM_VERSION:
    print('expected rustc to use LLVM {}, found {}'.format(LLVM_VERSION, llvm_version))
    raise SystemExit(-1)

all_infos = []

# unit tests
cleanup()
rustc('--package', 'rustls', '--profile', 'test', '--lib')
run('rustls')
all_infos.append(lcov('rustls.info'))

cleanup()
for example in 'bench bogo_shim trytls_shim'.split():
    rustc('--package', 'rustls', '--profile', 'dev', '--example', example)

# crate-level tests
for test in 'api client_cert_verifier key_log_file_env server_cert_verifier'.split():
    rustc('--package', 'rustls', '--profile', 'dev', '--test', test)
    run(test)

# trytls/bogo
#sh('cd trytls && ./runme')
sh('cd bogo && env USE_EXISTING_BOGO_SHIM=1 ./runme')
all_infos.append(lcov('tests.info'))

merge('merged.info', all_infos)
remove('coverage.info', 'merged.info', '/rustls/src/msgs/macros.rs')
extract('final.info', 'coverage.info')
genhtml('target/coverage/', 'final.info')
