#! /usr/bin/env python # Copyright (c) 2017 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # import sys import os import re import subprocess import difflib import csv # import argparse import pprint # sys.path.append("${INSTALL_PYTHON_DIR}") # sys.path.append("${DEPENDENCY_PYTHON_DIR}") # sys.path.append("../site-packages/longbow/") import LongBow import GCov import GCovSummary import FileUtil import ANSITerm import Language_C def checkTestExecutable(executableFileName): result = False if not os.path.exists(executableFileName): return result path = os.path.dirname(executableFileName) pattern = os.path.basename(executableFileName)+'*.gcda' if not Language_C.findFiles(path, pattern): return result pattern = os.path.basename(executableFileName)+'*.gcno' if not Language_C.findFiles(path, pattern): return result result = True return result def findTestExecutable(fileName, hints=[]): ''' Given a file name, look in the canonical places for a corresponding LongBow test file. ''' directoryName = os.path.dirname(fileName) if len(directoryName) == 0: directoryName = "." file = Language_C.Module(fileName) possibleTestFiles = list() for hint in hints: possibleTestFiles.append(hint + "/" + file.getExecutableName()) possibleTestFiles.append(hint + "/" + file.getTestExecutableName()) possibleTestFiles.append(directoryName + "/" + file.getExecutableName()) possibleTestFiles.append(directoryName + "/" + file.getTestExecutableName()) possibleTestFiles.append(directoryName + "/test/" + file.getTestExecutableName()) result = None for possibleTestFile in possibleTestFiles: if checkTestExecutable(possibleTestFile) == True: result = os.path.abspath(possibleTestFile) break return result def textSummary(args, filesAndTests, gCovResults, prefix=""): summary = GCov.computeSummary(filesAndTests, gCovResults) if not args.includeTestSources: summary = GCovSummary.removeTestSourceFiles(summary) if len(summary) == 0: return if args.explain: pp = pprint.PrettyPrinter(indent=2, width=150) pp.pprint(summary) maximumFileLength = max(map(lambda entry: len(entry), summary)) format = "%s%-" + str(maximumFileLength) + "s %6s" print format % (prefix, "File Path", "Score") format = "%s%-" + str(maximumFileLength) + "s %6.2f" for testedFile in sorted(summary.keys()): string = format % (prefix, testedFile, summary[testedFile]["coverage"]) if summary[testedFile]["direct"] == "indirect": ANSITerm.printColorized("magenta", string) else: LongBow.scorePrinter(eval(args.distribution), summary[testedFile]["coverage"], string) return def textAverage(args, filesAndTests, gcovResults): summary = GCov.computeSummary(filesAndTests, gcovResults) if not args.includeTestSources: summary = GCovSummary.removeTestSourceFiles(summary) score = GCovSummary.averageCoverage(summary) LongBow.scorePrinter(eval(args.distribution), score, "%.2f" % (score)) return score def csvSummary(args, filesAndTests, gCovResults): summary = GCov.computeSummary(filesAndTests, gCovResults) if not args.includeTestSources: summary = GCovSummary.removeTestSourceFiles(summary) if len(summary) > 0: for testedFile in sorted(summary.keys()): outputString = "%s,%.2f" % (testedFile, summary[testedFile]["coverage"]) LongBow.scorePrinter(eval(args.distribution), summary[testedFile]["coverage"], outputString) return def csvAverage(args, filesAndTests, gcovResults): summary = GCov.computeSummary(filesAndTests, gcovResults) if not args.includeTestSources: summary = GCovSummary.removeTestSourceFiles(summary) score = GCovSummary.averageCoverage(summary) LongBow.scorePrinter(eval(args.distribution), score, "%.2f" % (score)) return def textVisualDisplayGcovLine(line): token = line.split(":", 2) if len(token) == 3: if token[0] == "#####": print ANSITerm.colorize("red", token[1] + " " + token[2]) elif token[0] == "$$$$$": print ANSITerm.colorize("yellow", token[1] + " " + token[2]) else: print ANSITerm.colorize("green", token[1] + " " + token[2]) return def textVisual(args, filesAndTests, gcovResults): summary = GCov.computeSummary(filesAndTests, gcovResults) if args.explain: pp = pprint.PrettyPrinter(indent=2, width=150) pp.pprint(summary) pp.pprint(filesAndTests) for entry in filesAndTests: print entry[0] try: gcovLines = summary[entry[0]]["gcovLines"] map(lambda line: textVisualDisplayGcovLine(line.strip()), gcovLines) except KeyError: print >> sys.stderr, "No coverage information for", entry[0] return def displaySummary(args, filesAndTests, newGCovResults): if args.output == "text": textSummary(args, filesAndTests, newGCovResults) elif args.output == "csv": csvSummary(args, filesAndTests, newGCovResults) else: print >> sys.stderr, "Unsupported output type" return def displayAverage(args, filesAndTests, gcovResults): if args.output == "text": textAverage(args, filesAndTests, gcovResults) elif args.output == "csv": csvAverage(args, filesAndTests, gcovResults) else: print >> sys.stderr, "Unsupported output type" return def explain(args, filesAndTests, gcovResults): pp = pprint.PrettyPrinter(indent=2, width=150) pp.pprint(gcovResults) return def getFilesAndTests(fileNames, testDirs=[]): namesAndPaths = map(lambda fileName: [fileName, os.path.abspath(fileName)], fileNames) filesAndTests = map(lambda nameAndPath: [ nameAndPath[0], findTestExecutable(nameAndPath[1], testDirs) ], namesAndPaths) return filesAndTests def gradeAndPrint(targets, testDirs=[], problemsOnly=False, prefix=""): filesAndTests = getFilesAndTests(targets, testDirs) newGCovResults = map(lambda fileAndTestFile: GCov.getCoverage(fileAndTestFile[1]), filesAndTests) summarys = GCov.computeSummary(filesAndTests, newGCovResults) if len(summarys) < 1: print "%sNo GCov Results - Please be sure to run 'make check' first" % prefix return False summarys = GCovSummary.removeTestSourceFiles(summarys) paths = summarys.keys() if problemsOnly: paths = filter(lambda key: summarys[key]["coverage"] < 100, paths) distribution=[99,90] maximumFileLength = max(map(lambda entry: len(os.path.relpath(entry)), paths)) format = "%s%-" + str(maximumFileLength) + "s %6s" print format % (prefix, "File Path", "Score") format = "%s%-" + str(maximumFileLength) + "s %6.2f" for path in sorted(paths): string = format % (prefix, os.path.relpath(path), summarys[path]["coverage"]) LongBow.scorePrinter(distribution, summarys[path]["coverage"], string) return True def commandLineMain(args, fileNames, testDir=""): testDirs = [] if testDir: testDirs.append(testDir) fileNames = map(lambda fileName: os.path.abspath(fileName), fileNames) filesAndTests = map(lambda fileName: [fileName, findTestExecutable(fileName, testDirs)], fileNames) filesWithNoTest = filter(lambda fileAndTest: fileAndTest[1] == None, filesAndTests) if len(filesWithNoTest) != 0: outputFormat = "%s has no corresponding test executable or coverage data.\n" map(lambda filesAndTests: sys.stderr.write(outputFormat % (filesAndTests[0])), filesWithNoTest) gCovResults = map(lambda fileAndTestFile: GCov.getCoverage(fileAndTestFile[1]), filesAndTests) if args.summary is True: displaySummary(args, filesAndTests, gCovResults) elif args.average is True: displayAverage(args, filesAndTests, gCovResults) elif args.visual is True: textVisual(args, filesAndTests, gCovResults) elif args.explain is True: explain(args, filesAndTests, gCovResults) return True