Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development: Execute server tests only for changed modules in GitHub action for non-default branches #10036

Merged
merged 82 commits into from
Jan 19, 2025

Conversation

ole-ve
Copy link
Contributor

@ole-ve ole-ve commented Dec 17, 2024

Checklist

General

Motivation and Context

Most often developers don't change functionality related to all of Artemis, but only to some modules. Thus, running the whole test-suite for each commit is ineffective. Instead, for pull requests etc. it might be fine to run the reduced test suite for the in-memory DB test pipeline on non-default branches. To ensure everything still works, all tests should be run for every default-branch push.

Description

Matching tests and source code

By determining the module-folders with changed files, the tests for the respective module are executed. For example changing src/main/java/de/tum/cit/aet/artemis/text/service/ABCService.java, all tests within the folder src/main/java/de/tum/cit/aet/artemis/text are executed.

This is a first step and in the future, it might be an idea to extend the reduced test set to modules on which the changed module is dependent. This however, is not that trivial because of transitivity.

Per-Module Coverage Report & Verification

When running a reduced set of tests for specific modules, we might be interested in having per-module reports and validate thresholds per-module. Validating on a module basis is not only more insightful but also required as running a reduced set of tests might lead to reduced coverage metrics causing verification to fail.

Steps for Testing

These testing steps are a bit different and don't involve Artemis.

  1. Clone this branch locally.
  2. Create a new branch using git checkout -b feat/test-changed-modules-1
  3. Commit some changes mapping to two or three different modules.
  4. Push the branch and create a (draft) pull request. Otherwise, you could add your branch name to the triggering branches in .github/workflows/test.yml
  5. Wait for the test pipeline to finish.
  6. Only on success: See a summary of the whole test job (including coverage metrics of changed modules).
  7. Only on success: Verify that the mysql/postgres test pipelines are started and run all tests

Screenshots

The job summary generated on a branch where a single file in the fileupload-module has been changed.

coverage-summary

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a new script to identify changed modules in a specified branch.
    • Added a script for processing JaCoCo coverage reports and generating summaries.
    • Registered a new task for generating test reports.
    • Added a new section in documentation files to present coverage results for specific modules.
  • Bug Fixes

    • Updated the server start count check in the log extraction script for improved error handling.
  • Chores

    • Modified the pull request template to reflect changes in the code coverage generation path.
    • Enhanced test configuration and dependency management in the build setup.
    • Implemented a new Jacoco configuration for coverage thresholds across multiple modules.
    • Updated comments in build scripts for clarity on test execution commands.
    • Added detailed instructions for running tests in specific modules.

ole-ve added 30 commits December 7, 2024 15:56
This reverts commit 786d172.
@DominikRemo DominikRemo self-requested a review January 3, 2025 17:34
DominikRemo
DominikRemo previously approved these changes Jan 4, 2025
Copy link
Contributor

@DominikRemo DominikRemo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've looked through the workflow and since we still run all tests for MySql and Postgres / the default branch, I understand that the downsides are limited. I also really like the idea of coverage thresholds per module.

Thanks for the clarification and the improvement. Keep it up so we can see the benefit of module specific reports manifest :)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
gradle/jacoco.gradle (1)

190-191: ⚠️ Potential issue

Prevent path traversal vulnerability

The module path construction should validate the module name to prevent path traversal.

+    if (!moduleName.matches("^[a-zA-Z0-9-_]+$")) {
+        throw new GradleException("Invalid module name: $moduleName")
+    }
     def modulePath = "$BasePath/$moduleName/**/*.class"
     task.sourceDirectories.setFrom(project.files("src/main/java/$modulePath"))
🧹 Nitpick comments (4)
gradle/jacoco.gradle (4)

98-110: Add documentation for the test report configuration

While the configuration is correct, it would benefit from comments explaining:

  • The purpose of the XML and HTML reports
  • How the dynamic module list works with reportedModules
  • The relationship between the aggregated and per-module reports

112-115: Clarify the verification enabling logic

The condition enabled = reportedModules.size() == 0 might not be immediately clear to other developers. Consider adding a comment explaining why verification is only enabled when no specific modules are set.


170-171: Fix typo in task description

There's a typo in the task description: "vioalations" should be "violations"

-    task.description = "Validates JaCoCo coverage for vioalations for $moduleName"
+    task.description = "Validates JaCoCo coverage violations for $moduleName"

133-149: Add parameter validation in helper methods

The helper methods should validate their input parameters to prevent NullPointerException.

Add null checks for the parameters:

 private JacocoReport registerJacocoReportTask(String moduleName, JacocoReport rootTask) {
+    Objects.requireNonNull(moduleName, "moduleName must not be null")
+    Objects.requireNonNull(rootTask, "rootTask must not be null")
     def taskName = "jacocoCoverageReport-$moduleName"
     // ... rest of the method
 }

 private JacocoCoverageVerification registerJacocoTestCoverageVerification(String moduleName, JacocoCoverageVerification rootTask) {
+    Objects.requireNonNull(moduleName, "moduleName must not be null")
+    Objects.requireNonNull(rootTask, "rootTask must not be null")
     def taskName = "jacocoTestCoverageVerification-$moduleName"
     // ... rest of the method
 }

Also applies to: 158-176

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa439c and ea64960.

📒 Files selected for processing (1)
  • gradle/jacoco.gradle (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (1)
gradle/jacoco.gradle (1)

20-23: Address low test coverage in buildagent module

The buildagent module has a significantly low instruction coverage threshold of 31.3%, which is considerably lower than other modules (most are above 75%). This indicates potential testing gaps that should be addressed.

Consider:

  1. Investigating why the coverage is so low
  2. Creating a plan to improve test coverage
  3. Setting a timeline for gradually increasing the threshold

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 12, 2025
build.gradle Outdated Show resolved Hide resolved
Copy link
Member

@krusche krusche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look really nice, I have one change request. Can you please check if you can fulfill it?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
check.sh (1)

6-12: Improve array handling and IFS usage.

The current implementation has potential issues with array handling and IFS modification.

 if [ -n "${CHANGED_MODULES}" ]; then
   # Always execute ArchitectureTests
   CHANGED_MODULES+=("ArchitectureTest")
 
-  IFS=,
-  TEST_MODULE_TAGS=$(echo "-DtestTags=${CHANGED_MODULES[*]}")
+  # Use local IFS to avoid affecting the rest of the script
+  local IFS=,
+  TEST_MODULE_TAGS="-DtestTags=${CHANGED_MODULES[*]}"
 fi
gradle/test.gradle (2)

48-57: Document the purpose of ext properties and improve null handling.

The ext block needs better documentation and null handling.

 // Allow using in jacoco.gradle
 ext {
+    // Property to specify test tags to include (comma-separated)
     includedTestTags = System.getProperty("includeTags")
-    includedTags = !includedTestTags ? new String[]{} : includedTestTags.split(",") as String[]
+    includedTags = includedTestTags?.split(",") as String[] ?: new String[]{}
 
+    // Property to specify modules to include (comma-separated)
     includedModulesTag = System.getProperty("includeModules")
-    includedModules = !includedModulesTag ? new String[]{} : includedModulesTag.split(",") as String[]
+    includedModules = includedModulesTag?.split(",") as String[] ?: new String[]{}
 
+    // Flag to determine if all tests should be run
     runAllTests = includedTags.size() == 0 && includedModules.size() == 0
+    // Base package path for module-specific test filtering
     BasePath = "de/tum/cit/aet/artemis"
 }

93-94: Document the purpose of jacoco.gradle integration.

Add documentation explaining the purpose of the jacoco.gradle integration and its relationship with module-specific coverage.

-// Dynamic generation of jacoco test report generation-/coverage verification-tasks (per-module)
+/**
+ * Integration with jacoco.gradle for test coverage reporting and verification
+ * - Generates per-module coverage reports
+ * - Configures coverage verification thresholds
+ * - Supports both aggregated and module-specific coverage analysis
+ */
 apply from: "gradle/jacoco.gradle"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea64960 and cf9bebb.

⛔ Files ignored due to path filters (1)
  • docker/test-server-multi-node-postgresql-localci.yml is excluded by !**/*.yml
📒 Files selected for processing (6)
  • build.gradle (1 hunks)
  • check.sh (1 hunks)
  • gradle/test.gradle (2 hunks)
  • per-module-coverage-recorder.sh (1 hunks)
  • read.ms (1 hunks)
  • result.md (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • result.md
  • read.ms
🚧 Files skipped from review as they are similar to previous changes (1)
  • build.gradle
🧰 Additional context used
🪛 Shellcheck (0.10.0)
per-module-coverage-recorder.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

check.sh

[error] 1-1: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.

(SC2148)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: client-style
  • GitHub Check: server-style
  • GitHub Check: client-tests
  • GitHub Check: server-tests
  • GitHub Check: Build and Push Docker Image
  • GitHub Check: Build .war artifact
  • GitHub Check: Analyse
  • GitHub Check: Mend Security Check
🔇 Additional comments (1)
gradle/test.gradle (1)

74-80: Verify module path construction and improve error handling.

The module path construction could be more robust.

✅ Verification successful

Module path construction is valid and robust

The verification confirms that all module paths follow the correct pattern de.tum.cit.aet.artemis.<module> and exist in the codebase, including the "shared" module. The current implementation is appropriate and doesn't require additional error handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify that all module paths exist
for module in assessment athena atlas buildagent communication core exam exercise fileupload iris lecture lti modeling plagiarism programming quiz text tutorialgroup shared; do
    echo "Checking path: de/tum/cit/aet/artemis/$module"
    if ! find . -type d -path "*/de/tum/cit/aet/artemis/$module" | grep -q .; then
        echo "Warning: Module path not found: $module"
    fi
done

Length of output: 6594

per-module-coverage-recorder.sh Show resolved Hide resolved
per-module-coverage-recorder.sh Show resolved Hide resolved
check.sh Show resolved Hide resolved
check.sh Show resolved Hide resolved
coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 19, 2025
@ole-ve ole-ve requested a review from krusche January 19, 2025 12:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
gradle/test.gradle (6)

48-57: Consider moving BasePath to a constants file.

The BasePath constant represents a core package structure that might be used across different Gradle files. Consider moving it to a dedicated constants file to improve maintainability and reusability.


62-62: Update comment to reflect new module-based execution.

The comment should be updated to include examples of both tag-based and module-based test execution for better documentation.

-// Execute the test cases: ./gradlew test
-// Execute only architecture tests: ./gradlew test -DincludeTags="ArchitectureTest"
+// Examples:
+// Execute all test cases: ./gradlew test
+// Execute only architecture tests: ./gradlew test -DincludeTags="ArchitectureTest"
+// Execute tests for specific modules: ./gradlew test -DincludeModules="atlas,shared"

74-80: Consider making the shared module inclusion configurable.

The "shared" module is always included when running module-specific tests. Consider making this configurable in case there are scenarios where we don't want to run shared module tests.

-        // Always execute "shared"-folder when executing module-specifc tests
-        includedModules += "shared"
+        // Include shared module tests by default, unless explicitly disabled
+        if (!project.hasProperty('skipSharedTests')) {
+            includedModules += "shared"
+        }

77-79: Make the package structure more flexible.

The test filter assumes a fixed package structure. Consider making this more flexible to accommodate future package restructuring.

-                testFilter.includeTestsMatching("de.tum.cit.aet.artemis.$val.*")
+                testFilter.includeTestsMatching("${project.ext.BasePath}.$val.*")

Line range hint 84-91: Consider dynamic heap size configuration based on module size.

The heap size settings are fixed for all test executions. For module-specific tests, these values might be excessive. Consider making them configurable based on the modules being tested.

-    minHeapSize = "2g" // initial heap size
-    maxHeapSize = "6g" // maximum heap size
+    // Configure heap sizes based on test scope
+    minHeapSize = runAllTests ? "2g" : "1g"
+    maxHeapSize = runAllTests ? "6g" : "3g"

93-94: Add validation for Jacoco configuration file.

Add a check to ensure the Jacoco configuration file exists before applying it to prevent build failures.

-apply from: "gradle/jacoco.gradle"
+def jacocoConfig = file("gradle/jacoco.gradle")
+if (!jacocoConfig.exists()) {
+    throw new GradleException("Jacoco configuration file not found at: ${jacocoConfig.absolutePath}")
+}
+apply from: jacocoConfig
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf9bebb and ef9392d.

📒 Files selected for processing (1)
  • gradle/test.gradle (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: client-tests-selected
  • GitHub Check: client-style
  • GitHub Check: client-tests
  • GitHub Check: server-style
  • GitHub Check: server-tests
  • GitHub Check: Build and Push Docker Image
  • GitHub Check: Analyse
  • GitHub Check: Build .war artifact
🔇 Additional comments (1)
gradle/test.gradle (1)

64-80: Implementation aligns well with PR objectives.

The test configuration successfully implements module-specific test execution while maintaining support for tag-based filtering. This aligns perfectly with the PR's goal of improving test efficiency by running tests only for changed modules.

@krusche krusche changed the title Development: Execute only tests for changed modules in GitHub test job for non-default branches Development: Execute server tests only for changed modules in GitHub action for non-default branches Jan 19, 2025
@krusche krusche added this to the 7.9.0 milestone Jan 19, 2025
@krusche krusche merged commit 7668bec into develop Jan 19, 2025
22 of 27 checks passed
@krusche krusche deleted the chore/ci-test-changed-modules branch January 19, 2025 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Merged
Development

Successfully merging this pull request may close these issues.

5 participants