#!/usr/bin/env python
#$Id$
#
#  This code was modified by Ken Stanley, based on code written by Neal Bierbaum
#  for ml.
#
#  I added the -m minimum_number_of_lines option.  If fewer than the minimum number of lines
#  are executed this script return -1.  
#
"""
This is the simple processor for gcov results.
"""
import os, sys, re, getopt

def simplify_name(name):
    """
    Remove the path and extension information from the name.
    """
    basename = os.path.basename(name)
    shortname, ext = basename.split(".")
    return shortname

def build_file_list(directory, objFiles, gcovDaFiles):
    """
    Build separate lists for all .obj and .da files in
    "directory"
    """
    all_files = os.listdir(directory)
    for name in all_files:
        name = os.path.join(directory, name)
        #include only those files that have been compileed with
        #necessary flags
        if name.endswith(".bb") :
            objFiles.append(name)
        elif name.endswith(".da"):
            gcovDaFiles.append(name)

def process_gcov(gcovDirectory, gcovFile, gcovResultDictionary):
    """
    The primary action of the program. This runs "gcov" for an
    object file in the source directory and produce a dictionary
    entry of the result keyed by the shortened version of the file name.
    """
    if (os.path.isfile(gcovFile)):
        #create the gcov command
        #the cd is necessary because the gcov command follows
        #relative links
        gcov_command = "cd %s; gcov %s" %(gcovDirectory , gcovFile)
        stdin, stdout = os.popen2(gcov_command)
        stdin.close()
        gcov_result = stdout.read()
        stdout.close()
        data = re.findall(r'([\d]+\.[\d]+)\D+([\d]+)', gcov_result)
        percent_coverage, lines = data[len(data)-1]
        gcovResultDictionary[simplify_name(gcovFile)] = \
                    (str(float(percent_coverage) / 100.0), lines)

def scan_gcov_files(gcovDictionary, gcovDaFiles, gcovResultDictionary):
    """
    Call process_gcove for each of the files in the source directory
    the has gcov data.
    """
    for gcovFile in gcovDaFiles:
        process_gcov(gcovDictionary, gcovFile, gcovResultDictionary)

def process_results(objFiles, gcovResultDictionary):
    """
    Process the dictionary generated by scan_gcov_files and the set of
    unused source files to generate the csv line that contains all data.
    """
    total_lines_tested = 0
    total_lines = 0
    tested_files = 0
    total_files = 0
    detail_string = ""
    summary_string = ""
    for obj_file in objFiles:
        shortname = simplify_name(obj_file)
        total_files += 1
        if (gcovResultDictionary.has_key(shortname)):
            fraction_coverage_str, lines_str = gcovResultDictionary[shortname]
            fraction_coverage = float(fraction_coverage_str)
            lines = int(lines_str)
            lines_tested = lines * fraction_coverage
            total_lines_tested += int(lines_tested)
            total_lines += lines
            tested_files += 1
            detail_string += "," + str(fraction_coverage_str)
        else:
            detail_string += ",0.0"
    summary_string += "," + str(total_lines_tested)
    summary_string += "," + str(total_lines)
    if (total_lines > 0):
        fraction_tested_lines = float(total_lines_tested) / float(total_lines)
    else:
        fraction_tested_lines = 0.0
    summary_string += "," + str(fraction_tested_lines)
    summary_string += "," + str(tested_files)
    summary_string += "," + str(total_files)
    if (total_files > 0):
        fraction_tested_files = float(tested_files)/float(total_files)
    else:
        fraction_tested_files = 0.0
    summary_string += "," + str(fraction_tested_files)
    estimated_fraction_coverage = fraction_tested_lines * fraction_tested_files
    summary_string += "," + str(estimated_fraction_coverage)
    print summary_string,
    print detail_string,
    print " ", total_lines_tested, "lines out of ", total_lines, " were covered in the previous tests.  ", min_lines, " were expected. "
    if (min_lines > 0 ):
        if (total_lines_tested < min_lines ):
            print "Aborting"
            sys.exit(-1)

def print_header(objFiles):
    """
    Create a csv line to be used as a spreadsheet header.
    """
    print "Test Name",
    print ',tested lines,total lines,fraction tested,tested files,total files,fraction files tested,fraction estimated coverage',
    for objfile in objFiles:
        shortname = simplify_name(objfile)
        print ', %s' %shortname,
    print

def print_help():
    """
    Print help string for the program.
    """
    help_string = """
usage: analyze_coverage -h -H  -m minimum_lines -t name_of_test --help --Header --testname
            source_directory ...
    -h --help: print this message
    -m: minimum number of lines
    -H --Header: print header line
    -t --testname: name of test
    source_directory(s) one or more directories that contain files to
      analyze

    see README.analyze_coverage for further details
    """
    print help_string

#use python 2.2 version command line processing
if __name__ == "__main__":
    opts_list, pargs = getopt.getopt(sys.argv[1:], 'hm:Ht:', \
                            ["help", "Header", "testname="])
    opts_dict = {}
    for opt in opts_list:
        opts_dict[opt[0]] = opt[1]
    if (opts_dict.has_key('-h') or opts_dict.has_key('--help')):
        print_help()
        sys.exit(0)
    min_lines = int(-1)
    if (opts_dict.has_key("-m")):
        min_lines = int(opts_dict["-m"])
    testname = "unnamed"
    if (opts_dict.has_key("-t")):
        testname = opts_dict["-t"]
    elif (opts_dict.has_key("--testname")):
        testname = opts_dict["--testname"]
    directories = pargs
    gcov_directories = []
    for directory in directories:
        if os.path.isdir(directory):
            gcov_directory = os.path.abspath(directory)
            gcov_directories.append(gcov_directory)
        else:
            print >> sys.stderr, '"%s" is not a directory' %directory
            sys.exit(-1)
    obj_files = []
    gcov_da_files = []
    gcov_dictionary = {}
    for gcov_directory in gcov_directories:
        build_file_list(gcov_directory, obj_files, gcov_da_files)
        scan_gcov_files(gcov_directory, gcov_da_files, gcov_dictionary)
    if (opts_dict.has_key('-H') or opts_dict.has_key('--Header')):
        print_header(obj_files)
    process_results(obj_files, gcov_dictionary)
