diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0ff1125a2..4eaaf4cf5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -65,3 +65,13 @@ jobs: run: | bazel --bazelrc=$GITHUB_WORKSPACE/.github/workflows/ci.bazelrc --bazelrc=.bazelrc test --config=${{ matrix.config }} //... ls $(bazel info output_base)/external | grep -v __links | grep -vz unused + + - name: bazel coverage //... + env: + # Bazelisk will download bazel to here + XDG_CACHE_HOME: ~/.cache/bazel-repo + working-directory: ${{ matrix.folder }} + # Coverage does not work properly with RBE. See: bazelbuild/bazel#4685 + if: ${{ matrix.config == 'local' }} + run: | + bazel --bazelrc=$GITHUB_WORKSPACE/.github/workflows/ci.bazelrc --bazelrc=.bazelrc coverage --config=${{ matrix.config }} --instrument_test_targets //... diff --git a/js/private/coverage/bundle/c8.js b/js/private/coverage/bundle/c8.js index 2a3ac657c..86b70b064 100644 --- a/js/private/coverage/bundle/c8.js +++ b/js/private/coverage/bundle/c8.js @@ -2,6 +2,18 @@ import { Report } from 'c8' import fs from 'fs' import path from 'path' +// bazel will create the COVERAGE_OUTPUT_FILE whilst setting up the sandbox. +// therefore, should be doing a file size check rather than presence. +try { + const stats = fs.statSync(process.env.COVERAGE_OUTPUT_FILE) + if (stats.size != 0) { + // early exit here does not affect the outcome of the tests. + // bazel will only execute _lcov_merger when tests pass. + process.exit(0) + } + // in case file doesn't exist or some other error is thrown, just ignore it. +} catch {} + const include = fs .readFileSync(process.env.COVERAGE_MANIFEST) .toString('utf8') diff --git a/js/private/coverage/coverage.js b/js/private/coverage/coverage.js index 3d606e56a..82476c4d6 100755 --- a/js/private/coverage/coverage.js +++ b/js/private/coverage/coverage.js @@ -10983,6 +10983,18 @@ var report = function (opts) { var Report = report; +// bazel will create the COVERAGE_OUTPUT_FILE whilst setting up the sandbox. +// therefore, should be doing a file size check rather than presence. +try { + const stats = require$$0__default$1["default"].statSync(process.env.COVERAGE_OUTPUT_FILE); + if (stats.size != 0) { + // early exit here does not affect the outcome of the tests. + // bazel will only execute _lcov_merger when tests pass. + process.exit(0); + } + // in case file doesn't exist or some other error is thrown, just ignore it. +} catch {} + const include = require$$0__default$1["default"] .readFileSync(process.env.COVERAGE_MANIFEST) .toString('utf8') diff --git a/js/private/test/coverage/BUILD.bazel b/js/private/test/coverage/BUILD.bazel new file mode 100644 index 000000000..a422fcc4f --- /dev/null +++ b/js/private/test/coverage/BUILD.bazel @@ -0,0 +1,59 @@ +load(":test.bzl", "coverage_fail_test", "coverage_pass_test") +load("//js/private/coverage:merger.bzl", "coverage_merger") + +FAIL_CMD = """\ +echo "require('fs').writeFileSync(process.env.COVERAGE_OUTPUT_FILE, '# no coverage');" >> $@ +cat $(location //js/private/coverage:coverage.js) >> $@ +echo "process.on('exit', () => require('assert').equal(require('fs').readFileSync(process.env.COVERAGE_OUTPUT_FILE).toString(), '# no coverage'))" >> $@ +""" + +genrule( + name = "fail_merger_entrypoint", + srcs = ["//js/private/coverage:coverage.js"], + outs = ["fail_merger_entrypoint.js"], + cmd = FAIL_CMD, +) + +coverage_merger( + name = "fail_merger", + entry_point = ":fail_merger_entrypoint.js", + visibility = ["//visibility:public"], +) + +coverage_fail_test( + name = "fail", + data = ["lib.js"], + enable_runfiles = select({ + "@aspect_rules_js//js/private:enable_runfiles": True, + "//conditions:default": False, + }), + entry_point = "lib.js", +) + +PASS_CMD = """\ +cat $(location //js/private/coverage:coverage.js) >> $@ +echo "process.on('exit', () => require('assert').equal(require('fs').readFileSync(process.env.COVERAGE_OUTPUT_FILE).toString().split('\\n')[1], 'SF:js/private/test/coverage/lib.js'))" >> $@ +""" + +genrule( + name = "pass_merger_entrypoint", + srcs = ["//js/private/coverage:coverage.js"], + outs = ["pass_merger_entrypoint.js"], + cmd = PASS_CMD, +) + +coverage_merger( + name = "pass_merger", + entry_point = ":pass_merger_entrypoint.js", + visibility = ["//visibility:public"], +) + +coverage_pass_test( + name = "pass", + data = ["lib.js"], + enable_runfiles = select({ + "@aspect_rules_js//js/private:enable_runfiles": True, + "//conditions:default": False, + }), + entry_point = "lib.js", +) diff --git a/js/private/test/coverage/lib.js b/js/private/test/coverage/lib.js new file mode 100644 index 000000000..a20bd7baf --- /dev/null +++ b/js/private/test/coverage/lib.js @@ -0,0 +1,13 @@ +if (true) { + itstrue() +} else { + itsfalse() +} + +function itstrue() { + console.log('its true') +} + +function itsfalse() { + console.log('its false') +} diff --git a/js/private/test/coverage/test.bzl b/js/private/test/coverage/test.bzl new file mode 100644 index 000000000..258a55b05 --- /dev/null +++ b/js/private/test/coverage/test.bzl @@ -0,0 +1,27 @@ +load("//js/private:js_binary.bzl", "js_binary_lib") + +coverage_fail_test = rule( + implementation = js_binary_lib.implementation, + attrs = dict(js_binary_lib.attrs, **{ + "_lcov_merger": attr.label( + executable = True, + default = Label("//js/private/test/coverage:fail_merger"), + cfg = "exec", + ), + }), + test = True, + toolchains = js_binary_lib.toolchains, +) + +coverage_pass_test = rule( + implementation = js_binary_lib.implementation, + attrs = dict(js_binary_lib.attrs, **{ + "_lcov_merger": attr.label( + executable = True, + default = Label("//js/private/test/coverage:pass_merger"), + cfg = "exec", + ), + }), + test = True, + toolchains = js_binary_lib.toolchains, +)