diff --git a/.circleci/collect-jacoco-reports.sh b/.circleci/collect-jacoco-reports.sh new file mode 100755 index 00000000..dca6bbad --- /dev/null +++ b/.circleci/collect-jacoco-reports.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +############################################################################ +## Script to collect all JaCoCo reports from different modules into a +## single directory for easier artifact storage in CI. +############################################################################ + +mkdir -p /home/circleci/jacoco-reports/ + +############################################################## +## Find all module directories that have target/site/jacoco ## +############################################################## +for module_dir in */; do + if [ -d "${module_dir}target/site/jacoco" ]; then + module_name=$(basename "${module_dir%/}") + target_dir="/home/circleci/jacoco-reports/${module_name}" + + echo "Collecting JaCoCo reports for module: ${module_name}" + + cp -r "${module_dir}target/site/jacoco" "${target_dir}" + + echo "Copied JaCoCo reports for ${module_name} to ${target_dir}" + fi +done + +echo "All JaCoCo reports collected to /home/circleci/jacoco-reports/" \ No newline at end of file diff --git a/.circleci/concatenate-test-output.sh b/.circleci/concatenate-test-output.sh new file mode 100755 index 00000000..e86efd8e --- /dev/null +++ b/.circleci/concatenate-test-output.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +############################################################################ +## Script to concatenate all .txt files in the surefire-reports directory +## into a single artifact that can be stored in CI. +############################################################################ + +mkdir -p /home/circleci/test-output-artifacts/ + +################################################################### +## Find all module directories that have target/surefire-reports ## +################################################################### +for module_dir in */; do + if [ -d "${module_dir}target/surefire-reports" ]; then + module_name=$(basename "${module_dir%/}") + output_file="/home/circleci/test-output-artifacts/${module_name}-test-output.txt" + + echo "Processing module: ${module_name}" + echo "Output file: ${output_file}" + + ################################################################## + ## Concatenate all .txt files in the surefire-reports directory ## + ################################################################## + if [ -n "$(find "${module_dir}target/surefire-reports" -name "*.txt" -type f)" ]; then + echo "=== Test Output for ${module_name} ===" > "${output_file}" + echo "Generated at: $(date)" >> "${output_file}" + echo "==========================================" >> "${output_file}" + echo "" >> "${output_file}" + + ############################################## + ## Sort files to ensure consistent ordering ## + ############################################## + find "${module_dir}target/surefire-reports" -name "*.txt" -type f | sort | while read -r txt_file; do + echo "--- File: $(basename "${txt_file}") ---" >> "${output_file}" + cat "${txt_file}" >> "${output_file}" + echo "" >> "${output_file}" + echo "--- End of $(basename "${txt_file}") ---" >> "${output_file}" + echo "" >> "${output_file}" + echo "" >> "${output_file}" + echo "" >> "${output_file}" + done + + echo "Concatenated test output for ${module_name} to ${output_file}" + else + echo "No .txt files found in ${module_dir}target/surefire-reports" + fi + fi +done \ No newline at end of file diff --git a/.circleci/config.yml b/.circleci/config.yml index 7433d19e..748c1a64 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,35 +5,7 @@ orbs: browser-tools: circleci/browser-tools@1.4.7 commands: - store_jacoco_site: - parameters: - module: - type: string - steps: - - store_artifacts: - path: << parameters.module >>/target/site/jacoco/index.html - when: always - - store_artifacts: - path: << parameters.module >>/target/site/jacoco/jacoco-resources - when: always - - install_java17: - steps: - - run: - name: Install Java 17 - command: | - sudo apt-get update - sudo apt install -y openjdk-17-jdk - sudo rm /etc/alternatives/java - sudo ln -s /usr/lib/jvm/java-17-openjdk-amd64/bin/java /etc/alternatives/java - - run: - ## used by jacoco uncovered class reporting in pom.xml - name: Install html2text - command: | - sudo apt-get update - sudo apt-get install -y html2text - - mvn_verify: + mvn_build: steps: - checkout - restore_cache: @@ -45,30 +17,41 @@ commands: name: Write .env command: | echo "RDBMS_PASSWORD=$RDBMS_PASSWORD" >> qqq-sample-project/.env + - run: + name: Run Maven Compile + command: | + mvn -s .circleci/mvn-settings.xml -T4 --no-transfer-progress compile + - save_cache: + paths: + - ~/.m2 + key: v1-dependencies-{{ checksum "pom.xml" }} + + mvn_verify: + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "pom.xml" }} - run: name: Run Maven Verify command: | - mvn -s .circleci/mvn-settings.xml -T4 verify - - store_jacoco_site: - module: qqq-backend-core - - store_jacoco_site: - module: qqq-backend-module-filesystem - - store_jacoco_site: - module: qqq-backend-module-rdbms - - store_jacoco_site: - module: qqq-backend-module-api - - store_jacoco_site: - module: qqq-middleware-api - - store_jacoco_site: - module: qqq-middleware-javalin - - store_jacoco_site: - module: qqq-middleware-picocli - - store_jacoco_site: - module: qqq-middleware-slack - - store_jacoco_site: - module: qqq-language-support-javascript - - store_jacoco_site: - module: qqq-sample-project + mvn -s .circleci/mvn-settings.xml -T4 --no-transfer-progress verify + - run: + name: Collect JaCoCo reports + command: .circleci/collect-jacoco-reports.sh + when: always + - store_artifacts: + path: /home/circleci/jacoco-reports + destination: jacoco-reports + when: always + - run: + name: Concatenate test output files + command: .circleci/concatenate-test-output.sh + when: always + - store_artifacts: + path: /home/circleci/test-output-artifacts + destination: test-output + when: always - run: name: Save test results command: | @@ -77,10 +60,6 @@ commands: when: always - store_test_results: path: ~/test-results - - save_cache: - paths: - - ~/.m2 - key: v1-dependencies-{{ checksum "pom.xml" }} check_middleware_api_versions: steps: @@ -91,8 +70,8 @@ commands: - run: name: Build and Run ValidateApiVersions command: | - mvn -s .circleci/mvn-settings.xml -T4 install -DskipTests - mvn -s .circleci/mvn-settings.xml -pl qqq-middleware-javalin package appassembler:assemble -DskipTests + mvn -s .circleci/mvn-settings.xml -T4 --no-transfer-progress install -DskipTests + mvn -s .circleci/mvn-settings.xml -T4 --no-transfer-progress -pl qqq-middleware-javalin package appassembler:assemble -DskipTests qqq-middleware-javalin/target/appassembler/bin/ValidateApiVersions -r $(pwd) mvn_jar_deploy: @@ -108,7 +87,7 @@ commands: - run: name: Run Maven Jar Deploy command: | - mvn -s .circleci/mvn-settings.xml -T4 flatten:flatten jar:jar deploy:deploy + mvn -s .circleci/mvn-settings.xml -T4 --no-transfer-progress flatten:flatten jar:jar deploy:deploy - save_cache: paths: - ~/.m2 @@ -135,19 +114,25 @@ commands: when: always jobs: - mvn_test: + build: + executor: localstack/default + steps: + - mvn_build + + test: executor: localstack/default steps: - ## - localstack/startup - - install_java17 - mvn_verify + + api_version_check: + executor: localstack/default + steps: - check_middleware_api_versions mvn_deploy: executor: localstack/default steps: - ## - localstack/startup - - install_java17 + - mvn_build - mvn_verify - check_middleware_api_versions - mvn_jar_deploy @@ -161,13 +146,31 @@ jobs: workflows: test_only: jobs: - - mvn_test: + - build: context: [ qqq-maven-registry-credentials, build-qqq-sample-app ] filters: branches: ignore: /(dev|integration.*)/ tags: ignore: /(version|snapshot)-.*/ + - test: + context: [ qqq-maven-registry-credentials, build-qqq-sample-app ] + requires: + - build + filters: + branches: + ignore: /(dev|integration.*)/ + tags: + ignore: /(version|snapshot)-.*/ + - api_version_check: + context: [ qqq-maven-registry-credentials, build-qqq-sample-app ] + requires: + - build + filters: + branches: + ignore: /(dev|integration.*)/ + tags: + ignore: /(version|snapshot)-.*/ deploy: jobs: diff --git a/README.md b/README.md index 797f0e0b..8a810ee7 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,20 @@ There are a few useful IntelliJ settings files, under `qqq-dev-tools/intellij`: One will likely also want the [Kingsrook Commentator Plugin](https://plugins.jetbrains.com/plugin/19325-kingsrook-commentator). +## Test Logging +By default, when ran from the command line, mvn surefire will make each test's +output (e.g., System.out, err, printStackTrace, and all logger calls) go into a +file under target/surefire-reports/${className}.txt. + +The system property `-DtestOutputToFile=false` can be given on the command line +to get all of this output on the console. + +In the IDE (e.g,. IntelliJ), output goes to the Console. + +In CircleCI, output goes to files, and those files are concatenated together and +stored as artifacts. + + ## License QQQ - Low-code Application Framework for Engineers. \ Copyright (C) 2020-2024. Kingsrook, LLC \ diff --git a/pom.xml b/pom.xml index a8d48433..0f55dd18 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ 0.80 0.95 none + true @@ -141,6 +142,8 @@ @{jaCoCoArgLine} + + ${testOutputToFile} @@ -244,30 +247,29 @@ if [ ! -e target/site/jacoco/index.html ]; then fi echo -echo "Jacoco coverage summary report:" +echo "Jacoco coverage summary report for module: ${project.artifactId}" echo " See also target/site/jacoco/index.html" echo " and https://www.jacoco.org/jacoco/trunk/doc/counters.html" echo "------------------------------------------------------------" -if which xpath > /dev/null 2>&1; then - echo "Element\nInstructions Missed\nInstruction Coverage\nBranches Missed\nBranch Coverage\nComplexity Missed\nComplexity Hit\nLines Missed\nLines Hit\nMethods Missed\nMethods Hit\nClasses Missed\nClasses Hit\n" > /tmp/$$.headers - xpath -n -q -e '/html/body/table/tfoot/tr[1]/td/text()' target/site/jacoco/index.html > /tmp/$$.values +# Parse Jacoco HTML coverage summary +if [ -f target/site/jacoco/index.html ]; then + echo -e "Instructions Missed\nInstruction Coverage\nBranches Missed\nBranch Coverage\nComplexity Missed\nComplexity Hit\nLines Missed\nLines Hit\nMethods Missed\nMethods Hit\nClasses Missed\nClasses Hit\n" > /tmp/$$.headers + sed 's/<\/\w\+>/&\n/g' target/site/jacoco/index.html | grep -A 12 '' | grep '\([^<]*\)<\/td>/\1/' | grep -v Total > /tmp/$$.values paste /tmp/$$.headers /tmp/$$.values | tail +2 | awk -v FS='\t' '{printf("%-20s %s\n",$1,$2)}' rm /tmp/$$.headers /tmp/$$.values else - echo "xpath is not installed. Jacoco coverage summary will not be produced here..."; + echo "Jacoco coverage summary was not found."; fi +echo "-----------------------------" +echo -if which html2text > /dev/null 2>&1; then - echo "Untested classes, per Jacoco:" - echo "-----------------------------" - for i in target/site/jacoco/*/index.html; do - html2text -width 500 -nobs $i | sed '1,/^Total/d;' | grep -v Created | sed 's/ \+/ /g' | sed 's/ [[:digit:]]$//' | grep -v 0$ | cut -d' ' -f1; - done; - echo -else - echo "html2text is not installed. Untested classes from Jacoco will not be printed here..."; -fi +echo "Untested classes, per Jacoco for module: ${project.artifactId}" +echo "-----------------------------" +# Parse Jacoco XML reports directly to find classes with 0% coverage +sed 's//&\n/g;s/<\/class>/&\n/g' target/site/jacoco/jacoco.xml | grep -v 'counter type="CLASS" missed="0"' | sed 's/>.*//;s/.*\///;s/".*//' +echo "-----------------------------" +echo ]]>