diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ebad9b7bc7..52c63d595a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -308,15 +308,36 @@ jobs: emulator.log failure_screenshots/ + codecov-units: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - run: ./gradlew allCodeCoverageReport $CI_GRADLE_ARG_PROPERTIES + - name: Upload Codecov data + uses: actions/upload-artifact@v2 + if: always() + with: + name: codecov-xml + path: | + build/reports/jacoco/allCodeCoverageReport/allCodeCoverageReport.xml + sonarqube: runs-on: macos-latest if: always() needs: - - integration-tests - - ui-tests -# - unit-tests TODO: code coverage from here too - - build-android-test-matrix-sdk - - build-android-test-app + - codecov-units steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v2 @@ -331,6 +352,11 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- + - uses: actions/download-artifact@v3 + with: + name: codecov-xml # will restore to allCodeCoverageReport.xml by default; we restore to the same location in following tasks + - run: mkdir -p build/reports/jacoco/allCodeCoverageReport/ + - run: mv allCodeCoverageReport.xml build/reports/jacoco/allCodeCoverageReport/ - run: ./gradlew sonarqube $CI_GRADLE_ARG_PROPERTIES env: ORG_GRADLE_PROJECT_SONAR_LOGIN: ${{ secrets.SONAR_TOKEN }} diff --git a/build.gradle b/build.gradle index 9d84dc1edb..31416a0440 100644 --- a/build.gradle +++ b/build.gradle @@ -105,6 +105,16 @@ task clean(type: Delete) { delete rootProject.buildDir } +def launchTask = getGradle() + .getStartParameter() + .getTaskRequests() + .toString() + .toLowerCase() + +if (launchTask.contains("codeCoverageReport".toLowerCase())) { + apply from: 'coverage.gradle' +} + apply plugin: 'org.sonarqube' // To run a sonar analysis: @@ -119,10 +129,12 @@ sonarqube { property "sonar.projectVersion", project(":vector").android.defaultConfig.versionName property "sonar.sourceEncoding", "UTF-8" property "sonar.links.homepage", "https://github.com/vector-im/element-android/" - property "sonar.links.ci", "https://buildkite.com/matrix-dot-org/element-android" + property "sonar.links.ci", "https://github.com/vector-im/element-android/actions" property "sonar.links.scm", "https://github.com/vector-im/element-android/" property "sonar.links.issue", "https://github.com/vector-im/element-android/issues" property "sonar.organization", "new_vector_ltd_organization" + property "sonar.java.coveragePlugin", "jacoco" + property "sonar.coverage.jacoco.xmlReportPaths", "${project.buildDir}/reports/jacoco/allCodeCoverageReport/allCodeCoverageReport.xml" property "sonar.login", project.hasProperty("SONAR_LOGIN") ? SONAR_LOGIN : "invalid" } } diff --git a/coverage.gradle b/coverage.gradle new file mode 100644 index 0000000000..96881dfff2 --- /dev/null +++ b/coverage.gradle @@ -0,0 +1,55 @@ +def excludes = [ ] + +def initializeReport(report, projects, classExcludes) { + projects.each { project -> project.apply plugin: 'jacoco' } + report.executionData { fileTree(rootProject.rootDir.absolutePath).include("**/build/jacoco/*.exec") } + + report.reports { + xml.enabled true + html.enabled true + csv.enabled false + } + + gradle.projectsEvaluated { + def androidSourceDirs = [] + def androidClassDirs = [] + + projects.each { project -> + switch (project) { + case { project.plugins.hasPlugin("com.android.application") }: + androidClassDirs.add("${project.buildDir}/tmp/kotlin-classes/debug") + androidSourceDirs.add("${project.projectDir}/src/main/kotlin") + androidSourceDirs.add("${project.projectDir}/src/main/java") + break + case { project.plugins.hasPlugin("com.android.library") }: + androidClassDirs.add("${project.buildDir}/tmp/kotlin-classes/debug") + androidSourceDirs.add("${project.projectDir}/src/main/kotlin") + androidSourceDirs.add("${project.projectDir}/src/main/java") + break + default: + report.sourceSets project.sourceSets.main + } + } + + report.sourceDirectories.setFrom(report.sourceDirectories + files(androidSourceDirs)) + def classFiles = androidClassDirs.collect { files(it).files }.flatten() + report.classDirectories.setFrom(files((report.classDirectories.files + classFiles).collect { + fileTree(dir: it, excludes: classExcludes) + })) + } +} + +def collectProjects(predicate) { + return subprojects.findAll { it.buildFile.isFile() && predicate(it) } +} + +task allCodeCoverageReport(type: JacocoReport) { + outputs.upToDateWhen { false } + rootProject.apply plugin: 'jacoco' + // to limit projects in a specific report, add + // def excludedProjects = [ ... ] + // def projects = collectProjects { !excludedProjects.contains(it.name) } + def projects = collectProjects { true } + dependsOn { projects*.test } + initializeReport(it, projects, excludes) +} diff --git a/dependencies_groups.gradle b/dependencies_groups.gradle index 723941157d..45883f506d 100644 --- a/dependencies_groups.gradle +++ b/dependencies_groups.gradle @@ -156,6 +156,7 @@ ext.groups = [ 'org.ec4j.core', 'org.glassfish.jaxb', 'org.hamcrest', + 'org.jacoco', 'org.jetbrains', 'org.jetbrains.intellij.deps', 'org.jetbrains.kotlin',