CTest: Add Javascript coverage parser

Add a coverage parser for the Blanket.js library using the JSON output of
the mocha.js test runner.

Add a test for the new parser.
This commit is contained in:
Joseph Snyder 2014-10-23 17:03:30 -04:00 committed by Brad King
parent a2456e1572
commit 220e81345b
10 changed files with 823 additions and 0 deletions

View File

@ -522,6 +522,7 @@ set(CTEST_SRCS cmCTest.cxx
CTest/cmParseCacheCoverage.cxx
CTest/cmParseGTMCoverage.cxx
CTest/cmParseJacocoCoverage.cxx
CTest/cmParseBlanketJSCoverage.cxx
CTest/cmParsePHPCoverage.cxx
CTest/cmParseCoberturaCoverage.cxx
CTest/cmParseDelphiCoverage.cxx

View File

@ -16,6 +16,7 @@
#include "cmParseCacheCoverage.h"
#include "cmParseJacocoCoverage.h"
#include "cmParseDelphiCoverage.h"
#include "cmParseBlanketJSCoverage.h"
#include "cmCTest.h"
#include "cmake.h"
#include "cmMakefile.h"
@ -419,6 +420,13 @@ int cmCTestCoverageHandler::ProcessHandler()
file_count += this->HandleJacocoCoverage(&cont);
error = cont.Error;
if ( file_count < 0 )
{
return error;
}
file_count += this->HandleBlanketJSCoverage(&cont);
error = cont.Error;
if ( file_count < 0 )
{
return error;
@ -927,10 +935,12 @@ int cmCTestCoverageHandler::HandleDelphiCoverage(
std::vector<std::string> files;
g.SetRecurse(true);
std::string BinDir
= this->CTest->GetBinaryDir();
std::string coverageFile = BinDir+ "/*.html";
g.FindFiles(coverageFile);
files=g.GetFiles();
if (files.size() > 0)
@ -947,6 +957,36 @@ int cmCTestCoverageHandler::HandleDelphiCoverage(
}
return static_cast<int>(cont->TotalCoverage.size());
}
//----------------------------------------------------------------------
int cmCTestCoverageHandler::HandleBlanketJSCoverage(
cmCTestCoverageHandlerContainer* cont)
{
cmParseBlanketJSCoverage cov =
cmParseBlanketJSCoverage(*cont, this->CTest);
std::string SourceDir
= this->CTest->GetCTestConfiguration("SourceDirectory");
//Look for something other than output.json, still JSON extension.
std::string coverageFile = SourceDir+ "/*.json";
cmsys::Glob g;
std::vector<std::string> files;
g.FindFiles(coverageFile);
files=g.GetFiles();
if (files.size() > 0)
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
"Found BlanketJS output JSON, Performing Coverage" << std::endl);
cov.LoadCoverageData(files);
}
else
{
cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
" Cannot find BlanketJS coverage files: " << coverageFile
<< std::endl);
}
return static_cast<int>(cont->TotalCoverage.size());
}
//----------------------------------------------------------------------
int cmCTestCoverageHandler::HandleGCovCoverage(
cmCTestCoverageHandlerContainer* cont)

View File

@ -87,6 +87,9 @@ private:
//! Handle coverage for Delphi (Pascal)
int HandleDelphiCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage for Jacoco
int HandleBlanketJSCoverage(cmCTestCoverageHandlerContainer* cont);
//! Handle coverage using Bullseye
int HandleBullseyeCoverage(cmCTestCoverageHandlerContainer* cont);
int RunBullseyeSourceSummary(cmCTestCoverageHandlerContainer* cont);

View File

@ -0,0 +1,166 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc.
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmStandardIncludes.h"
#include <stdio.h>
#include <stdlib.h>
#include "cmSystemTools.h"
#include "cmParseBlanketJSCoverage.h"
#include <cmsys/Directory.hxx>
#include <cmsys/Glob.hxx>
#include <cmsys/FStream.hxx>
class cmParseBlanketJSCoverage::JSONParser
{
public:
typedef cmCTestCoverageHandlerContainer::
SingleFileCoverageVector FileLinesType;
JSONParser(cmCTestCoverageHandlerContainer& cont)
: Coverage(cont)
{
}
virtual ~JSONParser()
{
}
std::string getValue(std::string line, int type)
{
size_t begIndex;
size_t endIndex;
endIndex = line.rfind(',');
begIndex = line.find_first_of(':');
if(type == 0)
{
// A unique substring to remove the extra characters
// around the files name in the JSON (extra " and ,)
std::string foundFileName =
line.substr(begIndex+3,endIndex-(begIndex+4));
return foundFileName;
}
else
{
return line.substr(begIndex,line.npos);
}
}
bool ParseFile(std::string file)
{
FileLinesType localCoverageVector;
std::string filename;
bool foundFile = false;
bool inSource = false;
std::string covResult;
std::string line;
cmsys::ifstream in(file.c_str());
if(!in)
{
return false;
}
while( cmSystemTools::GetLineFromStream(in, line))
{
if(line.find("filename") != line.npos)
{
if(foundFile)
{
/*
* Upon finding a second file name, generate a
* vector within the total coverage to capture the
* information in the local vector
*/
FileLinesType& CoverageVector =
this->Coverage.TotalCoverage[filename.c_str()];
CoverageVector = localCoverageVector;
localCoverageVector.clear();
foundFile=false;
}
foundFile= true;
inSource = false;
filename = getValue(line,0).c_str();
}
else if((line.find("coverage") != line.npos) && foundFile && inSource )
{
/*
* two types of "coverage" in the JSON structure
*
* The coverage result over the file or set of files
* and the coverage for each individual line
*
* FoundFile and foundSource ensure that
* only the value of the line coverage is captured
*/
std::string result = getValue(line,1).c_str();
result = result.substr(2,result.npos);
if(result == "\"\"")
{
// Empty quotation marks indicate that the
// line is not executable
localCoverageVector.push_back(-1);
}
else
{
// Else, it contains the number of time executed
localCoverageVector.push_back(atoi(result.c_str()));
}
}
else if(line.find("source") != line.npos)
{
inSource=true;
}
}
// On exit, capture end of last file covered.
FileLinesType& CoverageVector =
this->Coverage.TotalCoverage[filename.c_str()];
CoverageVector = localCoverageVector;
foundFile=false;
localCoverageVector.clear();
return true;
}
private:
cmCTestCoverageHandlerContainer& Coverage;
};
cmParseBlanketJSCoverage::cmParseBlanketJSCoverage(
cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
:Coverage(cont), CTest(ctest)
{
}
bool cmParseBlanketJSCoverage::LoadCoverageData(std::vector<std::string> files)
{
size_t i=0;
std::string path;
cmCTestLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Found " << files.size() <<" Files" << std::endl);
for(i=0;i<files.size();i++)
{
cmCTestLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Reading JSON File " << files[i] << std::endl);
if(!this->ReadJSONFile(files[i]))
{
return false;
}
}
return true;
}
bool cmParseBlanketJSCoverage::ReadJSONFile(std::string file)
{
cmParseBlanketJSCoverage::JSONParser parser
(this->Coverage);
cmCTestLog(this->CTest,HANDLER_VERBOSE_OUTPUT,
"Parsing " << file << std::endl);
parser.ParseFile(file);
return true;
}

View File

@ -0,0 +1,48 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc.
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmParseBlanketJSCoverage_h
#define cmParseBlanketJSCoverage_h
#include "cmStandardIncludes.h"
#include "cmCTestCoverageHandler.h"
/** \class cmParseBlanketJSCoverage
* \brief Parse BlanketJS coverage information
*
* This class is used to parse BlanketJS(Pascal) coverage information
* generated by the Blanket.js library when used in conjunction with the
* test runner mocha.js, which is used to write out the JSON format.
*
* Blanket.js:
* http://blanketjs.org/
*
* Mocha.js
* http://visionmedia.github.io/mocha/
*/
class cmParseBlanketJSCoverage
{
public:
cmParseBlanketJSCoverage(cmCTestCoverageHandlerContainer& cont,
cmCTest* ctest);
bool LoadCoverageData(std::vector<std::string> files);
// Read the JSON output
bool ReadJSONFile(std::string file);
protected:
class JSONParser;
cmCTestCoverageHandlerContainer& Coverage;
cmCTest* CTest;
};
#endif

View File

@ -2339,6 +2339,25 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
"Process file.*CoverageTest.java.*Total LOC:.*17.*Percentage Coverage: 76.47*"
ENVIRONMENT COVFILE=)
# Adding a test case for Javascript Coverage
configure_file(
"${CMake_SOURCE_DIR}/Tests/JavascriptCoverage/DartConfiguration.tcl.in"
"${CMake_BINARY_DIR}/Testing/JavascriptCoverage/DartConfiguration.tcl")
configure_file(
"${CMake_SOURCE_DIR}/Tests/JavascriptCoverage/output.json.in"
"${CMake_BINARY_DIR}/Testing/JavascriptCoverage/output.json")
file(COPY "${CMake_SOURCE_DIR}/Tests/JavascriptCoverage/"
DESTINATION "${CMake_BINARY_DIR}/Testing/JavascriptCoverage"
FILES_MATCHING PATTERN "*.js")
add_test(NAME CTestJavascriptCoverage
COMMAND cmake -E chdir
${CMake_BINARY_DIR}/Testing/JavascriptCoverage
$<TARGET_FILE:ctest> -T Coverage --debug)
set_tests_properties(CTestJavascriptCoverage PROPERTIES
PASS_REGULAR_EXPRESSION
"Process file.*test3.js.*Total LOC:.*49.*Percentage Coverage: 79.59*"
ENVIRONMENT COVFILE=)
# test coverage for Delphi-code-Coverage
configure_file(
"${CMake_SOURCE_DIR}/Tests/DelphiCoverage/DartConfiguration.tcl.in"

View File

@ -0,0 +1,8 @@
# This file is configured by CMake automatically as DartConfiguration.tcl
# If you choose not to use CMake, this file may be hand configured, by
# filling in the required variables.
# Configuration directories and files
SourceDirectory: ${CMake_BINARY_DIR}/Testing/JavascriptCoverage
BuildDirectory: ${CMake_BINARY_DIR}/Testing/JavascriptCoverage

View File

@ -0,0 +1,448 @@
{
"instrumentation": "node-jscoverage",
"sloc": 29,
"hits": 28,
"misses": 1,
"coverage": 96.55172413793103,
"files": [
{
"filename": "${CMake_BINARY_DIR}/Testing/JavascriptCoverage/test.js",
"coverage": 96.55172413793103,
"hits": 28,
"misses": 1,
"sloc": 29,
"source": {
"1": {
"source": "var assert = require(\"assert\")",
"coverage": 1
},
"2": {
"source": "var test = {",
"coverage": 1
},
"3": {
"source": " version: \"1.0.0\"",
"coverage": ""
},
"4": {
"source": "}",
"coverage": ""
},
"5": {
"source": "function covTest(p1,p2) {",
"coverage": 1
},
"6": {
"source": " if (p1 > 3) {",
"coverage": 2
},
"7": {
"source": " return 1;",
"coverage": 1
},
"8": {
"source": " }",
"coverage": ""
},
"9": {
"source": " else {",
"coverage": ""
},
"10": {
"source": " return p1 + p2;",
"coverage": 1
},
"11": {
"source": " }",
"coverage": ""
},
"12": {
"source": "}",
"coverage": ""
},
"13": {
"source": "",
"coverage": ""
},
"14": {
"source": "function covTest2(p1,p2) {",
"coverage": 1
},
"15": {
"source": " return 0;",
"coverage": 0
},
"16": {
"source": "}",
"coverage": ""
},
"17": {
"source": "",
"coverage": ""
},
"18": {
"source": "function covTest3(p1) {",
"coverage": 1
},
"19": {
"source": " for(i=0;i < p1;i++){",
"coverage": 1
},
"20": {
"source": " }",
"coverage": ""
},
"21": {
"source": " return i;",
"coverage": 1
},
"22": {
"source": "}",
"coverage": ""
},
"23": {
"source": "function covTest4(p1) {",
"coverage": 1
},
"24": {
"source": " i=0;",
"coverage": 1
},
"25": {
"source": " while(i < p1){",
"coverage": 1
},
"26": {
"source": " i++;",
"coverage": 5
},
"27": {
"source": " }",
"coverage": ""
},
"28": {
"source": " return i;",
"coverage": 1
},
"29": {
"source": "}",
"coverage": ""
},
"30": {
"source": "",
"coverage": ""
},
"31": {
"source": "describe('Array', function(){",
"coverage": 1
},
"32": {
"source": " describe('CovTest', function(){",
"coverage": 1
},
"33": {
"source": " it('should return when the value is not present', function(){",
"coverage": 1
},
"34": {
"source": " assert.equal(4,covTest(2,2));",
"coverage": 1
},
"35": {
"source": " })",
"coverage": ""
},
"36": {
"source": " })",
"coverage": ""
},
"37": {
"source": " ",
"coverage": ""
},
"38": {
"source": " describe('CovTest>3', function(){",
"coverage": 1
},
"39": {
"source": " it('should return when the value is not present', function(){",
"coverage": 1
},
"40": {
"source": " assert.equal(1,covTest(4,2));",
"coverage": 1
},
"41": {
"source": " })",
"coverage": ""
},
"42": {
"source": " })",
"coverage": ""
},
"43": {
"source": " describe('covTest4', function(){",
"coverage": 1
},
"44": {
"source": " it('should return when the value is not present', function(){",
"coverage": 1
},
"45": {
"source": " assert.equal(5,covTest4(5));",
"coverage": 1
},
"46": {
"source": " })",
"coverage": ""
},
"47": {
"source": " })",
"coverage": ""
},
"48": {
"source": " describe('covTest3', function(){",
"coverage": 1
},
"49": {
"source": " it('should return when the value is not present', function(){",
"coverage": 1
},
"50": {
"source": " assert.equal(5,covTest3(5));",
"coverage": 1
},
"51": {
"source": " })",
"coverage": ""
},
"52": {
"source": " })",
"coverage": ""
},
"53": {
"source": "})",
"coverage": ""
},
"54": {
"source": "",
"coverage": ""
}
}
"filename": "${CMake_BINARY_DIR}/Testing/JavascriptCoverage/test3.js",
"coverage": 55.00000000000001,
"hits": 11,
"misses": 9,
"sloc": 20,
"source": {
"1": {
"source": "var assert = require(\"assert\")",
"coverage": 1
},
"2": {
"source": "var test = {",
"coverage": 1
},
"3": {
"source": " version: \"1.0.0\"",
"coverage": ""
},
"4": {
"source": "}",
"coverage": ""
},
"5": {
"source": "function covTest(p1,p2) {",
"coverage": 1
},
"6": {
"source": " if (p1 > 3) {",
"coverage": 0
},
"7": {
"source": " return 1;",
"coverage": 0
},
"8": {
"source": " }",
"coverage": ""
},
"9": {
"source": " else {",
"coverage": ""
},
"10": {
"source": " return p1 + p2;",
"coverage": 0
},
"11": {
"source": " }",
"coverage": ""
},
"12": {
"source": "}",
"coverage": ""
},
"13": {
"source": "",
"coverage": ""
},
"14": {
"source": "function covTest2(p1,p2) {",
"coverage": 1
},
"15": {
"source": " return 0;",
"coverage": 1
},
"16": {
"source": "}",
"coverage": ""
},
"17": {
"source": "",
"coverage": ""
},
"18": {
"source": "function covTest3(p1) {",
"coverage": 1
},
"19": {
"source": " for(i=0;i < p1;i++){",
"coverage": 0
},
"20": {
"source": " }",
"coverage": ""
},
"21": {
"source": " return i;",
"coverage": 0
},
"22": {
"source": "}",
"coverage": ""
},
"23": {
"source": "function covTest4(p1) {",
"coverage": 1
},
"24": {
"source": " i=0;",
"coverage": 0
},
"25": {
"source": " while(i < p1){",
"coverage": 0
},
"26": {
"source": " i++;",
"coverage": 0
},
"27": {
"source": " }",
"coverage": ""
},
"28": {
"source": " return i;",
"coverage": 0
},
"29": {
"source": "}",
"coverage": ""
},
"30": {
"source": "",
"coverage": ""
},
"31": {
"source": "describe('Array', function(){",
"coverage": 1
},
"32": {
"source": " describe('CovTest2', function(){",
"coverage": 1
},
"33": {
"source": " it('should return when the value is not present', function(){",
"coverage": 1
},
"34": {
"source": " assert.equal(0,covTest2(2,2));",
"coverage": 1
},
"35": {
"source": " })",
"coverage": ""
},
"36": {
"source": " })",
"coverage": ""
},
"37": {
"source": "})",
"coverage": ""
},
"38": {
"source": "",
"coverage": ""
}
}
}
],
"stats": {
"suites": 5,
"tests": 4,
"passes": 4,
"pending": 0,
"failures": 0,
"start": "2014-10-23T17:56:02.339Z",
"end": "2014-10-23T17:56:02.344Z",
"duration": 5
},
"tests": [
{
"title": "should return when the value is not present",
"fullTitle": "Array CovTest should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array CovTest>3 should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array covTest4 should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array covTest3 should return when the value is not present",
"duration": 0
}
],
"failures": [],
"passes": [
{
"title": "should return when the value is not present",
"fullTitle": "Array CovTest should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array CovTest>3 should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array covTest4 should return when the value is not present",
"duration": 0
},
{
"title": "should return when the value is not present",
"fullTitle": "Array covTest3 should return when the value is not present",
"duration": 0
}
]
}

View File

@ -0,0 +1,53 @@
var assert = require("assert")
var test = {
version: "1.0.0"
}
function covTest(p1,p2) {
if (p1 > 3) {
return 1;
}
else {
return p1 + p2;
}
}
function covTest2(p1,p2) {
return 0;
}
function covTest3(p1) {
for(i=0;i < p1;i++){
}
return i;
}
function covTest4(p1) {
i=0;
while(i < p1){
i++;
}
return i;
}
describe('Array', function(){
describe('CovTest', function(){
it('should return when the value is not present', function(){
assert.equal(4,covTest(2,2));
})
})
describe('CovTest>3', function(){
it('should return when the value is not present', function(){
assert.equal(1,covTest(4,2));
})
})
describe('covTest4', function(){
it('should return when the value is not present', function(){
assert.equal(5,covTest4(5));
})
})
describe('covTest3', function(){
it('should return when the value is not present', function(){
assert.equal(5,covTest3(5));
})
})
})

View File

@ -0,0 +1,37 @@
var assert = require("assert")
var test = {
version: "1.0.0"
}
function covTest(p1,p2) {
if (p1 > 3) {
return 1;
}
else {
return p1 + p2;
}
}
function covTest2(p1,p2) {
return 0;
}
function covTest3(p1) {
for(i=0;i < p1;i++){
}
return i;
}
function covTest4(p1) {
i=0;
while(i < p1){
i++;
}
return i;
}
describe('Array', function(){
describe('CovTest2', function(){
it('should return when the value is not present', function(){
assert.equal(0,covTest2(2,2));
})
})
})