diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml new file mode 100644 index 00000000..101a358e --- /dev/null +++ b/.github/workflows/code-coverage.yml @@ -0,0 +1,70 @@ +name: Code coverage + +on: + pull_request: + branches: + - dev + +jobs: + extender: + runs-on: ubuntu-22.04 + steps: + - name: Checkout repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Install Java + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 + with: + java-version: '21.0.5+11.0.LTS' + distribution: 'temurin' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 + - name: Run extender tests + run: ./gradlew server:clean server:test server:jacocoTestReport -PexcludeTags="integration" -i + - name: Setup .NET Core # Required to execute ReportGenerator + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 + with: + dotnet-version: 8.x + dotnet-quality: 'ga' + - name: ReportGenerator + uses: danielpalme/ReportGenerator-GitHub-Action@4c0f60daf67483745c34efdeadd4c4e78a19991e3 + with: + reports: '${{ github.workspace }}/server/build/reports/jacoco/test/jacocoTestReport.xml' # REQUIRED # The coverage reports that should be parsed (separated by semicolon). Globbing is supported. + targetdir: 'coveragereport' # REQUIRED # The directory where the generated report should be saved. + reporttypes: 'HtmlInline;MarkdownSummaryGithub' # The output formats and scope (separated by semicolon) Values: Badges, Clover, Cobertura, OpenCover, CsvSummary, Html, Html_Dark, Html_Light, Html_BlueRed, HtmlChart, HtmlInline, HtmlInline_AzurePipelines, HtmlInline_AzurePipelines_Dark, HtmlInline_AzurePipelines_Light, HtmlSummary, Html_BlueRed_Summary, JsonSummary, CodeClimate, Latex, LatexSummary, lcov, MarkdownSummary, MarkdownAssembliesSummary, MarkdownSummaryGithub, MarkdownDeltaSummary, MHtml, SvgChart, SonarQube, TeamCitySummary, TextSummary, TextDeltaSummary, Xml, XmlSummary + sourcedirs: 'server/src/main/java' # Optional directories which contain the corresponding source code (separated by semicolon). The source directories are used if coverage report contains classes without path information. + # historydir: '' # Optional directory for storing persistent coverage information. Can be used in future reports to show coverage evolution. + # workingdir: ${{ github.workspace }} # Optional working directory. If available, the targetdir, sourcedirs and historydir are interpreted relative to the working directory (only if not specified as absolute paths). + # plugins: '' # Optional plugin files for custom reports or custom history storage (separated by semicolon). + # assemblyfilters: '+*' # Optional list of assemblies that should be included or excluded in the report. Exclusion filters take precedence over inclusion filters. Wildcards are allowed. + # classfilters: '+*' # Optional list of classes that should be included or excluded in the report. Exclusion filters take precedence over inclusion filters. Wildcards are allowed. + # filefilters: '+*' # Optional list of files that should be included or excluded in the report. Exclusion filters take precedence over inclusion filters. Wildcards are allowed. + # riskhotspotassemblyfilters: '+*' # Optional list of assemblies that should be included or excluded in the risk hotspots. Exclusion filters take precedence over inclusion filters. Wildcards are allowed. + # riskhotspotclassfilters: '+*' # Optional list of classes that should be included or excluded in the risk hotspots. Exclusion filters take precedence over inclusion filters. Wildcards are allowed. + verbosity: 'Info' # The verbosity level of the log messages. Values: Verbose, Info, Warning, Error, Off + title: 'Extender code coverage report' # Optional title. + tag: '${{ github.run_number }}_${{ github.run_id }}' # Optional tag or build version. + # license: '' # Optional license for PRO version. Get your license here: https://reportgenerator.io/pro + # customSettings: '' # Optional custom settings (separated by semicolon). See: https://github.com/danielpalme/ReportGenerator/wiki/Settings. + toolpath: 'reportgeneratortool' # Default directory for installing the dotnet tool. + - name: Upload test results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: test-result + path: ${{ github.workspace }}/server/build/reports/tests/ + retention-days: 10 + - name: Upload coverage report artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 + with: + name: CoverageReport # Artifact name + path: coveragereport # Directory containing files to upload + retention-days: 10 + - name: Add comment to PR # Only applicable if 'MarkdownSummaryGithub' or one of the other Markdown report types is generated + if: github.event_name == 'pull_request' + run: gh pr comment $PR_NUMBER --edit-last --create-if-none --body-file coveragereport/SummaryGithub.md # Adjust path and filename if necessary + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.number }} + - name: Publish coverage in build summary # Only applicable if 'MarkdownSummaryGithub' or one of the other Markdown report types is generated + run: cat coveragereport/SummaryGithub.md >> $GITHUB_STEP_SUMMARY # Adjust path and filename if necessary + shell: bash diff --git a/server/build.gradle b/server/build.gradle index 81fcf6b4..ec09ad5b 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -15,6 +15,11 @@ plugins { id 'java' id 'idea' id 'org.springframework.boot' version '3.3.5' + id 'jacoco' +} + +jacoco { + toolVersion = "0.8.13" } apply plugin: 'io.spring.dependency-management' @@ -206,5 +211,18 @@ task buildStandalone() { } test { - useJUnitPlatform() + useJUnitPlatform { + if (project.hasProperty("excludeTags")) { + excludeTags project.getProperty("excludeTags") + } + } + finalizedBy jacocoTestReport } + +jacocoTestReport { + dependsOn test // tests must run before report + reports { + xml.required.set(true) + html.required.set(true) + } +} \ No newline at end of file diff --git a/server/src/test/java/com/defold/extender/AuthenticationTest.java b/server/src/test/java/com/defold/extender/AuthenticationTest.java index 98d727fe..572213a0 100644 --- a/server/src/test/java/com/defold/extender/AuthenticationTest.java +++ b/server/src/test/java/com/defold/extender/AuthenticationTest.java @@ -9,6 +9,7 @@ import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -26,6 +27,7 @@ import java.util.List; import java.util.stream.Collectors; +@Tag("integration") public class AuthenticationTest implements AfterEachCallback { private static final int EXTENDER_PORT = 9001; diff --git a/server/src/test/java/com/defold/extender/IntegrationTest.java b/server/src/test/java/com/defold/extender/IntegrationTest.java index c4b525a3..e1cf49d7 100644 --- a/server/src/test/java/com/defold/extender/IntegrationTest.java +++ b/server/src/test/java/com/defold/extender/IntegrationTest.java @@ -9,6 +9,7 @@ import org.jf.dexlib2.iface.DexFile; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; @@ -34,6 +35,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +@Tag("integration") public class IntegrationTest { private static final int EXTENDER_PORT = 9000;