From bc9c381027499b53e26301d3f55a8d6321d3e3a2 Mon Sep 17 00:00:00 2001 From: Wayne Andrews Date: Tue, 10 Jan 2017 09:21:30 +0000 Subject: [PATCH] build: add code coverage to make PR-URL: https://github.com/nodejs/node/pull/10856 Reviewed-By: Jeremiah Senkpiel Reviewed-By: Anna Henningsen Reviewed-By: Michael Dawson --- Makefile | 67 +++++++++++++++++++++++++- lib/internal/bootstrap_node.js | 2 + lib/internal/process/write-coverage.js | 46 ++++++++++++++++++ node.gyp | 1 + test/common.js | 9 +++- 5 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 lib/internal/process/write-coverage.js diff --git a/Makefile b/Makefile index 4aaef26da24d23..9c28a3e4f4f88e 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ TEST_CI_ARGS ?= STAGINGSERVER ?= node-www LOGLEVEL ?= silent OSTYPE := $(shell uname -s | tr '[A-Z]' '[a-z]') +COVTESTS ?= test ifdef JOBS PARALLEL_ARGS = -j $(JOBS) @@ -113,6 +114,69 @@ distclean: check: test +# Remove files generated by running coverage, put the non-instrumented lib back +# in place +coverage-clean: + if [ -d lib_ ]; then rm -rf lib; mv lib_ lib; fi + -rm -rf node_modules + -rm -rf gcovr testing + -rm -rf out/$(BUILDTYPE)/.coverage + -rm -rf .cov_tmp coverage + -rm -f out/$(BUILDTYPE)/obj.target/node/src/*.gcda + -rm -f out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcda + -rm -f out/$(BUILDTYPE)/obj.target/node/src/*.gcno + -rm -f out/$(BUILDTYPE)/obj.target/node/src/tracing*.gcno + +# Build and test with code coverage reporting. Leave the lib directory +# instrumented for any additional runs the user may want to make. +# For C++ coverage reporting, this needs to be run in conjunction with configure +# --coverage. html coverage reports will be created under coverage/ + +coverage: coverage-test + +coverage-build: all + mkdir -p node_modules + if [ ! -d node_modules/istanbul-merge ]; then \ + $(NODE) ./deps/npm install istanbul-merge; fi + if [ ! -d node_modules/nyc ]; then $(NODE) ./deps/npm install nyc; fi + if [ ! -d gcovr ]; then git clone --depth=1 \ + --single-branch git://github.com/gcovr/gcovr.git; fi + if [ ! -d testing ]; then git clone --depth=1 \ + --single-branch https://github.com/nodejs/testing.git; fi + if [ ! -f gcovr/scripts/gcovr.orig ]; then \ + (cd gcovr && patch -N -p1 < \ + "$(CURDIR)/testing/coverage/gcovr-patches.diff"); fi + if [ -d lib_ ]; then rm -rf lib; mv lib_ lib; fi + mv lib lib_ + $(NODE) ./node_modules/.bin/nyc instrument lib_/ lib/ + $(MAKE) + +coverage-test: coverage-build + -rm -rf out/$(BUILDTYPE)/.coverage + -rm -rf .cov_tmp + -rm -f out/$(BUILDTYPE)/obj.target/node/src/*.gcda + -rm -f out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcda + -$(MAKE) $(COVTESTS) + mv lib lib__ + mv lib_ lib + mkdir -p coverage .cov_tmp + $(NODE) ./node_modules/.bin/istanbul-merge --out \ + .cov_tmp/libcov.json 'out/Release/.coverage/coverage-*.json' + (cd lib && .$(NODE) ../node_modules/.bin/nyc report \ + --temp-directory "$(CURDIR)/.cov_tmp" -r html \ + --report-dir "../coverage") + -(cd out && "../gcovr/scripts/gcovr" --gcov-exclude='.*deps' \ + --gcov-exclude='.*usr' -v -r Release/obj.target/node \ + --html --html-detail -o ../coverage/cxxcoverage.html) + mv lib lib_ + mv lib__ lib + @echo -n "Javascript coverage %: " + @grep -B1 Lines coverage/index.html | head -n1 \ + | sed 's/<[^>]*>//g'| sed 's/ //g' + @echo -n "C++ coverage %: " + @grep -A3 Lines coverage/cxxcoverage.html | grep style \ + | sed 's/<[^>]*>//g'| sed 's/ //g' + cctest: all @out/$(BUILDTYPE)/$@ @@ -781,4 +845,5 @@ endif bench-all bench bench-misc bench-array bench-buffer bench-net \ bench-http bench-fs bench-tls cctest run-ci test-v8 test-v8-intl \ test-v8-benchmarks test-v8-all v8 lint-ci bench-ci jslint-ci doc-only \ - $(TARBALL)-headers test-ci test-ci-native test-ci-js build-ci clear-stalled + $(TARBALL)-headers test-ci test-ci-native test-ci-js build-ci clear-stalled \ + coverage-clean coverage-build coverage-test coverage diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index ccabffd8a6e342..be069d67a31816 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -44,6 +44,8 @@ NativeModule.require('internal/process/stdio').setup(); _process.setupKillAndExit(); _process.setupSignalHandlers(); + if (global.__coverage__) + NativeModule.require('internal/process/write-coverage').setup(); // Do not initialize channel in debugger agent, it deletes env variable // and the main thread won't see it. diff --git a/lib/internal/process/write-coverage.js b/lib/internal/process/write-coverage.js new file mode 100644 index 00000000000000..6bbc59a6e981be --- /dev/null +++ b/lib/internal/process/write-coverage.js @@ -0,0 +1,46 @@ +'use strict'; +const process = require('process'); +const path = require('path'); +const fs = require('fs'); +const mkdirSync = fs.mkdirSync; +const writeFileSync = fs.writeFileSync; + +var isWritingCoverage = false; +function writeCoverage() { + if (isWritingCoverage || !global.__coverage__) { + return; + } + isWritingCoverage = true; + + const dirname = path.join(path.dirname(process.execPath), '.coverage'); + const filename = `coverage-${process.pid}-${Date.now()}.json`; + try { + mkdirSync(dirname); + } catch (err) { + if (err.code !== 'EEXIST') { + console.error(err); + return; + } + } + + const target = path.join(dirname, filename); + const coverageInfo = JSON.stringify(global.__coverage__); + try { + writeFileSync(target, coverageInfo); + } catch (err) { + console.error(err); + } +} + +function setup() { + const reallyReallyExit = process.reallyExit; + + process.reallyExit = function(code) { + writeCoverage(); + reallyReallyExit(code); + }; + + process.on('exit', writeCoverage); +} + +exports.setup = setup; diff --git a/node.gyp b/node.gyp index 50d66837a62f33..d877bb22b69db7 100644 --- a/node.gyp +++ b/node.gyp @@ -93,6 +93,7 @@ 'lib/internal/process/warning.js', 'lib/internal/process.js', 'lib/internal/querystring.js', + 'lib/internal/process/write-coverage.js', 'lib/internal/readline.js', 'lib/internal/repl.js', 'lib/internal/socket_list.js', diff --git a/test/common.js b/test/common.js index 6cdf698fb5e839..8fe1d3c91e7b4f 100644 --- a/test/common.js +++ b/test/common.js @@ -284,6 +284,9 @@ exports.platformTimeout = function(ms) { if (process.config.target_defaults.default_configuration === 'Debug') ms = 2 * ms; + if (global.__coverage__) + ms = 4 * ms; + if (exports.isAix) return 2 * ms; // default localhost speed is slower on AIX @@ -381,7 +384,11 @@ function leakedGlobals() { if (!knownGlobals.includes(global[val])) leaked.push(val); - return leaked; + if (global.__coverage__) { + return leaked.filter((varname) => !/^(cov_|__cov)/.test(varname)); + } else { + return leaked; + } } exports.leakedGlobals = leakedGlobals;