Specify coverage file path #26
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: SonarCloud Analysis | |
| on: | |
| push: | |
| branches: | |
| - '*' | |
| pull_request: | |
| branches: | |
| - '*' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| sonarcloud: | |
| name: SonarCloud Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Enable KVM group perms | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Shallow clones should be disabled for better relevancy of analysis | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: 17 | |
| - name: Gradle cache | |
| uses: gradle/actions/setup-gradle@v3 | |
| - name: Configure Gradle memory settings | |
| run: | | |
| echo "org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError" >> gradle.properties | |
| echo "android.enableJetifier=false" >> gradle.properties | |
| echo "android.enableR8.fullMode=false" >> gradle.properties | |
| cat gradle.properties | |
| - name: Build project and run tests with coverage | |
| run: | | |
| # Build the project | |
| ./gradlew assembleDebug --stacktrace | |
| # Run tests with coverage - allow test failures | |
| ./gradlew testDebugUnitTest jacocoTestReport --stacktrace | |
| TEST_RESULT=$? | |
| if [ $TEST_RESULT -ne 0 ]; then | |
| echo "Some tests failed, but continuing to check for coverage data..." | |
| # Even if tests fail, JaCoCo should generate a report with partial coverage | |
| # from the tests that did pass | |
| fi | |
| # Check if the report was generated with content | |
| if [ -f build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml ]; then | |
| # Use stat command compatible with both Linux and macOS | |
| if [[ "$(uname)" == "Darwin" ]]; then | |
| # macOS syntax | |
| REPORT_SIZE=$(stat -f%z build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml) | |
| else | |
| # Linux syntax | |
| REPORT_SIZE=$(stat -c%s build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml) | |
| fi | |
| if [ "$REPORT_SIZE" -lt 1000 ]; then | |
| echo "JaCoCo report is too small ($REPORT_SIZE bytes), likely empty. Trying to generate from test execution data..." | |
| # Try to generate the report directly from the exec file | |
| if [ -f build/jacoco/testDebugUnitTest.exec ]; then | |
| java -jar ~/.gradle/caches/modules-2/files-2.1/org.jacoco/org.jacoco.cli/0.8.8/*/org.jacoco.cli-0.8.8.jar report build/jacoco/testDebugUnitTest.exec \ | |
| --classfiles build/intermediates/runtime_library_classes_dir/debug \ | |
| --sourcefiles src/main/java \ | |
| --xml build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml | |
| # Check if the report was successfully generated | |
| if [[ "$(uname)" == "Darwin" ]]; then | |
| # macOS syntax | |
| NEW_REPORT_SIZE=$(stat -f%z build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml) | |
| else | |
| # Linux syntax | |
| NEW_REPORT_SIZE=$(stat -c%s build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml) | |
| fi | |
| if [ "$NEW_REPORT_SIZE" -lt 1000 ]; then | |
| echo "ERROR: Failed to generate a valid JaCoCo report with coverage data" | |
| exit 1 | |
| else | |
| echo "JaCoCo report successfully generated with size $NEW_REPORT_SIZE bytes" | |
| fi | |
| else | |
| echo "ERROR: No JaCoCo execution data file found. Tests may not have run correctly." | |
| exit 1 | |
| fi | |
| else | |
| echo "JaCoCo report generated successfully with size $REPORT_SIZE bytes" | |
| fi | |
| else | |
| echo "ERROR: JaCoCo report file not found. Coverage data is missing." | |
| exit 1 | |
| fi | |
| - name: Prepare class files for SonarQube analysis | |
| run: | | |
| echo "Searching for compiled class files..." | |
| # Create the target directory | |
| mkdir -p build/intermediates/runtime_library_classes_dir/debug | |
| # Find all directories containing class files | |
| CLASS_DIRS=$(find build -name "*.class" -type f -exec dirname {} \; | sort -u) | |
| if [ -z "$CLASS_DIRS" ]; then | |
| echo "WARNING: No class files found in the build directory!" | |
| else | |
| echo "Found class files in the following directories:" | |
| echo "$CLASS_DIRS" | |
| # Copy classes from the first directory with classes | |
| FIRST_CLASS_DIR=$(echo "$CLASS_DIRS" | head -1) | |
| echo "Copying classes from $FIRST_CLASS_DIR to build/intermediates/runtime_library_classes_dir/debug" | |
| cp -r $FIRST_CLASS_DIR/* build/intermediates/runtime_library_classes_dir/debug/ || echo "Failed to copy from $FIRST_CLASS_DIR" | |
| # Verify the target directory now has class files | |
| CLASS_COUNT=$(find build/intermediates/runtime_library_classes_dir/debug -name "*.class" | wc -l) | |
| echo "Target directory now contains $CLASS_COUNT class files" | |
| fi | |
| # Update sonar-project.properties with all found class directories as a fallback | |
| echo "\n# Additional binary paths found during build" >> sonar-project.properties | |
| echo "sonar.java.binaries=build/intermediates/runtime_library_classes_dir/debug,$CLASS_DIRS" >> sonar-project.properties | |
| echo "Checking for JaCoCo report files..." | |
| find build -name "*.xml" | grep jacoco || echo "No XML files found" | |
| find build -name "*.exec" | grep jacoco || echo "No exec files found" | |
| echo "Contents of JaCoCo report directory:" | |
| ls -la build/reports/jacoco/jacocoTestReport/ || echo "Directory not found" | |
| echo "\nChecking test execution results:" | |
| find build -name "TEST-*.xml" | xargs cat | grep -E 'tests|failures|errors|skipped' || echo "No test result files found" | |
| echo "\nChecking JaCoCo report content:" | |
| if [ -f build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml ]; then | |
| echo "Report file size: $(wc -c < build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml) bytes" | |
| echo "First 500 chars of report:" | |
| head -c 500 build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml | |
| echo "\n\nCounting coverage elements:" | |
| grep -c "<package" build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml || echo "No packages found" | |
| grep -c "<class" build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml || echo "No classes found" | |
| grep -c "<method" build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml || echo "No methods found" | |
| grep -c "<line" build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml || echo "No lines found" | |
| grep -c 'ci="1"' build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml || echo "No covered lines found" | |
| else | |
| echo "JaCoCo report file not found" | |
| fi | |
| echo "\nChecking binary directories specified in sonar-project.properties:" | |
| echo "build/intermediates/runtime_library_classes_dir/debug:" | |
| ls -la build/intermediates/runtime_library_classes_dir/debug || echo "Directory not found" | |
| echo "\nChecking all available class directories:" | |
| find build -path "*/build/*" -name "*.class" | head -n 5 || echo "No class files found" | |
| # Android instrumented test section for comprehensive coverage | |
| - name: AVD cache | |
| uses: actions/cache@v4 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-28 | |
| - name: Create AVD and generate snapshot for caching | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 28 | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| disable-animations: true | |
| script: echo "Generated AVD snapshot for caching." | |
| - name: Assemble debug AndroidTest | |
| run: ./gradlew assembleDebugAndroidTest | |
| - name: Run instrumented tests with coverage | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 28 | |
| profile: Galaxy Nexus | |
| script: | | |
| # Ensure the app and test app are built | |
| ./gradlew assembleDebug assembleDebugAndroidTest --stacktrace | |
| echo "Starting instrumented tests with coverage..." | |
| # Run connectedDebugAndroidTest, explicitly telling it where to save the coverage file on the emulator. | |
| # This aligns with how AGP handles coverage when this property is set. | |
| ./gradlew connectedDebugAndroidTest --stacktrace | |
| TEST_RUN_RESULT=$? | |
| echo "Instrumented test run finished with Gradle task result: $TEST_RUN_RESULT" | |
| # Create the target directory for the pulled coverage file | |
| mkdir -p build/outputs/code_coverage/debugAndroidTest/connected/ | |
| echo "Attempting to pull coverage file from /sdcard/coverage.ec on emulator $AVD_NAME" | |
| adb pull /sdcard/coverage.ec build/outputs/code_coverage/debugAndroidTest/connected/$AVD_NAME-coverage.ec | |
| PULL_RESULT=$? | |
| echo "ADB pull command finished with result: $PULL_RESULT" | |
| # Define the expected path for the pulled coverage file | |
| PULLED_COVERAGE_FILE="build/outputs/code_coverage/debugAndroidTest/connected/$AVD_NAME-coverage.ec" | |
| # Verification | |
| SUCCESS=true | |
| if [ $TEST_RUN_RESULT -ne 0 ]; then | |
| echo "ERROR: Instrumented tests Gradle task failed (Result: $TEST_RUN_RESULT)." | |
| SUCCESS=false | |
| fi | |
| if [ $PULL_RESULT -ne 0 ]; then | |
| echo "ERROR: adb pull command failed to retrieve coverage file (Result: $PULL_RESULT)." | |
| SUCCESS=false | |
| fi | |
| if [ ! -f "$PULLED_COVERAGE_FILE" ]; then | |
| echo "ERROR: Coverage file was NOT found at $PULLED_COVERAGE_FILE after adb pull." | |
| SUCCESS=false | |
| else | |
| echo "Coverage file found at $PULLED_COVERAGE_FILE." | |
| ls -l "$PULLED_COVERAGE_FILE" | |
| # Optional: Check file size (e.g., if less than a few hundred bytes, it might be empty/invalid) | |
| COVERAGE_FILE_SIZE=$(stat -c%s "$PULLED_COVERAGE_FILE") | |
| echo "Coverage file size: $COVERAGE_FILE_SIZE bytes." | |
| if [ "$COVERAGE_FILE_SIZE" -lt 100 ]; then # Adjust threshold as needed | |
| echo "WARNING: Pulled coverage file is very small, possibly empty or invalid." | |
| # Depending on strictness, you might set SUCCESS=false here too | |
| fi | |
| fi | |
| if [ "$SUCCESS" = false ]; then | |
| echo "---------------------------------------------------------------------" | |
| echo "DIAGNOSTICS: Instrumented test coverage collection failed." | |
| echo "Listing /sdcard/ on emulator to check if coverage.ec was created there:" | |
| adb shell ls -l /sdcard/ | |
| echo "---------------------------------------------------------------------" | |
| echo "Exiting with error." | |
| exit 1 | |
| else | |
| echo "Instrumented test coverage collection appears successful." | |
| fi | |
| - name: Generate combined coverage report | |
| run: | | |
| # Generate combined report - allow failures | |
| ./gradlew jacocoCodeCoverageReport | |
| COMBINED_RESULT=$? | |
| if [ $COMBINED_RESULT -ne 0 ]; then | |
| echo "Failed to generate combined report, but continuing..." | |
| fi | |
| # Check if the combined report was generated with content | |
| if [ -f build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml ]; then | |
| # Use stat command compatible with both Linux and macOS | |
| if [[ "$(uname)" == "Darwin" ]]; then | |
| # macOS syntax | |
| REPORT_SIZE=$(stat -f%z build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml) | |
| else | |
| # Linux syntax | |
| REPORT_SIZE=$(stat -c%s build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml) | |
| fi | |
| echo "Combined JaCoCo report size: $REPORT_SIZE bytes" | |
| if [ "$REPORT_SIZE" -lt 1000 ]; then | |
| echo "WARNING: Combined JaCoCo report is too small, likely empty" | |
| # Try to manually combine the reports | |
| mkdir -p build/reports/jacoco/jacocoCombinedReport/ | |
| cp build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml | |
| else | |
| echo "Combined JaCoCo report generated successfully" | |
| fi | |
| else | |
| echo "WARNING: Combined JaCoCo report file not found, using unit test report as fallback" | |
| mkdir -p build/reports/jacoco/jacocoCombinedReport/ | |
| cp build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml | |
| fi | |
| - name: Check combined coverage report | |
| run: | | |
| echo "Checking for all coverage data files:" | |
| find build -name "*.exec" -o -name "*.ec" | sort | |
| echo "\nChecking combined JaCoCo report content:" | |
| if [ -f build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml ]; then | |
| echo "Report file size: $(wc -c < build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml) bytes" | |
| echo "First 500 chars of report:" | |
| head -c 500 build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml | |
| echo "\n\nCounting coverage elements:" | |
| grep -c "<package" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No packages found" | |
| grep -c "<class" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No classes found" | |
| grep -c "<method" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No methods found" | |
| grep -c "<line" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No lines found" | |
| grep -c 'ci="1"' build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No covered lines found" | |
| else | |
| echo "Combined JaCoCo report file not found" | |
| fi | |
| - name: SonarCloud Scan | |
| uses: SonarSource/sonarqube-scan-action@v5 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information | |
| SONAR_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} | |
| SONAR_HOST_URL: ${{ secrets.SONARQUBE_HOST }} |