From 6234fa4ca9ef1f63dbfcd6538aa21f1f36d03388 Mon Sep 17 00:00:00 2001 From: Mikita Hradovich Date: Mon, 24 Aug 2026 23:03:22 +0200 Subject: [PATCH 1/6] build: add an opt-in coverage profile with a dedicated argline property Introduce a `coverage` profile that attaches the JaCoCo agent to the forked test JVMs. It is opt-in because the agent slows every fork down and all existing test lanes have to stay able to run without it. The agent argument goes into a dedicated `jacoco.argline` property rather than surefire's conventional `argLine`, for two reasons: - the surefire argLine already carries -Djdk.attach.allowAttachSelf=true, so prepare-agent would have to preserve it; - a dedicated property can be declared empty in , which guarantees that surefire's late-replaced @{jacoco.argline} always resolves. Without a declared default, any build that skips prepare-agent -- no profile at all, or -Djacoco.skip=true -- passes the literal token to the forked JVM, which then dies with "Unrecognized option". Because 3.x runs its integration tests as TestNG `short`-group tests through surefire, this single argLine change instruments the unit lane and every integration lane. Failsafe is deliberately left alone. It only runs the tests under driver-tests/** (OSGi, shading), which are outside the scope of the report; the OSGi ones additionally load the driver inside their own Pax Exam container, where an agent on the Failsafe JVM would see little of it. Refs: DRIVER-892 --- pom.xml | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea0d22cae71..177f240b862 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,12 @@ updated. --> 1.5.9 3.5.4 + 0.8.14 + + 127.0.1. unit @@ -734,13 +740,19 @@ + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + maven-surefire-plugin ${surefire.version} ${test.groups} false - -Djdk.attach.allowAttachSelf=true + -Djdk.attach.allowAttachSelf=true @{jacoco.argline} alphabetical ${cassandra.version} @@ -941,6 +953,36 @@ + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + prepare-agent + + prepare-agent + + + + jacoco.argline + + + + + + + + + + + 4.0.0 + + + com.scylladb + scylla-driver-parent + 3.11.5.19-SNAPSHOT + + + scylla-driver-coverage-report + pom + Java Driver for Scylla and Apache Cassandra - Coverage report + Aggregates the JaCoCo execution data produced by the other modules into a single + coverage report. Produces no released artifact. + + + + + + com.scylladb + scylla-driver-core + + + + com.scylladb + scylla-driver-mapping + + + + com.scylladb + scylla-driver-extras + + + + + + + + + + + + org.codehaus.mojo + clirr-maven-plugin + + true + + + + + maven-source-plugin + + true + + + + + maven-javadoc-plugin + + true + + + + + maven-gpg-plugin + + true + + + + + maven-install-plugin + + true + + + + + maven-deploy-plugin + + true + + + + + + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + report-aggregate + verify + + report-aggregate + + + Java Driver for Scylla and Apache Cassandra 3.x + + HTML + XML + CSV + + ${project.build.directory}/site/jacoco-aggregate + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 177f240b862..16e6ea20f5f 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,7 @@ driver-examples driver-tests driver-dist + driver-coverage-report @@ -881,7 +882,7 @@ true central - scylla-driver-tests-parent,scylla-driver-tests-osgi,scylla-driver-tests-osgi-common,scylla-driver-tests-osgi-shaded,scylla-driver-tests-shading,scylla-driver-tests-shading-shaded,scylla-driver-tests-shading-unshaded,scylla-driver-tests-osgi-unshaded,scylla-driver-tests-stress,scylla-driver-dist,scylla-driver-examples + scylla-driver-coverage-report,scylla-driver-tests-parent,scylla-driver-tests-osgi,scylla-driver-tests-osgi-common,scylla-driver-tests-osgi-shaded,scylla-driver-tests-shading,scylla-driver-tests-shading-shaded,scylla-driver-tests-shading-unshaded,scylla-driver-tests-osgi-unshaded,scylla-driver-tests-stress,scylla-driver-dist,scylla-driver-examples ${release.autopublish} validated From e12ca7af187e3be7b406e8d0e3ff090239e73874 Mon Sep 17 00:00:00 2001 From: Mikita Hradovich Date: Mon, 24 Aug 2026 23:09:54 +0200 Subject: [PATCH 3/6] build: expose coverage through the Makefile `COVERAGE=true` on any of the test-* targets turns the profile on, rather than duplicating each recipe into a -coverage variant. It also pulls in a step that removes jacoco.exec first: JaCoCo appends by default, which is what lets one lane accumulate coverage across several forks, but it also means data from an earlier run survives a recompile and classes that changed in between get reported as uncovered because their checksum no longer matches. Only jacoco.exec is removed, so data deliberately renamed out of the way to hold one lane's results while another runs is kept; `make clean-coverage` is the one that removes everything. `make coverage-report` aggregates whatever execution data is on disk with tests skipped, so the same target serves one local lane and execution data collected from several CI jobs. Three things it refuses to do quietly: - it deletes the previous report first, so a failed Maven invocation cannot leave a stale report to be read as this run's result; - it stops before Maven when there is no execution data at all, instead of rendering a report that says 0.00%, which is what JaCoCo does when it loads nothing; - it fails if the finished report has no covered lines, which is the shape a checksum mismatch takes. The percentage comes from the LINE counter in jacoco.xml, not from positional fields of the unquoted CSV. Verified locally on JDK 8 and 11: `make test-unit COVERAGE=true` passes 976 unit tests with the agent attached and `make coverage-report` reports 9033/22602 lines (39.97%) with no checksum mismatches. Running the two-lane recipe from CONTRIBUTING.md gives 1.62% for the second lane alone and 1.85% for both, so the execution data is merged rather than one file winning. Refs: DRIVER-892 --- Makefile | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 76430900850..fb204296b5b 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,18 @@ SONATYPE_TOKEN_PASSWORD ?= RELEASE_SKIP_TESTS ?= false RELEASE_TARGET_TAG ?= +# Set COVERAGE=true on any of the test-* targets to attach the JaCoCo agent to the forked test +# JVMs; `make coverage-report` then aggregates whatever execution data is on disk. +COVERAGE ?= false +ifeq ($(filter true 1,$(COVERAGE)),) + MVN_COVERAGE := + COVERAGE_PREREQ := +else + MVN_COVERAGE := -Pcoverage + COVERAGE_PREREQ := .clean-coverage-data +endif +COVERAGE_REPORT_DIR := driver-coverage-report/target/site/jacoco-aggregate + ifeq (${CCM_CONFIG_DIR},) CCM_CONFIG_DIR = ~/.ccm endif @@ -67,6 +79,16 @@ export PATH := $(MAKEFILE_PATH)/bin:$(PATH) $(MAKE) install-cassandra-ccm fi +# JaCoCo appends to its execution data files by default, which is what lets a single lane +# accumulate coverage across several forks. The flip side is that data from an earlier run +# survives a recompile, and classes that changed in between are then reported as uncovered +# because their checksum no longer matches. Truncating before a run is the fix. +# +# Only jacoco.exec, the file the agent is about to write, is removed. Data that was renamed +# out of the way to keep one lane's results while another runs is left alone. +.clean-coverage-data: + @find . -name 'jacoco.exec' -delete + .prepare-environment-update-aio-max-nr: @if (( $$(< /proc/sys/fs/aio-max-nr) < 2097152 )); then echo 2097152 | sudo tee /proc/sys/fs/aio-max-nr >/dev/null @@ -240,10 +262,10 @@ check: fix: $(MVNCMD) fmt:format -test-unit: - $(MVNCMD) test -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true +test-unit: $(COVERAGE_PREREQ) + $(MVNCMD) test $(MVN_COVERAGE) -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true -test-integration-scylla: .prepare-scylla-ccm resolve-scylla-version .prepare-environment-update-aio-max-nr +test-integration-scylla: .prepare-scylla-ccm resolve-scylla-version .prepare-environment-update-aio-max-nr $(COVERAGE_PREREQ) @if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then SCYLLA_VERSION_RESOLVED=`cat '${SCYLLA_VERSION_FILE}'` fi @@ -251,9 +273,9 @@ test-integration-scylla: .prepare-scylla-ccm resolve-scylla-version .prepare-env echo "ScyllaDB version ${SCYLLA_VERSION} was not resolved" exit 1 fi - mvn -B verify -Pshort -Dscylla.version=$${SCYLLA_VERSION_RESOLVED} -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true + mvn -B verify -Pshort $(MVN_COVERAGE) -Dscylla.version=$${SCYLLA_VERSION_RESOLVED} -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true -test-integration-cassandra: .prepare-scylla-ccm resolve-cassandra-version +test-integration-cassandra: .prepare-scylla-ccm resolve-cassandra-version $(COVERAGE_PREREQ) @if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then CASSANDRA_VERSION_RESOLVED=`cat '${CASSANDRA_VERSION_FILE}'` fi @@ -261,7 +283,30 @@ test-integration-cassandra: .prepare-scylla-ccm resolve-cassandra-version echo "Cassandra version ${CASSANDRA_VERSION} was not resolved" exit 1 fi - mvn -B verify -Pshort -Dcassandra.version=$${CASSANDRA_VERSION_RESOLVED} -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true + mvn -B verify -Pshort $(MVN_COVERAGE) -Dcassandra.version=$${CASSANDRA_VERSION_RESOLVED} -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true + +# Aggregates the execution data left behind by any COVERAGE=true test run into a single report. +# Tests are skipped here on purpose: this only reads what is already on disk, so the same target +# works for one local lane and for execution data collected from several CI jobs. +coverage-report: + @set -o pipefail + if [[ -z "$$(find . -name 'jacoco*.exec' -print -quit)" ]]; then + echo 'No JaCoCo execution data found.' + echo "Run the tests with COVERAGE=true first, e.g. 'make test-unit COVERAGE=true'." + exit 1 + fi + rm -rf '${COVERAGE_REPORT_DIR}' + $(MVNCMD) -Pcoverage -DskipTests verify -pl driver-coverage-report -am -Dfmt.skip=true -Dclirr.skip=true -Danimal.sniffer.skip=true + if [[ ! -f '${COVERAGE_REPORT_DIR}/jacoco.xml' ]]; then + echo 'Maven produced no report at ${COVERAGE_REPORT_DIR}/jacoco.xml.' + exit 1 + fi + echo 'HTML report: ${COVERAGE_REPORT_DIR}/index.html' + python3 -c 'import sys, xml.etree.ElementTree as ET; r = ET.parse(sys.argv[1]).getroot(); c = next(x for x in r.findall("counter") if x.get("type") == "LINE"); missed, covered = int(c.get("missed")), int(c.get("covered")); total = missed + covered; print("Line coverage: {}/{} ({:.2f}%)".format(covered, total, 100.0 * covered / total if total else 0.0)); sys.exit("No lines are recorded as covered: the execution data is either missing or does not match these classes. Look for a checksum mismatch warning in the Maven log.") if covered == 0 else None' '${COVERAGE_REPORT_DIR}/jacoco.xml' | tee -a "$${GITHUB_STEP_SUMMARY:-/dev/null}" + +clean-coverage: + @find . -name 'jacoco*.exec' -delete + rm -rf '${COVERAGE_REPORT_DIR}' check-no-compile-warnings: @$(MAKE) compile-all | grep WARNING >/tmp/all-compile-warnings.log || true From 18bc20f3b867f22b0b0bb151266ce23eada1bef5 Mon Sep 17 00:00:00 2001 From: Mikita Hradovich Date: Mon, 24 Aug 2026 23:12:58 +0200 Subject: [PATCH 4/6] ci: collect coverage from the unit and integration lanes The unit lane and all four integration lanes already run the tests worth measuring, so they collect the execution data and one small job aggregates it. Re-running a suite inside a dedicated coverage job would add roughly ninety minutes per pull request and make a known integration flake fail a second job. Each lane flattens its data to one file per module before uploading, because upload-artifact derives the artifact root from the common ancestor of the files it matched: a lane where only one module produced data would otherwise upload a differently shaped artifact. The aggregate job renames each file after the lane it came from and drops it in the module's target directory, which is where jacoco:report-aggregate looks. The job is continue-on-error and runs on !cancelled(): coverage is a metric, and a flaky integration test must not turn it into a second red mark on the pull request. It also overrides MVNCMD to drop -X, which would otherwise produce a log measured in hundreds of megabytes for a build that has nothing to debug. actions/download-artifact is new to this repository, so its pinned SHA has to be added to the Actions allowlist (see CONTRIBUTING.md) before the job can run. Refs: DRIVER-892 --- .github/workflows/tests@v1.yml | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/.github/workflows/tests@v1.yml b/.github/workflows/tests@v1.yml index 273bcd63115..936720539b8 100644 --- a/.github/workflows/tests@v1.yml +++ b/.github/workflows/tests@v1.yml @@ -129,8 +129,30 @@ jobs: key: ${{ runner.os }}-${{ matrix.java-version }}-maven-${{ hashFiles('**/pom.xml') }} - name: Run unit tests + env: + COVERAGE: "true" run: make test-unit + # Flattened into one file per module, so the artifact layout does not depend on how many + # modules happened to produce data. + - name: Collect coverage execution data + if: ${{ !cancelled() }} + run: | + shopt -s nullglob + mkdir -p coverage-exec + for exec_file in */target/jacoco.exec; do + cp "$exec_file" "coverage-exec/${exec_file%%/*}.exec" + done + ls -l coverage-exec + + - name: Upload coverage execution data + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: ${{ !cancelled() }} + with: + name: coverage-exec-unit + path: coverage-exec/ + if-no-files-found: warn + - name: Copy test results if: always() run: | @@ -237,8 +259,29 @@ jobs: id: run-integration-tests env: CASSANDRA_VERSION_RESOLVED: ${{ steps.cassandra-version.outputs.value }} + COVERAGE: "true" run: make test-integration-cassandra + # Flattened into one file per module, so the artifact layout does not depend on how many + # modules happened to produce data. + - name: Collect coverage execution data + if: ${{ !cancelled() }} + run: | + shopt -s nullglob + mkdir -p coverage-exec + for exec_file in */target/jacoco.exec; do + cp "$exec_file" "coverage-exec/${exec_file%%/*}.exec" + done + ls -l coverage-exec + + - name: Upload coverage execution data + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: ${{ !cancelled() }} + with: + name: coverage-exec-cassandra-${{ matrix.cassandra-version }} + path: coverage-exec/ + if-no-files-found: warn + - name: Copy test results if: steps.run-integration-tests.outcome == 'failure' run: | @@ -336,8 +379,29 @@ jobs: id: run-integration-tests env: SCYLLA_VERSION_RESOLVED: ${{ steps.scylla-version.outputs.value }} + COVERAGE: "true" run: make test-integration-scylla + # Flattened into one file per module, so the artifact layout does not depend on how many + # modules happened to produce data. + - name: Collect coverage execution data + if: ${{ !cancelled() }} + run: | + shopt -s nullglob + mkdir -p coverage-exec + for exec_file in */target/jacoco.exec; do + cp "$exec_file" "coverage-exec/${exec_file%%/*}.exec" + done + ls -l coverage-exec + + - name: Upload coverage execution data + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: ${{ !cancelled() }} + with: + name: coverage-exec-scylla-${{ matrix.scylla-version }} + path: coverage-exec/ + if-no-files-found: warn + - name: Copy test results if: steps.run-integration-tests.outcome == 'failure' run: | @@ -370,3 +434,67 @@ jobs: detailed_summary: true updateComment: false skip_annotations: true + + coverage-report: + name: Coverage report + runs-on: ubuntu-latest + needs: [unit-tests, cassandra-integration-tests, scylla-integration-tests] + # Runs even when a test lane failed: partial coverage data is still worth reporting, and + # continue-on-error keeps a flaky integration test from turning this metric into a second + # failure on the pull request. + if: ${{ !cancelled() }} + continue-on-error: true + timeout-minutes: 20 + + env: + # Overrides the Makefile default of `mvn -B -X -ntp`; this job has nothing to debug and -X + # buys a log measured in hundreds of megabytes. + MVNCMD: mvn -B -ntp + + steps: + - name: Checkout source + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + + - name: Set up JDK 8 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + java-version: 8 + distribution: 'temurin' + + - name: Restore maven repository cache + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-8-maven-${{ hashFiles('**/pom.xml') }} + + - name: Download coverage execution data + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + pattern: coverage-exec-* + path: coverage-exec + + # jacoco:report-aggregate picks up every *.exec in a module's target directory, so each lane's + # data only has to land there under a name of its own. + - name: Place execution data next to the classes it was recorded against + run: | + shopt -s nullglob + for lane in coverage-exec/*/; do + lane_name="$(basename "$lane")" + for exec_file in "$lane"*.exec; do + module="$(basename "$exec_file" .exec)" + mkdir -p "$module/target" + cp "$exec_file" "$module/target/jacoco-${lane_name#coverage-exec-}.exec" + done + done + find . -name 'jacoco-*.exec' -printf '%p\t%s bytes\n' + + - name: Aggregate coverage + run: make coverage-report + + - name: Upload coverage report + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: ${{ !cancelled() }} + with: + name: coverage-report + path: driver-coverage-report/target/site/jacoco-aggregate + if-no-files-found: error From fe601993c21d91342337c6152d8c43e2f9962e64 Mon Sep 17 00:00:00 2001 From: Mikita Hradovich Date: Mon, 24 Aug 2026 23:15:03 +0200 Subject: [PATCH 5/6] docs: document how to produce a coverage report Added to the "Running the tests" section of CONTRIBUTING.md, which is where the test instructions already live, rather than to README-dev.md, which is about building the docs. Covers the two things that are not obvious from the commands: what the report's scope is, and that JaCoCo matches execution data to classes by checksum, which is what makes stale data show up as uncovered code. Refs: DRIVER-892 --- CONTRIBUTING.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b7611a6d25..949c53ef371 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -125,6 +125,47 @@ sudo ifconfig lo0 alias 127.0.1.2 up ... ``` +### Code coverage + +Coverage is measured with [JaCoCo](https://www.jacoco.org/jacoco/) and is off by default: the agent +slows every forked test JVM down, so it is opt-in through the `coverage` Maven profile. Pass +`COVERAGE=true` to any of the `test-*` Make targets to enable it, then aggregate: + +``` +make test-unit COVERAGE=true +make coverage-report +``` + +`make coverage-report` reads whatever execution data is already on disk, so several lanes can be +combined into one number: + +``` +make test-unit COVERAGE=true +mv driver-core/target/jacoco.exec driver-core/target/jacoco-unit.exec +make test-integration-scylla COVERAGE=true +make coverage-report +``` + +The report lands in `driver-coverage-report/target/site/jacoco-aggregate` (HTML, XML and CSV), and +`make clean-coverage` removes it along with the execution data. In CI, the unit and integration jobs +upload their execution data and the "Coverage report" job aggregates it; the percentage shows up in +that job's summary and the HTML report is attached as an artifact. + +Two things to know about the scope of the report: + +- It covers the modules that `driver-coverage-report` depends on: `driver-core`, + `driver-mapping` and `driver-extras`. `driver-examples`, `driver-tests/**` and `driver-dist` + are out of scope. +- Only Surefire is instrumented. That covers the unit tests and, because 3.x runs its integration + tests as TestNG `short`-group tests, the integration tests as well. The Failsafe-run tests in + `driver-tests/**` (OSGi, shading) are left out: the OSGi ones load the driver inside their own + Pax Exam container, and those modules are outside the report's scope in any case. + +JaCoCo matches execution data to classes by checksum, so the data has to come from the same build of +the classes the report is rendered against. If a report shows code you know was exercised as +uncovered, look for `Execution data for class ... does not match` in the Maven log; the usual cause +is stale execution data from before a recompile, which `make clean-coverage` clears. + ## Updating GitHub Actions workflows GitHub Actions workflows in this repository pin all third-party actions to specific commit SHAs From 0ed6f110ba8eb45396d21c15ee63538d13f2a3ff Mon Sep 17 00:00:00 2001 From: Mikita Hradovich Date: Mon, 24 Aug 2026 23:15:37 +0200 Subject: [PATCH 6/6] chore: drop the dead Cobertura harness testing/bin/coverage is a Python 2 script (it still uses print statements) that drives `mvn cobertura:cobertura` and rsyncs the result to a server whose address was never filled in. The Cobertura plugin is not configured anywhere in the build, so the script could not have worked for years. Its README duplicates what CONTRIBUTING.md already says about CCM and loopback aliases, and the directory was in .gitignore, so nothing new could be added to it anyway. Removing both leaves one answer to "how do I get coverage". Refs: DRIVER-892 --- .gitignore | 2 - testing/README.md | 79 ------------------- testing/bin/coverage | 181 ------------------------------------------- 3 files changed, 262 deletions(-) delete mode 100644 testing/README.md delete mode 100755 testing/bin/coverage diff --git a/.gitignore b/.gitignore index 9458be86d97..d9e2683b75d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ target/ -cobertura-history/ -testing/ .settings .classpath .project diff --git a/testing/README.md b/testing/README.md deleted file mode 100644 index 3b378d3a99c..00000000000 --- a/testing/README.md +++ /dev/null @@ -1,79 +0,0 @@ -## Testing Prerequisites - -### Install CCM - - pip3 install https://github.com/scylladb/scylla-ccm/archive/master.zip - -### Setup CCM Loopbacks (required for OSX) - - # For basic ccm - sudo ifconfig lo0 alias 127.0.0.2 up - sudo ifconfig lo0 alias 127.0.0.3 up - - # Additional loopbacks for java-driver testing - sudo ifconfig lo0 alias 127.0.1.1 up - sudo ifconfig lo0 alias 127.0.1.2 up - sudo ifconfig lo0 alias 127.0.1.3 up - sudo ifconfig lo0 alias 127.0.1.4 up - sudo ifconfig lo0 alias 127.0.1.5 up - sudo ifconfig lo0 alias 127.0.1.6 up - - - -## Building the Driver - - mvn clean package - - - -## Testing the Driver - -### Unit Tests - -Use the following command to run only the unit tests: - - mvn test - -_**Estimated Run Time**: x minutes_ - -### Integration Tests - -The following command runs the full set of unit and integration tests: - - mvn verify - -_**Estimated Run Time**: 4 minutes_ - -### Coverage Report - -The following command runs the full set of integration tests and produces a -coverage report: - - mvn cobertura:cobertura - -Coverage report can be found at: - - driver-core/target/site/cobertura/index.html - -_**Estimated Run Time**: 4 minutes_ - - - -## Test Utility - -`testing/bin/coverage` exists to make testing a bit more straight-forward. - -The main commands are as follows: - -Displays the available parameters: - - testing/bin/coverage --help - -Runs all the integration tests, creates the Cobertura report, and uploads Cobertura -site to a remote machine, if applicable: - - testing/bin/coverage - -Runs a single integration test along with the Cobertura report for that test: - - testing/bin/coverage --test TestClass[#optionalTestMethod] diff --git a/testing/bin/coverage b/testing/bin/coverage deleted file mode 100755 index c920e9fa6d1..00000000000 --- a/testing/bin/coverage +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python - -import argparse -import ConfigParser -import datetime -import os -import platform -import shlex -import subprocess -import sys - -USER_CONFIG = '~/.java_driver_tests.conf' - -def read_config(section, option, data_type='string', default=None): - '''Read configs as stored in the above defined USER_CONFIG.''' - - config = ConfigParser.ConfigParser() - config.read([os.path.expanduser(USER_CONFIG)]) - - if config.has_option(section, option): - if data_type == 'string': - to_return = config.get(section, option) - elif data_type == 'boolean': - to_return = config.getboolean(section, option) - return default if default else to_return - - return default if default else False - -def read_commandline(command): - '''Simple shell read access.''' - - return subprocess.check_output(command, shell=True) - -def execute(command): - '''Simple shell execute access.''' - - print 'Running command:\n\t%s\n' % command - subprocess.call(shlex.split(command)) - -def parse(): - '''Creates the argument parser for this tool.''' - - parser = argparse.ArgumentParser(description='command line tool for quick testing commands.') - parser.add_argument('--test', help='run a specific unit test') - parser.add_argument('--cassandra-version', help='run tests on a specific Cassandra version') - parser.add_argument('--upload', action='store_true', help='upload cobertura site to configured server') - parser.add_argument('--clean', action='store_true', help='runs the maven project from a clean environment') - parser.add_argument('--automated', action='store_true', help='ensures that runs do not get hung up by user input requests') - parser.add_argument('--testdocs', action='store_true', help='generates the test\'s Javadoc') - parser.add_argument('--samplecode', action='store_true', help='prints generated sample CQL') - args = parser.parse_args() - return args - -def check_path(): - '''Ensures this tool is run from the java-driver home directory.''' - - if not 'driver-core' in read_commandline('ls'): - sys.exit('Execute this command from your java-driver root directory.') - - -def maybe_setup_loopbacks(): - '''Currently only setting loopbacks for Mac OSX, but feel free to contribute for other setups.''' - - if platform.system() == 'Darwin' and not read_config('general', 'no_loopbacks', data_type='boolean'): - print 'Setting up CCM loopbacks...' - try: - loopbacks_enabled = read_commandline('ifconfig | grep "inet 127.0.1.4 netmask 0xff000000"') - if not loopbacks_enabled: - raise subprocess.CalledProcessError - except subprocess.CalledProcessError: - # For basic ccm - execute('sudo ifconfig lo0 alias 127.0.0.2 up') - execute('sudo ifconfig lo0 alias 127.0.0.3 up') - - # Additional loopbacks for the java-driver - execute('sudo ifconfig lo0 alias 127.0.1.1 up') - execute('sudo ifconfig lo0 alias 127.0.1.2 up') - execute('sudo ifconfig lo0 alias 127.0.1.3 up') - execute('sudo ifconfig lo0 alias 127.0.1.4 up') - execute('sudo ifconfig lo0 alias 127.0.1.5 up') - execute('sudo ifconfig lo0 alias 127.0.1.6 up') - -def maybe_upload_cobertura_site(): - ''' - The following must be set in the above defined USER_CONFIG: - [general] - cobertura_server = xxx - cobertura_directory = xxx - - If defined, the cobertura site will be rsync'd to a remote location. - ''' - - cobertura_server = read_config('general', 'cobertura_server') - cobertura_directory = read_config('general', 'cobertura_directory') - - if cobertura_server and cobertura_directory: - print 'rsync-ing cobertura site to %s:%s...' % ( - cobertura_server, - cobertura_directory) - execute('rsync -avz testing/cobertura-history %s:%s' % ( - cobertura_server, - cobertura_directory)) - -def save_cobertura_site(): - '''Save cobertura site folders by date''' - - execute('mkdir -p testing/cobertura-history') - today = datetime.date.today() - execute('cp -r driver-core/target/site/cobertura testing/cobertura-history/cobertura-%s' % today) - execute('rm -rf testing/cobertura-history/current') - execute('cp -r driver-core/target/site/cobertura testing/cobertura-history/current') - -def main(): - check_path() - args = parse() - - # Check if an upload is all that is required - if args.upload: - maybe_upload_cobertura_site() - sys.exit() - - if args.testdocs: - execute('mvn javadoc:test-javadoc') - print '\nTo view test Javadocs:' - print '\topen driver-core/target/site/testapidocs/index.html' - sys.exit() - - # Setup required ccm loopbacks - maybe_setup_loopbacks() - - # Start building the mvn command - cobertura_build_command = 'mvn' - - # Add the clean target, if asked or building the entire project - if args.clean or not args.test: - cobertura_build_command += ' clean' - - cobertura_build_command += ' versions:display-dependency-updates' # Ensures dependencies are up to date - cobertura_build_command += ' cobertura:cobertura' # Runs code coverage plugin - cobertura_build_command += ' --projects driver-core' # Runs only the main module - - if args.samplecode: - cobertura_build_command += ' -Pdoc' # Runs the docs "tests" and prints sample code - else: - cobertura_build_command += ' -Plong' # Runs the integration tests, not just tests - - # Use a specific Cassandra version, if asked - if args.cassandra_version: - cobertura_build_command += ' -Dcassandra.version=%s' % args.cassandra_version - - if args.test: - # Run against a single mvn test - execute('%s' - ' -Dmaven.test.failure.ignore=true' - ' -DfailIfNoTests=false' - ' -Dtest=%s' % (cobertura_build_command, args.test)) - else: - # Run against the entire integration suite - execute('%s' % cobertura_build_command) - - try: - if args.automated or raw_input('Save Cobertura Report? [y/N] ').lower() == 'y': - - # Save out cobertura site files - save_cobertura_site() - - # Perhaps move cobertura site files to a central location - maybe_upload_cobertura_site() - except KeyboardInterrupt: - print - - # Optionally, open coverage report when done building - if read_config('general', 'open_cobertura_site_after_build', 'boolean'): - execute('open driver-core/target/site/cobertura/index.html') - else: - print '\nTo view Cobertura report:' - print '\topen driver-core/target/site/cobertura/index.html' - - -if __name__ == '__main__': - main()