├── .editorconfig ├── .github ├── CODEOWNERS ├── codecov.yml ├── dependabot.yml ├── labeler.yml └── workflows │ ├── build.yml │ ├── dependabot.yml │ ├── labeler.yml │ ├── release-auto.yml │ ├── release.yml │ └── upgrade-gradle-wrapper.yml ├── .gitignore ├── .idea └── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── scripts └── git │ └── pre-commit ├── settings.gradle.kts └── src ├── main └── kotlin │ └── com │ └── coditory │ └── gradle │ └── integration │ ├── IntegrationTestPlugin.kt │ ├── IntegrationTestPluginConfig.kt │ ├── JacocoTaskConfiguration.kt │ ├── TestAllTaskConfiguration.kt │ └── TestSuitesConfiguration.kt └── test └── kotlin └── com └── coditory └── gradle └── integration ├── PluginSetupTest.kt ├── TestAllTaskConfigurationTest.kt ├── TestTaskConfigurationTest.kt ├── base ├── GradleTestVersions.kt ├── TestProject.kt └── TestProjectBuilder.kt └── cli ├── CommandLineTest.kt ├── ConfigurationInheritanceTest.kt ├── DeduplicationTest.kt ├── JUnitBasicTest.kt ├── JUnitClasspathTest.kt ├── JacocoBasedTest.kt ├── KotestBasicTest.kt ├── KotestClasspathTest.kt ├── KotlinInternalScopeTest.kt ├── LazyTaskRegisteringTest.kt ├── LombokTest.kt ├── PlatformDependencyTest.kt ├── SpockBasicTest.kt ├── SpockClasspathTest.kt └── Versions.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{kt,kts}] 12 | indent_size = 4 13 | ktlint_code_style = intellij_idea 14 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 15 | ktlint_ignore_back_ticked_identifier = true 16 | ktlint_function_signature_body_expression_wrapping = default 17 | ktlint_standard_function-expression-body = disabled 18 | ktlint_standard_class-signature = disabled -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @coditory/reviewers 2 | 3 | # Exclusions so changes made by dependabot and other bots could be auto reviewed by Coditory App. 4 | # Currently App cannot be a part of a team or CODEOWNERS file. 5 | # https://github.com/orgs/community/discussions/23064 6 | .github/workflows/* 7 | gradle/** 8 | **/build.gradle.kts 9 | **/settings.gradle.kts 10 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | patch: off -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | groups: 8 | # merged and released instantly 9 | sec-updates: 10 | applies-to: security-updates 11 | patterns: 12 | - "*" 13 | # merged automatically 14 | dev-dependencies: 15 | patterns: 16 | - "*" 17 | 18 | - package-ecosystem: "gradle" 19 | directory: "/" 20 | schedule: 21 | interval: "daily" 22 | groups: 23 | # merged and released instantly 24 | sec-updates: 25 | applies-to: security-updates 26 | patterns: 27 | - "*" 28 | # merged automatically 29 | dev-dependencies: 30 | patterns: 31 | # gradle plugins 32 | - "*kotlin-gradle-plugin" 33 | - "com.coditory.integration-test" 34 | - "com.gradle.plugin-publish" 35 | - "org.jlleitschuh.gradle.ktlint" 36 | - "org.jetbrains.kotlinx.kover" 37 | # test dependencies 38 | - "org.assertj*" 39 | - "org.junit*" 40 | # merged and released automatically 41 | prod-dependencies: 42 | update-types: 43 | - "patch" 44 | - "minor" 45 | # requires human approval and has higher chance to fail build 46 | prod-dependencies-major: 47 | update-types: 48 | - "major" 49 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | backport: 2 | - head-branch: ['^v[0-9]+\.x\.x'] 3 | 4 | code: 5 | - changed-files: 6 | - any-glob-to-any-file: 7 | - "src/**" 8 | 9 | build: 10 | - changed-files: 11 | - any-glob-to-any-file: 12 | - "**/*.gradle*" 13 | 14 | ci: 15 | - changed-files: 16 | - any-glob-to-any-file: 17 | - ".github/**" 18 | 19 | documentation: 20 | - changed-files: 21 | - any-glob-to-any-file: 22 | - "**/*.md" 23 | - "docs/**" 24 | 25 | license: 26 | - changed-files: 27 | - any-glob-to-any-file: 28 | - "LICENSE" 29 | 30 | gradle: 31 | - changed-files: 32 | - any-glob-to-any-file: 33 | - "gradlew*" 34 | - ".gradle/**" 35 | - "gradle/**" 36 | - "setting.gradle*" 37 | 38 | gitignore: 39 | - changed-files: 40 | - any-glob-to-any-file: 41 | - ".gitignore" 42 | 43 | codestyle: 44 | - changed-files: 45 | - any-glob-to-any-file: 46 | - ".editorconfig" 47 | - ".idea/codeStyles/**" 48 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches-ignore: 8 | - 'gh-pages' 9 | 10 | jobs: 11 | build: 12 | uses: coditory/jvm-workflows/.github/workflows/build.yml@v1 13 | # Required to pass codecov token 14 | secrets: inherit 15 | with: 16 | java-version: 17 17 | build-command: ./gradlew build coverage 18 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: "Dependabot" 2 | 3 | on: pull_request_target 4 | 5 | jobs: 6 | dependabot: 7 | uses: coditory/workflows/.github/workflows/dependabot.yml@v1 8 | secrets: inherit 9 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Labeler" 2 | 3 | on: pull_request_target 4 | 5 | jobs: 6 | labeler: 7 | uses: coditory/workflows/.github/workflows/labeler.yml@v1 8 | secrets: inherit 9 | -------------------------------------------------------------------------------- /.github/workflows/release-auto.yml: -------------------------------------------------------------------------------- 1 | name: Release Auto 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | security-updates-only: 7 | description: "Security updates only" 8 | type: boolean 9 | required: false 10 | default: false 11 | consider-snapshot: 12 | description: "Consider snapshot" 13 | type: boolean 14 | required: false 15 | default: false 16 | workflow_run: 17 | workflows: ["Build"] 18 | types: [completed] 19 | branches: 20 | - main 21 | - v*x.x 22 | schedule: 23 | # at 5:30 UTC every other month 24 | - cron: "30 5 1 */2 *" 25 | 26 | jobs: 27 | check: 28 | uses: coditory/workflows/.github/workflows/release-check.yml@v1 29 | secrets: inherit 30 | if: | 31 | github.event_name != 'workflow_run' 32 | || !contains(github.event.workflow_run.head_commit.message, '[ci-skip-build]') 33 | with: 34 | security-updates-only: ${{ inputs.security-updates-only || github.event_name == 'workflow_run' }} 35 | 36 | release: 37 | uses: ./.github/workflows/release.yml 38 | secrets: inherit 39 | needs: check 40 | if: needs.check.outputs.release == 'true' 41 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | branch: 7 | type: string 8 | description: Branch name to release 9 | required: true 10 | default: main 11 | section: 12 | type: choice 13 | description: Version section to increment 14 | options: 15 | - patch 16 | - minor 17 | - major 18 | required: true 19 | default: patch 20 | version: 21 | type: string 22 | description: ...or manually define version like 1.2.3, 1.2.3-suffix 23 | required: false 24 | # Called from release-auto 25 | workflow_call: 26 | inputs: 27 | branch: 28 | type: string 29 | required: false 30 | default: main 31 | section: 32 | type: string 33 | required: false 34 | default: patch 35 | version: 36 | type: string 37 | required: false 38 | 39 | jobs: 40 | release: 41 | uses: coditory/workflows/.github/workflows/release.yml@v1 42 | secrets: inherit 43 | with: 44 | branch: ${{ inputs.branch }} 45 | section: ${{ inputs.section }} 46 | version: ${{ inputs.version }} 47 | java-version: 17 48 | release-command: | 49 | ./gradlew publishPlugins \ 50 | -Pgradle.publish.key=$GRADLE_PUBLISH_KEY \ 51 | -Pgradle.publish.secret=$GRADLE_PUBLISH_SECRET \ 52 | -Pversion=$NEXT_VERSION 53 | version-command: ./gradlew version --quiet --no-scan 54 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-gradle-wrapper.yml: -------------------------------------------------------------------------------- 1 | name: Upgrade Gradle Wrapper 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # at 5:30 UTC every month 7 | - cron: "30 5 1 * *" 8 | 9 | jobs: 10 | upgrade-gradle-wrapper: 11 | uses: coditory/jvm-workflows/.github/workflows/upgrade-gradle-wrapper.yml@v1 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | .gradle 3 | .kotlin 4 | build 5 | !gradle/wrapper/gradle-wrapper.jar 6 | 7 | # IntelliJ 8 | out 9 | *.iml 10 | .idea/* 11 | !.idea/codeStyles 12 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 13 | 14 | 15 | 17 | 18 | 19 | 30 | 31 | 33 | 34 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | This is a development focused supplement 4 | to [CONTRIBUTING.md](https://github.com/coditory/.github/blob/main/CONTRIBUTING.md). 5 | 6 | ## Pre commit hook (optional) 7 | 8 | Installing pre-commit hook is optional but can save you some headache when pushing unformatted code. 9 | 10 | Installing git pre-commit hook that formats code with [Ktlint](https://pinterest.github.io/ktlint): 11 | 12 | ```sh 13 | cp scripts/git/pre-commit .git/hooks/pre-commit 14 | ``` 15 | 16 | ## Commit messages 17 | 18 | Before writing a commit message read [this article](https://chris.beams.io/posts/git-commit/). 19 | 20 | ## Build 21 | 22 | Before pushing any changes make sure project builds without errors with: 23 | 24 | ```sh 25 | ./gradlew build 26 | ``` 27 | 28 | ## Code conventions 29 | 30 | This repository follows the [Kotlin coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html). 31 | That are enforced by ktlint and [.editorconfig](../.editorconfig). 32 | 33 | You can check style with: 34 | 35 | ```sh 36 | ./gradlew ktlintCheck 37 | ``` 38 | 39 | ## Unit tests and coverage 40 | 41 | Use [JUnit 5](https://junit.org/junit5/docs/current/user-guide/) for testing. 42 | 43 | Uou can check coverage in `build/reports/kover/` after running: 44 | 45 | ```sh 46 | ./gradlew test coverage 47 | ``` 48 | 49 | ## Validate changes locally 50 | 51 | Before submitting a pull request test your changes on a local project. 52 | There are few ways for testing locally a gradle plugin: 53 | 54 | **Publish plugin to the local maven repository** 55 | 56 | - Publish plugin to your local maven repository (`$HOME/.m2`) with: 57 | ```sh 58 | ./gradlew publishToMavenLocal -Pversion="" && ls -la ~/.m2/repository/com/coditory/gradle/integration-test-plugin 59 | ``` 60 | - Add section to `settings.gradle.kts`: 61 | ```kt 62 | // Instruct a sample project to use maven local to find the plugin 63 | pluginManagement { 64 | repositories { 65 | mavenLocal() 66 | gradlePluginPortal() 67 | } 68 | } 69 | ``` 70 | - Add dependency: 71 | ```kt 72 | plugins { 73 | id("com.coditory.integration-test") version "" 74 | } 75 | ``` 76 | 77 | **Import plugin jar** 78 | Add plugin jar to the sample project (that uses the tested plugin): 79 | 80 | ```kt 81 | buildscript { 82 | dependencies { 83 | classpath(files("/build/libs/integration-test-plugin.jar")) 84 | } 85 | } 86 | 87 | apply(plugin = "com.coditory.integration-test") 88 | ``` 89 | 90 | ## Validating plugin module metadata 91 | 92 | The easiest way to validate plugin's module metadata is to publish the plugin to a dummy local repository. 93 | 94 | Add to `build.gradle.kts`: 95 | 96 | ``` 97 | publishing { 98 | repositories { 99 | maven { 100 | name = "localPluginRepository" 101 | url = uri("./local-plugin-repository") 102 | } 103 | } 104 | } 105 | ``` 106 | 107 | ...and publish the plugin with: 108 | 109 | ``` 110 | ./gradlew publish -Pversion=0.0.1 111 | ``` 112 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Paweł Mendelski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Integration Test Gradle Plugin 2 | 3 | [![Build](https://github.com/coditory/gradle-integration-test-plugin/actions/workflows/build.yml/badge.svg)](https://github.com/coditory/gradle-integration-test-plugin/actions/workflows/build.yml) 4 | [![Coverage](https://codecov.io/gh/coditory/gradle-integration-test-plugin/branch/main/graph/badge.svg)](https://codecov.io/gh/coditory/gradle-integration-test-plugin) 5 | [![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/com.coditory.integration-test)](https://plugins.gradle.org/plugin/com.coditory.integration-test) 6 | 7 | > Single line in build.gradle.kts to enable integration tests in JVM projects 8 | 9 | **Zero configuration**, **single responsibility** gradle plugin for integration tests. 10 | 11 | - Adds `integrationTest` task that executes tests under `src/integration/*`. 12 | - Adds `testAll` task that executes tests under `src/test/*` and `src/integration/*`. 13 | - Handles flags parameters to skip tests `skipTest`, `skipIntegrationTest`, `skipUnitTest`. 14 | - Makes integration classpath extend test classpath and main classpath (in this order). 15 | - Makes sure IntelliJ idea treats `src/integration/*` as test sources. 16 | - Exposes kotlin internal scope (from main and test module) to integration tests. 17 | - Integrates with test coverage tools like [Jacoco](https://docs.gradle.org/current/userguide/jacoco_plugin.html) 18 | and [Kover](https://github.com/Kotlin/kotlinx-kover). 19 | - Integrates with test frameworks like [JUnit5](https://junit.org/junit5/), [Spock](https://spockframework.org/) and 20 | [Kotest](https://kotest.io/). 21 | - Compatible with [gradle configuration cache](https://docs.gradle.org/current/userguide/configuration_cache.html) 22 | and [lazy task configuration](https://docs.gradle.org/current/userguide/lazy_configuration.html). 23 | 24 | ## Using the plugin 25 | 26 | Update `build.gradle.kts` 27 | 28 | ```gradle 29 | plugins { 30 | id("com.coditory.integration-test") version "2.2.5" 31 | } 32 | 33 | dependencies { 34 | integrationImplementation(...) 35 | } 36 | ``` 37 | 38 | Add integration tests under `src/integration`. That's it! 39 | 40 | There are more details below but the rest is quite obvious as it suppose to be. 41 | 42 | ### Sample usages with different test frameworks 43 | 44 | See a [project](https://github.com/coditory/gradle-integration-test-plugin-sample) with all the examples. 45 | 46 |
Java + JUnit5 (project) 47 |

48 | 49 | ```gradle 50 | // build.gradle.kts 51 | plugins { 52 | id("java") 53 | id("com.coditory.integration-test") version "2.2.5" 54 | } 55 | 56 | dependencies { 57 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.0") 58 | testRuntime("org.junit.jupiter:junit-jupiter-engine:5.11.0") 59 | } 60 | 61 | tasks.withType { 62 | useJUnitPlatform() 63 | } 64 | ``` 65 | 66 |

67 |
68 |
Groovy + Spock (project) 69 |

70 | 71 | ```gradle 72 | // build.gradle 73 | plugins { 74 | id "groovy" 75 | id "com.coditory.integration-test" version "2.2.5" 76 | } 77 | 78 | dependencies { 79 | testCompile "org.spockframework:spock-core:2.4-M4-groovy-4.0" 80 | } 81 | 82 | tasks.withType(Test) { 83 | useJUnitPlatform() 84 | } 85 | ``` 86 | 87 |

88 |
89 |
Kotlin + JUnit5 (project) 90 |

91 | 92 | ```gradle 93 | // build.gradle.kts 94 | plugins { 95 | kotlin("jvm") version "2.0.21" 96 | id("com.coditory.integration-test") version "2.2.5" 97 | } 98 | 99 | dependencies { 100 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.3") 101 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.11.3") 102 | } 103 | 104 | tasks.withType { 105 | useJUnitPlatform() 106 | } 107 | ``` 108 | 109 |

110 |
111 |
Kotlin + Kotest (project) 112 |

113 | 114 | ```gradle 115 | // build.gradle.kts 116 | plugins { 117 | kotlin("jvm") version "2.0.21" 118 | id("com.coditory.integration-test") version "2.2.5" 119 | } 120 | 121 | dependencies { 122 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.3") 123 | testRuntime("org.junit.jupiter:junit-jupiter-engine:5.11.3") 124 | testImplementation("io.kotest:kotest-runner-junit5:5.9.1") 125 | } 126 | 127 | tasks.withType { 128 | useJUnitPlatform() 129 | } 130 | ``` 131 | 132 |

133 |
134 | 135 | ## Usage 136 | 137 | Running tests: 138 | 139 | ```sh 140 | # Runs tests from /src/test 141 | ./gradlew test 142 | 143 | # Runs tests /src/integration 144 | ./gradlew integrationTest 145 | ./gradlew iT 146 | 147 | # Runs all tests (/src/test and /src/integration) 148 | ./gradlew testAll 149 | ./gradlew tA 150 | ``` 151 | 152 | Skipping tests: 153 | 154 | ```sh 155 | # Skip all tests 156 | ./gradlew clean build -x test integrationTest 157 | # ...or skipTests=true/false 158 | ./gradlew clean build -PskipTest 159 | 160 | # Skip tests from /src/test 161 | ./gradlew clean build -x test 162 | # ...or skipUnitTests=true/false 163 | ./gradlew clean build -PskipUnitTest 164 | 165 | # Skip tests from /src/integration 166 | ./gradlew clean build -x integrationTest 167 | # ...or skipIntegrationTests=true/false 168 | ./gradlew clean build -PskipIntegrationTest 169 | ``` 170 | 171 | [Test filtering](https://docs.gradle.org/current/userguide/java_testing.html#test_filtering) is supported as well: 172 | 173 | ```sh 174 | ./gradlew iT --tests com.coditory.SampleTest.shouldWork 175 | ``` 176 | 177 | ## The no-plugin alternative 178 | 179 | If you're against adding plugins to your build file, simply copy-paste the configuration from: 180 | 181 | - [Java + Junit5 (no plugin)](https://github.com/coditory/gradle-integration-test-plugin-sample/tree/master/java-junit5-no-plugin/build.gradle) 182 | - [Kotlin + Junit5 (no plugin)](https://github.com/coditory/gradle-integration-test-plugin-sample/tree/master/kotlin-junit5-no-plugin/build.gradle.kts) 183 | 184 | ...though mind the boilerplate 185 | 186 | ## Migrating from 1.x.x to 2.x.x 187 | 188 | - Skipping flags changed names. Use `skipTests`, `skipUnitTests`, `skipIntegrationTests` 189 | instead of `skipTest`, `skipUnitTest`, `skipIntegrationTest`. 190 | - Added integration with Jacoco - coverage from integration tests is automatically included in report. 191 | - Integration with JUnit4 is dropped. 192 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") version "2.1.21" 3 | id("java-gradle-plugin") 4 | id("maven-publish") 5 | id("com.gradle.plugin-publish") version "1.3.1" 6 | id("org.jlleitschuh.gradle.ktlint") version "12.1.2" 7 | id("org.jetbrains.kotlinx.kover") version "0.9.1" 8 | } 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.10") 16 | testImplementation("org.assertj:assertj-core:3.27.3") 17 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.4") 18 | testImplementation("org.junit.jupiter:junit-jupiter-params:5.11.4") 19 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.11.4") 20 | } 21 | 22 | group = "com.coditory.gradle" 23 | 24 | kotlin { 25 | compilerOptions { 26 | allWarningsAsErrors = true 27 | } 28 | } 29 | 30 | java { 31 | toolchain { 32 | languageVersion = JavaLanguageVersion.of(17) 33 | } 34 | } 35 | 36 | ktlint { 37 | version = "1.4.0" 38 | } 39 | 40 | tasks.withType().configureEach { 41 | useJUnitPlatform() 42 | testLogging { 43 | events("passed", "failed", "skipped") 44 | setExceptionFormat("full") 45 | } 46 | } 47 | 48 | tasks.register("coverage") { 49 | dependsOn("koverXmlReport", "koverHtmlReport", "koverLog") 50 | } 51 | 52 | // Marking new version (incrementPatch [default], incrementMinor, incrementMajor) 53 | // ./gradlew markNextVersion -Prelease.incrementer=incrementMinor 54 | // Releasing the plugin: 55 | // ./gradlew release && ./gradlew publishPlugins 56 | gradlePlugin { 57 | website = "https://github.com/coditory/gradle-integration-test-plugin" 58 | vcsUrl = "https://github.com/coditory/gradle-integration-test-plugin" 59 | plugins { 60 | create("integrationTestPlugin") { 61 | id = "com.coditory.integration-test" 62 | implementationClass = "com.coditory.gradle.integration.IntegrationTestPlugin" 63 | displayName = "Integration test plugin" 64 | description = "Gradle Plugin for integration tests" 65 | tags = listOf("test", "integration", "integration-test", "java-integration-test") 66 | } 67 | } 68 | } 69 | 70 | // Prints project version. 71 | // Usage: ./gradlew version --quiet 72 | tasks.register("version") { 73 | val version = project.version 74 | doLast { 75 | println(version) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=2.2.5 2 | org.gradle.configuration-cache=true 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coditory/gradle-integration-test-plugin/47e1043c7ea2a28feaf2d40213cd3e1cb0e923d9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /scripts/git/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euf -o pipefail 3 | 4 | function run_ktlint { 5 | if ! command -v ktlint &>/dev/null; then 6 | echo -e "${RED}Please install Ktlint${ENDCOLOR} (https://pinterest.github.io/ktlint/latest/install/cli/#download-and-verification)" 7 | exit 1 8 | fi 9 | # https://pinterest.github.io/ktlint/0.49.0/install/cli/#git-hooks 10 | KT_FILES=$(git diff --name-only --cached --relative --diff-filter=ACMR -- '*.kt' '*.kts') 11 | if [ -n "$KT_FILES" ]; then 12 | echo -e "${BLUE}Ktlint: linting $(echo "$KT_FILES" | wc -l) files${ENDCOLOR}" 13 | start="$(date +%s)" 14 | echo -n "$KT_FILES" | tr '\n' ',' | ktlint --relative --format --patterns-from-stdin=',' 15 | echo -e "${GREEN}Ktlint: finished in $(($(date +%s) - start))s${ENDCOLOR}" 16 | echo "$KT_FILES" | xargs git add 17 | fi 18 | } 19 | 20 | if [ "${NO_COLOR:-}" != "false" ]; then 21 | RED="\e[31m" 22 | GREEN="\e[32m" 23 | BLUE="\e[34m" 24 | ENDCOLOR="\e[0m" 25 | else 26 | RED="" 27 | GREEN="" 28 | BLUE="" 29 | ENDCOLOR="" 30 | fi 31 | 32 | prestart="$(date +%s)" 33 | echo -e "${BLUE}Pre-commit: Starting${ENDCOLOR}" 34 | 35 | if [ ./scripts/git/pre-commit -nt .git/hooks/pre-commit ]; then 36 | cp -f ./scripts/git/pre-commit .git/hooks/pre-commit 37 | echo -e "${RED}Updated git pre-commit hook. Please re-run commit.${ENDCOLOR}" 38 | exit 1 39 | fi 40 | 41 | run_ktlint 42 | 43 | echo -e "${GREEN}Pre-commit: finished in $(($(date +%s) - prestart))s${ENDCOLOR}" 44 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.gradle.develocity") version ("3.18.2") 3 | } 4 | 5 | rootProject.name = "integration-test-plugin" 6 | 7 | develocity { 8 | buildScan { 9 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" 10 | termsOfUseAgree = "yes" 11 | 12 | publishing.onlyIf { false } 13 | if (!System.getenv("CI").isNullOrEmpty()) { 14 | publishing.onlyIf { true } 15 | tag("CI") 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/coditory/gradle/integration/IntegrationTestPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | import org.gradle.api.plugins.JavaPlugin 6 | import org.gradle.api.plugins.JvmTestSuitePlugin 7 | 8 | @Suppress("UnstableApiUsage") 9 | open class IntegrationTestPlugin : Plugin { 10 | override fun apply(project: Project) { 11 | if (!project.plugins.hasPlugin(JavaPlugin::class.java)) { 12 | project.plugins.apply(JavaPlugin::class.java) 13 | } 14 | if (!project.plugins.hasPlugin(JvmTestSuitePlugin::class.java)) { 15 | project.plugins.apply(JvmTestSuitePlugin::class.java) 16 | } 17 | val config = IntegrationTestPluginConfig.resolve(project) 18 | TestSuitesConfiguration.apply(project, config) 19 | TestAllTaskConfiguration.apply(project, config) 20 | JacocoTaskConfiguration.apply(project) 21 | } 22 | 23 | companion object { 24 | const val PLUGIN_ID = "com.coditory.integration-test" 25 | const val INTEGRATION = "integration" 26 | const val INTEGRATION_TEST = "integrationTest" 27 | const val TEST_ALL_TASK_NAME = "testAll" 28 | const val SKIP_UNIT_TEST_FLAG_NAME = "skipUnitTest" 29 | const val SKIP_INTEGRATION_TEST_FLAG_NAME = "skipIntegrationTest" 30 | const val SKIP_TEST_ALL_FLAG_NAME = "skipTest" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/coditory/gradle/integration/IntegrationTestPluginConfig.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION 4 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION_TEST 5 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.SKIP_INTEGRATION_TEST_FLAG_NAME 6 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.SKIP_TEST_ALL_FLAG_NAME 7 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.SKIP_UNIT_TEST_FLAG_NAME 8 | import org.gradle.api.Project 9 | 10 | internal data class IntegrationTestPluginConfig( 11 | val allTestTaskEnabled: Boolean, 12 | val unitTestsEnabled: Boolean, 13 | val integrationTestsEnabled: Boolean, 14 | ) { 15 | companion object { 16 | fun resolve(project: Project): IntegrationTestPluginConfig { 17 | return IntegrationTestPluginConfig( 18 | allTestTaskEnabled = !skipAllTests(project), 19 | unitTestsEnabled = !skipUnitTests(project), 20 | integrationTestsEnabled = !skipIntegrationTests(project), 21 | ) 22 | } 23 | 24 | private fun skipAllTests(project: Project): Boolean { 25 | return hasTestAllFlag(project) || 26 | (hasSkipUnitTestFlag(project) && hasSkipIntegrationTestFlag(project)) 27 | } 28 | 29 | private fun skipUnitTests(project: Project): Boolean { 30 | return hasTestAllFlag(project) || hasSkipUnitTestFlag(project) 31 | } 32 | 33 | private fun skipIntegrationTests(project: Project): Boolean { 34 | return hasTestAllFlag(project) || hasSkipIntegrationTestFlag(project) 35 | } 36 | 37 | private fun hasTestAllFlag(project: Project): Boolean { 38 | return hasPropertyFlag(project, SKIP_TEST_ALL_FLAG_NAME) 39 | } 40 | 41 | private fun hasSkipUnitTestFlag(project: Project): Boolean { 42 | return hasPropertyFlag(project, SKIP_UNIT_TEST_FLAG_NAME) 43 | } 44 | 45 | private fun hasSkipIntegrationTestFlag(project: Project): Boolean { 46 | return hasPropertyFlag(project, SKIP_INTEGRATION_TEST_FLAG_NAME) || 47 | hasExcludeIntegrationTestTaskParam( 48 | project, 49 | ) 50 | } 51 | 52 | private fun hasExcludeIntegrationTestTaskParam(project: Project): Boolean { 53 | return hasExcludedTask(project, INTEGRATION_TEST) || hasExcludedTask(project, INTEGRATION) 54 | } 55 | 56 | private fun hasPropertyFlag(project: Project, name: String): Boolean { 57 | if (project.properties.containsKey(name)) { 58 | val value = project.properties[name] 59 | return value == null || !value.toString().equals("false", true) 60 | } 61 | return false 62 | } 63 | 64 | private fun hasExcludedTask(project: Project, name: String): Boolean { 65 | return project.gradle.startParameter.excludedTaskNames.contains(name) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/kotlin/com/coditory/gradle/integration/JacocoTaskConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION 4 | import org.gradle.api.Project 5 | import org.gradle.testing.jacoco.plugins.JacocoTaskExtension 6 | import org.gradle.testing.jacoco.tasks.JacocoCoverageVerification 7 | import org.gradle.testing.jacoco.tasks.JacocoReport 8 | 9 | internal object JacocoTaskConfiguration { 10 | private const val JACOCO_PLUGIN = "jacoco" 11 | private const val JACOCO_REPORT_TASK = "jacocoTestReport" 12 | 13 | fun apply(project: Project) { 14 | if (!project.pluginManager.hasPlugin(JACOCO_PLUGIN)) return 15 | project.tasks.withType(JacocoCoverageVerification::class.java).configureEach { task -> 16 | task.mustRunAfter(INTEGRATION) 17 | } 18 | project.tasks.withType(JacocoReport::class.java).configureEach { task -> 19 | task.mustRunAfter(INTEGRATION) 20 | } 21 | // execute only if integration tests or jacocoTestReport are on the execution path 22 | // to preserve lazy task configuration 23 | project.gradle.taskGraph.whenReady { 24 | val names = project.gradle.taskGraph.allTasks.map { it.name } 25 | if (names.contains(JACOCO_REPORT_TASK) || names.contains(INTEGRATION)) { 26 | project.tasks.withType(JacocoReport::class.java) 27 | .named(JACOCO_REPORT_TASK) { reportTask -> 28 | val jacocoTaskExtension = 29 | project.tasks.getByName(INTEGRATION).extensions.getByType(JacocoTaskExtension::class.java) 30 | val dstFile = jacocoTaskExtension.destinationFile?.path 31 | if (dstFile != null) { 32 | reportTask.executionData(dstFile) 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/com/coditory/gradle/integration/TestAllTaskConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION_TEST 4 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.TEST_ALL_TASK_NAME 5 | import org.gradle.api.Project 6 | import org.gradle.api.Task 7 | import org.gradle.api.tasks.testing.Test 8 | import org.gradle.language.base.plugins.LifecycleBasePlugin 9 | 10 | internal object TestAllTaskConfiguration { 11 | fun apply(project: Project, config: IntegrationTestPluginConfig) { 12 | val testTasks = project.tasks.withType(Test::class.java).names 13 | project.tasks.register(TEST_ALL_TASK_NAME, DummyTestTask::class.java) { testAllTask: Task -> 14 | testAllTask.description = "Runs all test suites." 15 | testAllTask.group = LifecycleBasePlugin.VERIFICATION_GROUP 16 | testAllTask.enabled = config.allTestTaskEnabled 17 | testTasks.forEach { 18 | testAllTask.dependsOn(it) 19 | } 20 | testAllTask.dependsOn(INTEGRATION_TEST) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/com/coditory/gradle/integration/TestSuitesConfiguration.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION 4 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION_TEST 5 | import org.gradle.api.Project 6 | import org.gradle.api.Task 7 | import org.gradle.api.plugins.JavaBasePlugin 8 | import org.gradle.api.plugins.JavaPluginExtension 9 | import org.gradle.api.plugins.jvm.JvmTestSuite 10 | import org.gradle.api.tasks.SourceSet 11 | import org.gradle.api.tasks.testing.Test 12 | import org.gradle.language.base.plugins.LifecycleBasePlugin 13 | import org.gradle.testing.base.TestingExtension 14 | 15 | @Suppress("UnstableApiUsage") 16 | internal object TestSuitesConfiguration { 17 | private val isKotlinProject: Boolean by lazy { 18 | try { 19 | Class.forName("org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension") 20 | true 21 | } catch (_: ClassNotFoundException) { 22 | false 23 | } 24 | } 25 | 26 | fun apply(project: Project, config: IntegrationTestPluginConfig) { 27 | setupTestSuite(project, config) 28 | setupTestTask(project, config) 29 | if (isKotlinProject) { 30 | configureKotlinCompilation(project) 31 | } 32 | } 33 | 34 | private fun setupTestSuite(project: Project, config: IntegrationTestPluginConfig) { 35 | val testing = project.extensions.getByType(TestingExtension::class.java) 36 | val test = testing.suites.getByName("test") as JvmTestSuite 37 | test.targets.all { target -> 38 | target.testTask.configure { task -> 39 | task.enabled = config.unitTestsEnabled 40 | } 41 | } 42 | testing.suites.register(INTEGRATION, JvmTestSuite::class.java) { testSuite -> 43 | testSuite.targets.all { target -> 44 | target.testTask.configure { task -> 45 | task.shouldRunAfter(test) 46 | task.enabled = config.integrationTestsEnabled 47 | } 48 | } 49 | setupIntegrationSourceSet(project, testSuite) 50 | } 51 | } 52 | 53 | private fun setupIntegrationSourceSet(project: Project, testSuite: JvmTestSuite) { 54 | val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets 55 | val mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) 56 | val testSourceSet = sourceSets.getByName(SourceSet.TEST_SOURCE_SET_NAME) 57 | val integrationSourceSet = testSuite.sources 58 | 59 | project.configurations.getByName(integrationSourceSet.implementationConfigurationName) 60 | .extendsFrom(project.configurations.getByName(testSourceSet.implementationConfigurationName)) 61 | 62 | project.configurations.getByName(integrationSourceSet.runtimeOnlyConfigurationName) 63 | .extendsFrom(project.configurations.getByName(testSourceSet.runtimeOnlyConfigurationName)) 64 | 65 | project.configurations.getByName(integrationSourceSet.compileOnlyConfigurationName) 66 | .extendsFrom(project.configurations.getByName(testSourceSet.compileOnlyConfigurationName)) 67 | 68 | project.configurations.getByName(integrationSourceSet.annotationProcessorConfigurationName) 69 | .extendsFrom(project.configurations.getByName(testSourceSet.annotationProcessorConfigurationName)) 70 | 71 | integrationSourceSet.compileClasspath += testSourceSet.output + mainSourceSet.output 72 | integrationSourceSet.runtimeClasspath += testSourceSet.output + mainSourceSet.output 73 | } 74 | 75 | private fun setupTestTask(project: Project, config: IntegrationTestPluginConfig) { 76 | project.tasks.register(INTEGRATION_TEST, DummyTestTask::class.java) { integrationTestTask: Task -> 77 | integrationTestTask.description = "Runs integration test suites." 78 | integrationTestTask.group = LifecycleBasePlugin.VERIFICATION_GROUP 79 | integrationTestTask.enabled = config.integrationTestsEnabled 80 | integrationTestTask.dependsOn(INTEGRATION) 81 | } 82 | project.tasks.named(JavaBasePlugin.CHECK_TASK_NAME) { checkTask -> 83 | checkTask.dependsOn(INTEGRATION_TEST) 84 | checkTask.dependsOn(INTEGRATION) 85 | } 86 | } 87 | 88 | private fun configureKotlinCompilation(project: Project) { 89 | val kotlin = project.extensions 90 | .findByType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension::class.java) ?: return 91 | kotlin.target.compilations.getByName(INTEGRATION) { 92 | val test = kotlin.target.compilations.getByName(SourceSet.TEST_SOURCE_SET_NAME) 93 | it.associateWith(test) 94 | } 95 | } 96 | } 97 | 98 | // Test task type required only for better intellij integration 99 | // See: 100 | // https://github.com/coditory/gradle-integration-test-plugin/pull/179 101 | // https://github.com/coditory/gradle-integration-test-plugin/issues/181 102 | // 103 | // Thanks to DummyTestTask type Intellij: 104 | // - displays testAll task results (unit + integration tests together) in a typical test tree 105 | // - applies proper styles to the task in the gradle tasks window 106 | // Drawbacks: 107 | // - Gradle test configuration is executed for instances of Test and DummyTestTasks but there was no observable time penalty 108 | abstract class DummyTestTask : Test() { 109 | override fun executeTests() { 110 | // deliberately empty 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/PluginSetupTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.base.TestProjectBuilder.Companion.createProject 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.gradle.api.Project 6 | import org.junit.jupiter.api.Test 7 | 8 | class PluginSetupTest { 9 | private val project: Project = createProject() 10 | 11 | @Test 12 | fun `should register plugin`() { 13 | assertThat(project.plugins.getPlugin(IntegrationTestPlugin.PLUGIN_ID)) 14 | .isInstanceOf(IntegrationTestPlugin::class.java) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/TestAllTaskConfigurationTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION 4 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION_TEST 5 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.TEST_ALL_TASK_NAME 6 | import com.coditory.gradle.integration.base.TestProjectBuilder.Companion.createProject 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.api.Project 9 | import org.gradle.api.Task 10 | import org.gradle.api.plugins.JavaPlugin.TEST_TASK_NAME 11 | import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP 12 | import org.junit.jupiter.api.Test 13 | 14 | class TestAllTaskConfigurationTest { 15 | private val project: Project = createProject() 16 | 17 | @Test 18 | fun `should configure testAll task`() { 19 | val task = getTestAllTask() 20 | assertThat(task.dependsOn).isEqualTo(setOf(TEST_TASK_NAME, INTEGRATION, INTEGRATION_TEST)) 21 | assertThat(task.description).isEqualTo("Runs all test suites.") 22 | assertThat(task.group).isEqualTo(VERIFICATION_GROUP) 23 | assertThat(task.enabled).isEqualTo(true) 24 | assertThat(task).isInstanceOf(org.gradle.api.tasks.testing.Test::class.java) 25 | } 26 | 27 | private fun getTestAllTask(): Task { 28 | return project.tasks.getByName(TEST_ALL_TASK_NAME) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/TestTaskConfigurationTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION 4 | import com.coditory.gradle.integration.IntegrationTestPlugin.Companion.INTEGRATION_TEST 5 | import com.coditory.gradle.integration.base.TestProjectBuilder.Companion.createProject 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.api.Project 8 | import org.gradle.api.plugins.JavaBasePlugin 9 | import org.gradle.api.plugins.jvm.JvmTestSuite 10 | import org.gradle.api.tasks.SourceSet 11 | import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP 12 | import org.gradle.testing.base.TestingExtension 13 | import org.junit.jupiter.api.Test 14 | import org.gradle.api.tasks.testing.Test as TestTask 15 | 16 | class TestTaskConfigurationTest { 17 | private val project = createProject() 18 | 19 | @Test 20 | fun `should configure integrationTest task`() { 21 | val integrationSourceSet = getSourceSet() 22 | val task = getTestTask() 23 | assertThat(task.testClassesDirs).isNotNull 24 | assertThat(task.description).isEqualTo("Runs the integration suite.") 25 | assertThat(task.group).isEqualTo(VERIFICATION_GROUP) 26 | assertThat(task.testClassesDirs).isEqualTo(integrationSourceSet.output.classesDirs) 27 | assertThat(task.classpath).isEqualTo(integrationSourceSet.runtimeClasspath) 28 | assertThat(task.enabled).isEqualTo(true) 29 | } 30 | 31 | @Test 32 | fun `should configure integrationTest task to run after check`() { 33 | val checkTask = project.tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME) 34 | val dependencies = checkTask.dependsOn 35 | .filterIsInstance() 36 | assertThat(dependencies).contains(INTEGRATION_TEST) 37 | } 38 | 39 | private fun getTestTask(): TestTask { 40 | return project.tasks.getByName(INTEGRATION) as TestTask 41 | } 42 | 43 | @Suppress("UnstableApiUsage") 44 | private fun getSourceSet(project: Project = this.project): SourceSet { 45 | return project.extensions.getByType(TestingExtension::class.java).suites 46 | .getByName(INTEGRATION) 47 | .let { it as JvmTestSuite } 48 | .sources 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/base/GradleTestVersions.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.base 2 | 3 | object GradleTestVersions { 4 | const val GRADLE_MAX_SUPPORTED_VERSION = "current" 5 | const val GRADLE_MIN_SUPPORTED_VERSION = "8.7" 6 | } 7 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/base/TestProject.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.base 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.testkit.runner.BuildResult 5 | import org.gradle.testkit.runner.GradleRunner 6 | import java.io.File 7 | 8 | class TestProject(private val project: Project) : Project by project { 9 | fun toBuildPath(vararg paths: String): String { 10 | return paths.joinToString(File.pathSeparator) { 11 | "${this.layout.buildDirectory.get()}${File.separator}${it.replace("/", File.separator)}" 12 | } 13 | } 14 | 15 | fun readFileFromBuildDir(path: String): String { 16 | return this.layout.buildDirectory.file(path).get().asFile.readText() 17 | } 18 | 19 | fun runGradle(arguments: List, gradleVersion: String? = null): BuildResult { 20 | return gradleRunner(this, arguments, gradleVersion).build() 21 | } 22 | 23 | fun runGradleAndFail(arguments: List, gradleVersion: String? = null): BuildResult { 24 | return gradleRunner(this, arguments, gradleVersion).buildAndFail() 25 | } 26 | 27 | // Used by @AutoClose test annotation 28 | fun close() { 29 | this.projectDir.deleteRecursively() 30 | } 31 | 32 | fun clean() { 33 | this.runGradle(listOf("clean")) 34 | } 35 | 36 | private fun gradleRunner(project: Project, args: List, gradleVersion: String? = null): GradleRunner { 37 | val builder = GradleRunner.create() 38 | .withProjectDir(project.projectDir) 39 | .withArguments(args) 40 | .withPluginClasspath() 41 | .forwardOutput() 42 | if (!gradleVersion.isNullOrBlank() && gradleVersion != "current") { 43 | builder.withGradleVersion(gradleVersion) 44 | } 45 | return builder 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/base/TestProjectBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.base 2 | 3 | import com.coditory.gradle.integration.IntegrationTestPlugin 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.internal.project.DefaultProject 6 | import org.gradle.api.plugins.JavaPlugin 7 | import org.gradle.testfixtures.ProjectBuilder 8 | import java.io.File 9 | import java.nio.file.Files 10 | import kotlin.io.path.createTempDirectory 11 | import kotlin.reflect.KClass 12 | 13 | class TestProjectBuilder private constructor(projectDir: File, name: String) { 14 | private val project = ProjectBuilder.builder() 15 | .withProjectDir(projectDir) 16 | .withName(name) 17 | .build() as DefaultProject 18 | 19 | fun withGroup(group: String): TestProjectBuilder { 20 | project.group = group 21 | return this 22 | } 23 | 24 | fun withVersion(version: String): TestProjectBuilder { 25 | project.version = version 26 | return this 27 | } 28 | 29 | fun withExtProperty(name: String, value: String): TestProjectBuilder { 30 | project.extensions.extraProperties[name] = value 31 | return this 32 | } 33 | 34 | fun withPlugins(vararg plugins: KClass>): TestProjectBuilder { 35 | plugins 36 | .toList() 37 | .forEach { project.plugins.apply(it.java) } 38 | return this 39 | } 40 | 41 | fun withBuildGradleKts(content: String): TestProjectBuilder { 42 | val buildFile = project.rootDir.resolve("build.gradle.kts") 43 | buildFile.writeText(content.trimIndent().trim()) 44 | return this 45 | } 46 | 47 | fun withBuildGradle(content: String): TestProjectBuilder { 48 | val buildFile = project.rootDir.resolve("build.gradle") 49 | buildFile.writeText(content.trimIndent().trim()) 50 | return this 51 | } 52 | 53 | fun withFile(path: String, content: String): TestProjectBuilder { 54 | val filePath = project.rootDir.resolve(path).toPath() 55 | Files.createDirectories(filePath.parent) 56 | val testFile = Files.createFile(filePath).toFile() 57 | testFile.writeText(content.trimIndent().trim()) 58 | return this 59 | } 60 | 61 | fun withDirectory(path: String): TestProjectBuilder { 62 | val filePath = project.rootDir.resolve(path).toPath() 63 | Files.createDirectories(filePath) 64 | return this 65 | } 66 | 67 | fun build(): TestProject { 68 | project.evaluate() 69 | return TestProject(project) 70 | } 71 | 72 | companion object { 73 | fun createProject(): TestProject { 74 | return projectWithPlugins().build() 75 | } 76 | 77 | fun project(name: String = "sample-project"): TestProjectBuilder { 78 | return TestProjectBuilder(createProjectDir(name), name) 79 | } 80 | 81 | private fun projectWithPlugins(name: String = "sample-project"): TestProjectBuilder { 82 | return project(name) 83 | .withPlugins(JavaPlugin::class, IntegrationTestPlugin::class) 84 | } 85 | 86 | @Suppress("EXPERIMENTAL_API_USAGE_ERROR") 87 | private fun createProjectDir(directory: String): File { 88 | val projectParentDir = createTempDirectory().toFile() 89 | val projectDir = projectParentDir.resolve(directory) 90 | projectDir.mkdir() 91 | return projectDir 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/CommandLineTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.api.Test 12 | import org.junit.jupiter.params.ParameterizedTest 13 | import org.junit.jupiter.params.provider.ValueSource 14 | 15 | class CommandLineTest { 16 | companion object { 17 | @AutoClose 18 | private val project = createProject() 19 | 20 | @AutoClose 21 | private val failingProject = createProject(passingIntgTests = false) 22 | 23 | private fun createProject(passingIntgTests: Boolean = true): TestProject { 24 | val name = listOf( 25 | "project", 26 | CommandLineTest::class.simpleName, 27 | if (passingIntgTests) "passing" else "failing", 28 | ).joinToString("-") 29 | return TestProjectBuilder 30 | .project(name) 31 | .withBuildGradleKts( 32 | """ 33 | plugins { 34 | id("com.coditory.integration-test") 35 | } 36 | 37 | repositories { 38 | mavenCentral() 39 | } 40 | 41 | dependencies { 42 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 43 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 44 | } 45 | 46 | tasks.withType().configureEach { 47 | useJUnitPlatform() 48 | testLogging { 49 | events("passed", "failed", "skipped") 50 | setExceptionFormat("full") 51 | } 52 | } 53 | """, 54 | ).withFile( 55 | "src/integration/java/TestIntgSpec.java", 56 | """ 57 | import static org.junit.jupiter.api.Assertions.assertEquals; 58 | import org.junit.jupiter.api.Test; 59 | 60 | public class TestIntgSpec { 61 | @Test 62 | public void shouldPass() { 63 | assertEquals(true, $passingIntgTests); 64 | } 65 | } 66 | """, 67 | ).withFile( 68 | "src/test/java/TestUnitSpec.java", 69 | """ 70 | import static org.junit.jupiter.api.Assertions.assertEquals; 71 | import org.junit.jupiter.api.Test; 72 | 73 | public class TestUnitSpec { 74 | @Test 75 | public void shouldPass() { 76 | assertEquals(true, true); 77 | } 78 | } 79 | """, 80 | ) 81 | .build() 82 | } 83 | } 84 | 85 | @AfterEach 86 | fun cleanProjects() { 87 | project.clean() 88 | failingProject.clean() 89 | } 90 | 91 | @ParameterizedTest(name = "should run unit tests and integration tests on check command for gradle {0}") 92 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 93 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 94 | // when 95 | val result = project.runGradle(listOf("check"), gradleVersion) 96 | // then 97 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 98 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 99 | } 100 | 101 | @ParameterizedTest(name = "should run integration tests on integrationTest command for gradle {0}") 102 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 103 | fun `should run integration tests on integrationTest command`(gradleVersion: String?) { 104 | // when 105 | val result = project.runGradle(listOf("integrationTest"), gradleVersion) 106 | // then 107 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 108 | } 109 | 110 | @ParameterizedTest(name = "should run integration tests on integration command for gradle {0}") 111 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 112 | fun `should run integration tests on integration command`(gradleVersion: String?) { 113 | // when 114 | val result = project.runGradle(listOf("integration"), gradleVersion) 115 | // then 116 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 117 | } 118 | 119 | @Test 120 | fun `should not run integration tests during test task`() { 121 | // when 122 | val result = project.runGradle(listOf("test")) 123 | // then 124 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 125 | assertThat(result.task(":integration")?.outcome).isNull() 126 | } 127 | 128 | @Test 129 | fun `should run integration tests and unit tests during testAll task`() { 130 | // when 131 | val result = project.runGradle(listOf("testAll")) 132 | // then 133 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 134 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 135 | } 136 | 137 | @Test 138 | fun `should exclude integration tests on -x integrationTest`() { 139 | // when 140 | val result = project.runGradle(listOf("check", "-x", "integrationTest")) 141 | // then 142 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 143 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SKIPPED) 144 | } 145 | 146 | @Test 147 | fun `should fail check command when integration tests fail`() { 148 | // when 149 | val result = failingProject.runGradleAndFail(listOf("check")) 150 | // then 151 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.FAILED) 152 | assertThat(result.task(":check")?.outcome).isNull() 153 | } 154 | 155 | @Test 156 | fun `should skip integration tests -PskipIntegrationTest`() { 157 | // when 158 | val result = project.runGradle(listOf("check", "-PskipIntegrationTest")) 159 | // then 160 | assertThat(result.task(":check")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 161 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 162 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SKIPPED) 163 | } 164 | 165 | @Test 166 | fun `should skip all tests on -PskipTest`() { 167 | // when 168 | val result = project.runGradle(listOf("check", "-PskipTest")) 169 | // then 170 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SKIPPED) 171 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SKIPPED) 172 | } 173 | 174 | @Test 175 | fun `should skip unit tests on -PskipUnitTest`() { 176 | // when 177 | val result = project.runGradle(listOf("check", "-PskipUnitTest")) 178 | // then 179 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SKIPPED) 180 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 181 | } 182 | 183 | @ParameterizedTest(name = "task {0} should be cacheable by gradle configuration cache") 184 | @ValueSource(strings = ["test", "integrationTest", "testAll", "check"]) 185 | fun `should be cacheable by gradle configuration cache`(task: String?) { 186 | // when 187 | val result = project.runGradle(listOf("--configuration-cache", task!!)) 188 | // then 189 | assertThat(result.task(":$task")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/ConfigurationInheritanceTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProjectBuilder 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.testkit.runner.TaskOutcome 8 | import org.junit.jupiter.api.AfterEach 9 | import org.junit.jupiter.api.AutoClose 10 | import org.junit.jupiter.params.ParameterizedTest 11 | import org.junit.jupiter.params.provider.ValueSource 12 | 13 | class ConfigurationInheritanceTest { 14 | companion object { 15 | @AutoClose 16 | private val project = TestProjectBuilder 17 | .project("project-${ConfigurationInheritanceTest::class.simpleName}") 18 | .withBuildGradleKts( 19 | """ 20 | plugins { 21 | id("com.coditory.integration-test") 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | testing { 29 | suites { 30 | register("customTest") 31 | } 32 | } 33 | 34 | val customTestImplementation by configurations.getting { 35 | extendsFrom(configurations.integrationImplementation.get()) 36 | } 37 | val customTestRuntimeOnly by configurations.getting { 38 | extendsFrom(configurations.integrationRuntimeOnly.get()) 39 | } 40 | 41 | dependencies { 42 | testImplementation("org.junit.jupiter:junit-jupiter:${Versions.junit}") 43 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 44 | // sample dependency 45 | implementation("com.google.code.gson:gson:${Versions.gson}") 46 | } 47 | 48 | tasks.withType().configureEach { 49 | useJUnitPlatform() 50 | testLogging { 51 | events("passed", "failed", "skipped") 52 | setExceptionFormat("full") 53 | } 54 | } 55 | """, 56 | ).withFile( 57 | "src/customTest/java/TestSpec.java", 58 | """ 59 | import org.junit.jupiter.api.Test; 60 | import com.google.gson.Gson; 61 | 62 | public class TestSpec { 63 | @Test 64 | void shouldResolveCompileDependencyFromMainConfig() { 65 | // Gson class import is the actual test 66 | } 67 | } 68 | """, 69 | ) 70 | .build() 71 | } 72 | 73 | @AfterEach 74 | fun cleanProject() { 75 | project.clean() 76 | } 77 | 78 | @ParameterizedTest(name = "should resolve compile dependencies in custom test suite for gradle {0}") 79 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 80 | fun `should resolve compile dependencies in custom test suite`(gradleVersion: String?) { 81 | // when 82 | val result = project.runGradle(listOf("customTest"), gradleVersion) 83 | // then 84 | assertThat(result.task(":customTest")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/DeduplicationTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.TestProject 4 | import com.coditory.gradle.integration.base.TestProjectBuilder 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.assertj.core.api.ListAssert 7 | import org.gradle.testkit.runner.BuildResult 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.api.Test 12 | 13 | class DeduplicationTest { 14 | companion object { 15 | const val TEST_CONFIGURATION_LOG = "Test config" 16 | 17 | @AutoClose 18 | private val project = createProject() 19 | 20 | private fun createProject(): TestProject { 21 | val name = "project-${DeduplicationTest::class.simpleName}" 22 | return TestProjectBuilder 23 | .project(name) 24 | .withBuildGradleKts( 25 | """ 26 | plugins { 27 | id("com.coditory.integration-test") 28 | } 29 | 30 | repositories { 31 | mavenCentral() 32 | } 33 | 34 | dependencies { 35 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 36 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 37 | } 38 | 39 | tasks.withType().configureEach { 40 | println("$TEST_CONFIGURATION_LOG - " + this.name) 41 | useJUnitPlatform() 42 | testLogging { 43 | events("passed", "failed", "skipped") 44 | setExceptionFormat("full") 45 | } 46 | } 47 | """, 48 | ).withFile( 49 | "src/integration/java/TestIntgSpec.java", 50 | """ 51 | import static org.junit.jupiter.api.Assertions.assertEquals; 52 | import org.junit.jupiter.api.Test; 53 | 54 | public class TestIntgSpec { 55 | @Test 56 | public void shouldPassIntegration() { 57 | assertEquals(true, true); 58 | } 59 | } 60 | """, 61 | ).withFile( 62 | "src/test/java/TestUnitSpec.java", 63 | """ 64 | import static org.junit.jupiter.api.Assertions.assertEquals; 65 | import org.junit.jupiter.api.Test; 66 | 67 | public class TestUnitSpec { 68 | @Test 69 | public void shouldPassUnit() { 70 | assertEquals(true, true); 71 | } 72 | } 73 | """, 74 | ) 75 | .build() 76 | } 77 | } 78 | 79 | @AfterEach 80 | fun cleanProjects() { 81 | project.clean() 82 | } 83 | 84 | @Test 85 | fun `should not duplicate test runs and test configuration on check command`() { 86 | // when 87 | val result = project.runGradle(listOf("check")) 88 | // then all tests pass and run once 89 | assertDeduplicatedSuccessfulTests(result, "check") 90 | // and test configuration was executed 3 times for each test task 91 | assertTestConfigurationsForTasks(result, listOf("integration", "integrationTest", "test")) 92 | } 93 | 94 | @Test 95 | fun `should not duplicate test runs and test configuration on testAll command`() { 96 | // when 97 | val result = project.runGradle(listOf("testAll")) 98 | // then all tests pass and run once 99 | assertDeduplicatedSuccessfulTests(result, "testAll") 100 | // and test configuration was executed 4 times for each test task 101 | assertTestConfigurationsForTasks(result, listOf("testAll", "integration", "integrationTest", "test")) 102 | } 103 | 104 | private fun assertDeduplicatedSuccessfulTests(result: BuildResult, lastTask: String) { 105 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 106 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 107 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 108 | assertThat(result.task(":$lastTask")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 109 | 110 | // and test task runs only unit tests 111 | assertLogsBetweenTasks(result, "test", "compileIntegrationJava") 112 | .containsExactly("TestUnitSpec > shouldPassUnit() PASSED") 113 | .doesNotContain("TestIntgSpec > shouldPassIntegration()") 114 | 115 | // and integration task runs only intg tests 116 | assertLogsBetweenTasks(result, "integration", "integrationTest") 117 | .containsExactly("TestIntgSpec > shouldPassIntegration() PASSED") 118 | .doesNotContain("TestUnitSpec > shouldPassUnit()") 119 | 120 | // and integrationTest task runs no tests (only depends on integration task) 121 | assertLogsBetweenTasks(result, "integrationTest", lastTask) 122 | .doesNotContain("TestUnitSpec > shouldPassIntegration()") 123 | .doesNotContain("TestUnitSpec > shouldPassUnit()") 124 | } 125 | 126 | private fun assertLogsBetweenTasks(result: BuildResult, task: String, nextTask: String): ListAssert { 127 | val logs = result.output.substring( 128 | result.output.indexOf("> Task :$task\n"), 129 | result.output.indexOf("> Task :$nextTask\n"), 130 | ).split("\n").drop(2).filterNot { it.isEmpty() } 131 | return assertThat(logs) 132 | } 133 | 134 | private fun assertTestConfigurationsForTasks(result: BuildResult, tasks: List) { 135 | val testConfigRuns = result.output 136 | .windowed(TEST_CONFIGURATION_LOG.length, 1) 137 | .count { it == TEST_CONFIGURATION_LOG } 138 | assertThat(testConfigRuns).isEqualTo(tasks.size) 139 | tasks.forEach { 140 | assertThat(result.output).contains("$TEST_CONFIGURATION_LOG - $it") 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/JUnitBasicTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.params.ParameterizedTest 12 | import org.junit.jupiter.params.provider.ValueSource 13 | 14 | class JUnitBasicTest { 15 | companion object { 16 | @AutoClose 17 | private val project = createProject() 18 | 19 | @AutoClose 20 | private val failingProject = createProject(passingIntgTests = false) 21 | 22 | private fun createProject(passingIntgTests: Boolean = true): TestProject { 23 | val name = listOf( 24 | "project", 25 | JUnitBasicTest::class.simpleName, 26 | if (passingIntgTests) "passing" else "failing", 27 | ).joinToString("-") 28 | return TestProjectBuilder 29 | .project(name) 30 | .withBuildGradleKts( 31 | """ 32 | plugins { 33 | id("com.coditory.integration-test") 34 | } 35 | 36 | repositories { 37 | mavenCentral() 38 | } 39 | 40 | dependencies { 41 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 42 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 43 | } 44 | 45 | tasks.withType().configureEach { 46 | useJUnitPlatform() 47 | testLogging { 48 | events("passed", "failed", "skipped") 49 | setExceptionFormat("full") 50 | } 51 | } 52 | """, 53 | ).withFile( 54 | "src/integration/java/TestIntgSpec.java", 55 | """ 56 | import org.junit.jupiter.api.Test; 57 | import static org.junit.jupiter.api.Assertions.assertEquals; 58 | 59 | public class TestIntgSpec { 60 | @Test 61 | public void shouldPass() { 62 | assertEquals(true, $passingIntgTests); 63 | } 64 | } 65 | """, 66 | ).withFile( 67 | "src/test/java/TestUnitSpec.java", 68 | """ 69 | import org.junit.jupiter.api.Test; 70 | import static org.junit.jupiter.api.Assertions.assertEquals; 71 | 72 | public class TestUnitSpec { 73 | @Test 74 | public void shouldPass() { 75 | assertEquals(true, true); 76 | } 77 | } 78 | """, 79 | ) 80 | .build() 81 | } 82 | } 83 | 84 | @AfterEach 85 | fun cleanProjects() { 86 | project.clean() 87 | failingProject.clean() 88 | } 89 | 90 | @ParameterizedTest(name = "should pass unit tests and integration tests on check command for gradle {0}") 91 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 92 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 93 | // when 94 | val result = project.runGradle(listOf("check"), gradleVersion) 95 | // then 96 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 97 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 98 | } 99 | 100 | @ParameterizedTest(name = "should fail integration tests on test failure for gradle {0}") 101 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 102 | fun `should fail integration tests on test failure`(gradleVersion: String?) { 103 | // when 104 | val result = failingProject.runGradleAndFail(listOf("integration"), gradleVersion) 105 | // then 106 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.FAILED) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/JUnitClasspathTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.params.ParameterizedTest 12 | import org.junit.jupiter.params.provider.ValueSource 13 | 14 | class JUnitClasspathTest { 15 | companion object { 16 | @AutoClose 17 | private val project = createProject() 18 | 19 | private fun createProject(): TestProject { 20 | val builder = TestProjectBuilder 21 | .project("project-${JUnitClasspathTest::class.simpleName}") 22 | .withBuildGradleKts( 23 | """ 24 | plugins { 25 | id("com.coditory.integration-test") 26 | } 27 | 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 34 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 35 | // sample integration test dependency 36 | integrationImplementation("com.google.code.gson:gson:${Versions.gson}") 37 | } 38 | 39 | tasks.withType().configureEach { 40 | useJUnitPlatform() 41 | testLogging { 42 | events("passed", "failed", "skipped") 43 | setExceptionFormat("full") 44 | } 45 | } 46 | """, 47 | ).withFile( 48 | "src/test/java/sample/ClasspathFileReader.java", 49 | """ 50 | package sample; 51 | 52 | import java.io.IOException; 53 | import java.net.URI; 54 | import java.net.URISyntaxException; 55 | import java.nio.file.Files; 56 | import java.nio.file.Path; 57 | import java.nio.file.Paths; 58 | 59 | public class ClasspathFileReader { 60 | public static String readFile(String name) { 61 | try { 62 | URI uri = ClasspathFileReader.class.getClassLoader() 63 | .getResource(name) 64 | .toURI(); 65 | Path path = Paths.get(uri); 66 | return Files.readString(path); 67 | } catch (IOException | URISyntaxException e) { 68 | throw new RuntimeException("Could not read file from classpath: " + name, e); 69 | } 70 | } 71 | } 72 | """, 73 | ).withFile( 74 | "src/integration/java/sample/TestIntgSpec.java", 75 | """ 76 | package sample; 77 | 78 | import org.junit.jupiter.api.Test; 79 | import static org.junit.jupiter.api.Assertions.assertEquals; 80 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 81 | import static sample.ClasspathFileReader.readFile; 82 | 83 | public class TestIntgSpec { 84 | @Test 85 | public void shouldReadATxtFileFromMain() { 86 | assertEquals("main-a", readFile("a.txt")); 87 | } 88 | 89 | @Test 90 | public void shouldReadBTxtFileFromTest() { 91 | assertEquals("test-b", readFile("b.txt")); 92 | } 93 | 94 | @Test 95 | public void shouldReadCTxtFileFromIntegration() { 96 | assertEquals("integration-c", readFile("c.txt")); 97 | } 98 | 99 | @Test 100 | public void shouldReadConstantValueAFromMain() { 101 | assertEquals("main-a", ConstantValuesA.MODULE); 102 | } 103 | 104 | @Test 105 | public void shouldReadConstantValueBFromTest() { 106 | assertEquals("test-b", ConstantValuesB.MODULE); 107 | } 108 | 109 | @Test 110 | public void shouldReadConstantValueCFromIntegration() { 111 | assertEquals("integration-c", ConstantValuesC.MODULE); 112 | } 113 | 114 | @Test 115 | void shouldResolveIntegrationDependency() { 116 | assertDoesNotThrow(() -> Class.forName("com.google.gson.Gson")); 117 | } 118 | } 119 | """, 120 | ).withFile( 121 | "src/test/java/sample/TestUnitSpec.java", 122 | """ 123 | package sample; 124 | 125 | import org.junit.jupiter.api.Test; 126 | import static org.junit.jupiter.api.Assertions.assertEquals; 127 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 128 | import static sample.ClasspathFileReader.readFile; 129 | 130 | public class TestUnitSpec { 131 | @Test 132 | public void shouldReadATxtFromMain() { 133 | assertEquals("main-a", readFile("a.txt")); 134 | } 135 | 136 | @Test 137 | public void shouldReadBTxtFromTest() { 138 | assertEquals("test-b", readFile("b.txt")); 139 | } 140 | 141 | @Test 142 | public void shouldReadConstantValueAFromMain() { 143 | assertEquals("main-a", ConstantValuesA.MODULE); 144 | } 145 | 146 | @Test 147 | public void shouldReadConstantValueBFromTest() { 148 | assertEquals("test-b", ConstantValuesB.MODULE); 149 | } 150 | } 151 | """, 152 | ) 153 | listOf("main").forEach { 154 | builder 155 | .withFile("src/$it/resources/a.txt", "$it-a") 156 | .withFile( 157 | "src/$it/java/sample/ConstantValuesA.java", 158 | """ 159 | package sample; 160 | 161 | public class ConstantValuesA { 162 | public static final String MODULE = "$it-a"; 163 | } 164 | """, 165 | ) 166 | } 167 | listOf("main", "test").forEach { 168 | builder 169 | .withFile("src/$it/resources/b.txt", "$it-b") 170 | .withFile( 171 | "src/$it/java/sample/ConstantValuesB.java", 172 | """ 173 | package sample; 174 | 175 | public class ConstantValuesB { 176 | public static final String MODULE = "$it-b"; 177 | } 178 | """, 179 | ) 180 | } 181 | listOf("main", "test", "integration").forEach { 182 | builder 183 | .withFile("src/$it/resources/c.txt", "$it-c") 184 | .withFile( 185 | "src/$it/java/sample/ConstantValuesC.java", 186 | """ 187 | package sample; 188 | 189 | public class ConstantValuesC { 190 | public static final String MODULE = "$it-c"; 191 | } 192 | """, 193 | ) 194 | } 195 | return builder.build() 196 | } 197 | } 198 | 199 | @AfterEach 200 | fun cleanProject() { 201 | project.clean() 202 | } 203 | 204 | @ParameterizedTest(name = "should read files from classpath for gradle {0}") 205 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 206 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 207 | // when 208 | val result = project.runGradle(listOf("check"), gradleVersion) 209 | // then 210 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 211 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/JacocoBasedTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProjectBuilder 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.testkit.runner.TaskOutcome 8 | import org.junit.jupiter.api.AfterEach 9 | import org.junit.jupiter.api.AutoClose 10 | import org.junit.jupiter.params.ParameterizedTest 11 | import org.junit.jupiter.params.provider.ValueSource 12 | 13 | class JacocoBasedTest { 14 | companion object { 15 | @AutoClose 16 | private val project = TestProjectBuilder 17 | .project("project-" + JacocoBasedTest::class.simpleName) 18 | .withBuildGradleKts( 19 | """ 20 | plugins { 21 | id("jacoco") 22 | id("com.coditory.integration-test") 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | 29 | dependencies { 30 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 31 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 32 | } 33 | 34 | tasks.withType().configureEach { 35 | useJUnitPlatform() 36 | testLogging { 37 | events("passed", "failed", "skipped") 38 | setExceptionFormat("full") 39 | } 40 | } 41 | 42 | tasks.jacocoTestReport { 43 | reports { 44 | xml.required.set(true) 45 | } 46 | } 47 | """, 48 | ).withFile( 49 | "src/main/java/Calculator.java", 50 | """ 51 | public class Calculator { 52 | public static int add(int a, int b) { 53 | return a + b; 54 | } 55 | 56 | public static int subtract(int a, int b) { 57 | return a - b; 58 | } 59 | } 60 | """, 61 | ).withFile( 62 | "src/integration/java/TestIntgSpec.java", 63 | """ 64 | import org.junit.jupiter.api.Test; 65 | import static org.junit.jupiter.api.Assertions.assertEquals; 66 | 67 | public class TestIntgSpec { 68 | @Test 69 | public void shouldSubtract() { 70 | assertEquals(3, Calculator.subtract(6, 3)); 71 | } 72 | } 73 | """, 74 | ).withFile( 75 | "src/test/java/TestUnitSpec.java", 76 | """ 77 | import org.junit.jupiter.api.Test; 78 | import static org.junit.jupiter.api.Assertions.assertEquals; 79 | 80 | public class TestUnitSpec { 81 | @Test 82 | public void shouldAdd() { 83 | assertEquals(9, Calculator.add(6, 3)); 84 | } 85 | } 86 | """, 87 | ) 88 | .build() 89 | } 90 | 91 | @AfterEach 92 | fun cleanProject() { 93 | project.clean() 94 | } 95 | 96 | @ParameterizedTest(name = "should aggregate coverage from unit and integration tests when using Jacoco {0}") 97 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 98 | fun `should aggregate coverage from unit and integration tests when using Jacoco`(gradleVersion: String?) { 99 | // when 100 | val result = project.runGradle(listOf("check", "jacocoTestReport"), gradleVersion) 101 | // then 102 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 103 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 104 | assertThat(project.readFileFromBuildDir("reports/jacoco/test/jacocoTestReport.xml")) 105 | // missed method is the init 106 | .contains("") 107 | } 108 | 109 | @ParameterizedTest(name = "should aggregate coverage from unit and integration tests when using Jacoco after tests {0}") 110 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 111 | fun `should aggregate coverage from unit and integration tests when using Jacoco after tests`(gradleVersion: String?) { 112 | // given 113 | project.runGradle(listOf("check"), gradleVersion) 114 | // when 115 | project.runGradle(listOf("jacocoTestReport"), gradleVersion) 116 | // then 117 | assertThat(project.readFileFromBuildDir("reports/jacoco/test/jacocoTestReport.xml")) 118 | // missed method is the init 119 | .contains("") 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/KotestBasicTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.params.ParameterizedTest 12 | import org.junit.jupiter.params.provider.ValueSource 13 | 14 | class KotestBasicTest { 15 | companion object { 16 | @AutoClose 17 | private val project = createProject() 18 | 19 | @AutoClose 20 | private val failingProject = createProject(passingIntgTests = false) 21 | 22 | private fun createProject(passingIntgTests: Boolean = true): TestProject { 23 | val name = listOf( 24 | "project", 25 | KotestBasicTest::class.simpleName, 26 | if (passingIntgTests) "passing" else "failing", 27 | ).joinToString("-") 28 | return TestProjectBuilder 29 | .project(name) 30 | .withBuildGradleKts( 31 | """ 32 | plugins { 33 | kotlin("jvm") version "${Versions.kotlin}" 34 | id("com.coditory.integration-test") 35 | } 36 | 37 | repositories { 38 | mavenCentral() 39 | } 40 | 41 | dependencies { 42 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 43 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 44 | testImplementation("io.kotest:kotest-runner-junit5:${Versions.kotest}") 45 | } 46 | 47 | tasks.withType().configureEach { 48 | useJUnitPlatform() 49 | testLogging { 50 | events("passed", "failed", "skipped") 51 | setExceptionFormat("full") 52 | } 53 | } 54 | """, 55 | ).withFile( 56 | "src/integration/kotlin/TestIntgSpec.kt", 57 | """ 58 | import io.kotest.core.spec.style.FreeSpec 59 | import org.junit.jupiter.api.Assertions.assertEquals 60 | 61 | class TestIntgSpec : FreeSpec({ 62 | "should pass" { 63 | assertEquals(true, $passingIntgTests) 64 | } 65 | }) 66 | """, 67 | ).withFile( 68 | "src/test/kotlin/TestUnitSpec.kt", 69 | """ 70 | import io.kotest.core.spec.style.FreeSpec 71 | import org.junit.jupiter.api.Assertions.assertEquals 72 | 73 | class TestUnitSpec : FreeSpec({ 74 | "should pass" { 75 | assertEquals(4, 2 + 2) 76 | } 77 | }) 78 | """, 79 | ) 80 | .build() 81 | } 82 | } 83 | 84 | @AfterEach 85 | fun cleanProjects() { 86 | project.clean() 87 | failingProject.clean() 88 | } 89 | 90 | @ParameterizedTest(name = "should pass unit tests and integration tests on check command for gradle {0}") 91 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 92 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 93 | // when 94 | val result = project.runGradle(listOf("check"), gradleVersion) 95 | // then 96 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 97 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 98 | } 99 | 100 | @ParameterizedTest(name = "should fail integration tests on test failure for gradle {0}") 101 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 102 | fun `should fail integration tests on test failure`(gradleVersion: String?) { 103 | // when 104 | val result = failingProject.runGradleAndFail(listOf("integration"), gradleVersion) 105 | // then 106 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.FAILED) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/KotestClasspathTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.params.ParameterizedTest 12 | import org.junit.jupiter.params.provider.ValueSource 13 | 14 | class KotestClasspathTest { 15 | companion object { 16 | @AutoClose 17 | private val project = createProject() 18 | 19 | private fun createProject(): TestProject { 20 | val builder = TestProjectBuilder 21 | .project("project-${KotestClasspathTest::class.simpleName}") 22 | .withBuildGradleKts( 23 | """ 24 | plugins { 25 | kotlin("jvm") version "${Versions.kotlin}" 26 | id("com.coditory.integration-test") 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 35 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 36 | testImplementation("io.kotest:kotest-runner-junit5:${Versions.kotest}") 37 | // sample integration test dependency 38 | integrationImplementation("com.google.code.gson:gson:${Versions.gson}") 39 | } 40 | 41 | tasks.withType().configureEach { 42 | useJUnitPlatform() 43 | testLogging { 44 | events("passed", "failed", "skipped") 45 | setExceptionFormat("full") 46 | } 47 | } 48 | """, 49 | ).withFile( 50 | "src/test/kotlin/ClasspathFileReader.kt", 51 | """ 52 | import java.net.URI 53 | import java.nio.file.Files 54 | import java.nio.file.Paths 55 | 56 | object ClasspathFileReader { 57 | fun readFile(name: String): String { 58 | val uri: URI = ClasspathFileReader::class.java.classLoader 59 | ?.getResource(name) 60 | ?.toURI()!! 61 | return Files.readString(Paths.get(uri)) 62 | } 63 | } 64 | """, 65 | ).withFile( 66 | "src/integration/kotlin/TestIntgSpec.kt", 67 | """ 68 | import io.kotest.core.spec.style.FreeSpec 69 | import org.junit.jupiter.api.Assertions.assertEquals 70 | import org.junit.jupiter.api.Assertions.assertDoesNotThrow 71 | import ClasspathFileReader.readFile 72 | 73 | class TestIntgSpec : FreeSpec({ 74 | "should read a.txt from main" { 75 | assertEquals("main-a", readFile("a.txt")) 76 | } 77 | 78 | "should read b.txt from test" { 79 | assertEquals("test-b", readFile("b.txt")) 80 | } 81 | 82 | "should read c.txt from integration" { 83 | assertEquals("integration-c", readFile("c.txt")) 84 | } 85 | 86 | "should read constant value A from main" { 87 | assertEquals("main-a", ConstantValuesA.MODULE) 88 | } 89 | 90 | "should read constant value B from test" { 91 | assertEquals("test-b", ConstantValuesB.MODULE) 92 | } 93 | 94 | "should read constant value C from integration" { 95 | assertEquals("integration-c", ConstantValuesC.MODULE) 96 | } 97 | 98 | "should resolve integration dependency" { 99 | assertDoesNotThrow { Class.forName("com.google.gson.Gson") } 100 | } 101 | }) 102 | """, 103 | ).withFile( 104 | "src/test/kotlin/TestUnitSpec.kt", 105 | """ 106 | import io.kotest.core.spec.style.FreeSpec 107 | import org.junit.jupiter.api.Assertions.assertEquals 108 | import ClasspathFileReader.readFile 109 | 110 | class TestUnitSpec : FreeSpec({ 111 | "should read a.txt from main" { 112 | assertEquals("main-a", readFile("a.txt")) 113 | } 114 | 115 | "should read b.txt from test" { 116 | assertEquals("test-b", readFile("b.txt")) 117 | } 118 | 119 | "should read constant value A from main" { 120 | assertEquals("main-a", ConstantValuesA.MODULE) 121 | } 122 | 123 | "should read constant value B from test" { 124 | assertEquals("test-b", ConstantValuesB.MODULE) 125 | } 126 | }) 127 | """, 128 | ) 129 | listOf("main").forEach { 130 | builder 131 | .withFile("src/$it/resources/a.txt", "$it-a") 132 | .withFile( 133 | "src/$it/kotlin/ConstantValuesA.kt", 134 | """ 135 | object ConstantValuesA { 136 | const val MODULE: String = "$it-a"; 137 | } 138 | """, 139 | ) 140 | } 141 | listOf("main", "test").forEach { 142 | builder 143 | .withFile("src/$it/resources/b.txt", "$it-b") 144 | .withFile( 145 | "src/$it/kotlin/ConstantValuesB.kt", 146 | """ 147 | object ConstantValuesB { 148 | const val MODULE: String = "$it-b"; 149 | } 150 | """, 151 | ) 152 | } 153 | listOf("main", "test", "integration").forEach { 154 | builder 155 | .withFile("src/$it/resources/c.txt", "$it-c") 156 | .withFile( 157 | "src/$it/kotlin/ConstantValuesC.kt", 158 | """ 159 | object ConstantValuesC { 160 | const val MODULE: String = "$it-c"; 161 | } 162 | """, 163 | ) 164 | } 165 | return builder.build() 166 | } 167 | } 168 | 169 | @AfterEach 170 | fun cleanProject() { 171 | project.clean() 172 | } 173 | 174 | @ParameterizedTest(name = "should read files from classpath for gradle {0}") 175 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 176 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 177 | // when 178 | val result = project.runGradle(listOf("check"), gradleVersion) 179 | // then 180 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 181 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/KotlinInternalScopeTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProjectBuilder 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.testkit.runner.TaskOutcome 8 | import org.junit.jupiter.api.AfterEach 9 | import org.junit.jupiter.api.AutoClose 10 | import org.junit.jupiter.params.ParameterizedTest 11 | import org.junit.jupiter.params.provider.ValueSource 12 | 13 | class KotlinInternalScopeTest { 14 | companion object { 15 | @AutoClose 16 | private val project = TestProjectBuilder 17 | .project("project-${KotlinInternalScopeTest::class.simpleName}") 18 | .withBuildGradleKts( 19 | """ 20 | plugins { 21 | kotlin("jvm") version "${Versions.kotlin}" 22 | id("com.coditory.integration-test") 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | 29 | dependencies { 30 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 31 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 32 | } 33 | 34 | tasks.withType().configureEach { 35 | useJUnitPlatform() 36 | testLogging { 37 | events("passed", "failed", "skipped") 38 | setExceptionFormat("full") 39 | } 40 | } 41 | """, 42 | ).withFile( 43 | "src/main/kotlin/PublicObject.kt", 44 | """ 45 | object PublicObject { 46 | val SOME_VALUE = "Public" 47 | } 48 | """, 49 | ).withFile( 50 | "src/main/kotlin/InternalObject.kt", 51 | """ 52 | object InternalObject { 53 | val SOME_VALUE = "Internal" 54 | } 55 | """, 56 | ).withFile( 57 | "src/integration/kotlin/TestIntgSpec.kt", 58 | """ 59 | import org.junit.jupiter.api.Test 60 | import org.junit.jupiter.api.Assertions.assertEquals 61 | import InternalObject 62 | import PublicObject 63 | 64 | class TestIntgSpec { 65 | @Test 66 | fun shouldSeePublicObjectOnClasspath() { 67 | assertEquals("Public", PublicObject.SOME_VALUE) 68 | } 69 | 70 | @Test 71 | fun shouldSeeInternalObjectOnClasspath() { 72 | assertEquals("Internal", InternalObject.SOME_VALUE) 73 | } 74 | } 75 | """, 76 | ).withFile( 77 | "src/test/kotlin/TestUnitSpec.kt", 78 | """ 79 | import org.junit.jupiter.api.Test 80 | import org.junit.jupiter.api.Assertions.assertEquals 81 | import InternalObject 82 | import PublicObject 83 | 84 | class TestUnitSpec { 85 | @Test 86 | fun shouldSeePublicObjectOnClasspath() { 87 | assertEquals("Public", PublicObject.SOME_VALUE) 88 | } 89 | 90 | @Test 91 | fun shouldSeeInternalObjectOnClasspath() { 92 | assertEquals("Internal", InternalObject.SOME_VALUE) 93 | } 94 | } 95 | """, 96 | ) 97 | .build() 98 | } 99 | 100 | @AfterEach 101 | fun cleanProject() { 102 | project.clean() 103 | } 104 | 105 | @ParameterizedTest(name = "should make internal scope visible in integration tests for gradle {0}") 106 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 107 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 108 | // when 109 | val result = project.runGradle(listOf("check"), gradleVersion) 110 | // then 111 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 112 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/LazyTaskRegisteringTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.TestProjectBuilder 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.AfterEach 6 | import org.junit.jupiter.api.AutoClose 7 | import org.junit.jupiter.api.Test 8 | 9 | class LazyTaskRegisteringTest { 10 | companion object { 11 | const val TEST_CONFIG_LOG = "Long running configuration..." 12 | 13 | @AutoClose 14 | private val project = TestProjectBuilder 15 | .project(LazyTaskRegisteringTest::class.simpleName!!) 16 | .withBuildGradleKts( 17 | """ 18 | plugins { 19 | id("jacoco") 20 | id("com.coditory.integration-test") 21 | } 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | dependencies { 28 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 29 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 30 | } 31 | 32 | tasks.withType().configureEach { 33 | println("$TEST_CONFIG_LOG") 34 | } 35 | """, 36 | ) 37 | .build() 38 | } 39 | 40 | @AfterEach 41 | fun cleanProject() { 42 | project.clean() 43 | } 44 | 45 | @Test 46 | fun `should register test tasks in a lazy manner`() { 47 | // when 48 | val result = project.runGradle(listOf("clean")) 49 | // then 50 | assertThat(result.output).doesNotContain(TEST_CONFIG_LOG) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/LombokTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProjectBuilder 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.testkit.runner.TaskOutcome 8 | import org.junit.jupiter.api.AfterEach 9 | import org.junit.jupiter.api.AutoClose 10 | import org.junit.jupiter.params.ParameterizedTest 11 | import org.junit.jupiter.params.provider.ValueSource 12 | 13 | class LombokTest { 14 | companion object { 15 | @AutoClose 16 | private val project = TestProjectBuilder 17 | .project("project-" + LombokTest::class.simpleName) 18 | .withBuildGradleKts( 19 | """ 20 | plugins { 21 | id("com.coditory.integration-test") 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | dependencies { 29 | compileOnly("org.projectlombok:lombok:${Versions.lombok}") 30 | annotationProcessor("org.projectlombok:lombok:${Versions.lombok}") 31 | testCompileOnly("org.projectlombok:lombok:${Versions.lombok}") 32 | testAnnotationProcessor("org.projectlombok:lombok:${Versions.lombok}") 33 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 34 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 35 | } 36 | 37 | tasks.withType().configureEach { 38 | useJUnitPlatform() 39 | testLogging { 40 | events("passed", "failed", "skipped") 41 | setExceptionFormat("full") 42 | } 43 | } 44 | """, 45 | ).withFile( 46 | "src/main/java/MainValueExample.java", 47 | """ 48 | import lombok.Value; 49 | 50 | @Value 51 | public class MainValueExample { 52 | private final String name; 53 | } 54 | """, 55 | ).withFile( 56 | "src/test/java/TestValueExample.java", 57 | """ 58 | import lombok.Value; 59 | 60 | @Value 61 | public class TestValueExample { 62 | private final String name; 63 | } 64 | """, 65 | ).withFile( 66 | "src/integration/java/IntgValueExample.java", 67 | """ 68 | import lombok.Value; 69 | 70 | @Value 71 | public class IntgValueExample { 72 | private final String name; 73 | } 74 | """, 75 | ).withFile( 76 | "src/integration/java/TestIntgSpec.java", 77 | """ 78 | import org.junit.jupiter.api.Test; 79 | 80 | import static org.junit.jupiter.api.Assertions.assertEquals; 81 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 82 | 83 | public class TestIntgSpec { 84 | @Test 85 | public void shouldValueObjectsFromMain() { 86 | assertEquals(new MainValueExample("X"), new MainValueExample("X")); 87 | assertNotEquals(new MainValueExample("X"), new MainValueExample("Y")); 88 | } 89 | 90 | @Test 91 | public void shouldValueObjectsFromTest() { 92 | assertEquals(new TestValueExample("X"), new TestValueExample("X")); 93 | assertNotEquals(new TestValueExample("X"), new TestValueExample("Y")); 94 | } 95 | 96 | @Test 97 | public void shouldValueObjectsFromIntegration() { 98 | assertEquals(new IntgValueExample("X"), new IntgValueExample("X")); 99 | assertNotEquals(new IntgValueExample("X"), new IntgValueExample("Y")); 100 | } 101 | } 102 | """, 103 | ).withFile( 104 | "src/test/java/TestUnitSpec.java", 105 | """ 106 | import org.junit.jupiter.api.Test; 107 | 108 | import static org.junit.jupiter.api.Assertions.assertEquals; 109 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 110 | 111 | public class TestUnitSpec { 112 | @Test 113 | public void shouldValueObjectsFromMain() { 114 | assertEquals(new MainValueExample("X"), new MainValueExample("X")); 115 | assertNotEquals(new MainValueExample("X"), new MainValueExample("Y")); 116 | } 117 | 118 | @Test 119 | public void shouldValueObjectsFromTest() { 120 | assertEquals(new TestValueExample("X"), new TestValueExample("X")); 121 | assertNotEquals(new TestValueExample("X"), new TestValueExample("Y")); 122 | } 123 | } 124 | """, 125 | ) 126 | .build() 127 | } 128 | 129 | @AfterEach 130 | fun cleanProject() { 131 | project.clean() 132 | } 133 | 134 | @ParameterizedTest(name = "should run unit tests and integration tests on check command for gradle {0}") 135 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 136 | fun `should run unit tests and integration tests with lombok`(gradleVersion: String?) { 137 | // when 138 | val result = project.runGradle(listOf("check"), gradleVersion) 139 | // then 140 | assertThat(result.task(":test")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 141 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/PlatformDependencyTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProjectBuilder 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.gradle.testkit.runner.TaskOutcome 8 | import org.junit.jupiter.api.AfterEach 9 | import org.junit.jupiter.api.AutoClose 10 | import org.junit.jupiter.params.ParameterizedTest 11 | import org.junit.jupiter.params.provider.ValueSource 12 | 13 | class PlatformDependencyTest { 14 | companion object { 15 | @AutoClose 16 | private val project = TestProjectBuilder 17 | .project("project-${PlatformDependencyTest::class.simpleName}") 18 | .withBuildGradleKts( 19 | """ 20 | plugins { 21 | id("com.coditory.integration-test") 22 | } 23 | 24 | repositories { 25 | mavenCentral() 26 | } 27 | 28 | dependencies { 29 | implementation(platform("org.springframework.boot:spring-boot-dependencies:${Versions.spring}")) 30 | testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.junit}") 31 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.junit}") 32 | integrationImplementation("org.springframework.boot:spring-boot-starter-test") 33 | } 34 | 35 | tasks.withType().configureEach { 36 | useJUnitPlatform() 37 | testLogging { 38 | events("passed", "failed", "skipped") 39 | setExceptionFormat("full") 40 | } 41 | } 42 | """, 43 | ).withFile( 44 | "src/integration/java/TestIntgSpec.java", 45 | """ 46 | import org.junit.jupiter.api.Test; 47 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 48 | 49 | public class TestIntgSpec { 50 | @Test 51 | void shouldResolveDependencyFromBom() { 52 | assertDoesNotThrow(() -> Class.forName("org.springframework.test.context.ContextConfiguration")); 53 | } 54 | } 55 | """, 56 | ) 57 | .build() 58 | } 59 | 60 | @AfterEach 61 | fun cleanProject() { 62 | project.clean() 63 | } 64 | 65 | @ParameterizedTest(name = "should use dependency version from platform dependency for gradle {0}") 66 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 67 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 68 | // when 69 | val result = project.runGradle(listOf("check"), gradleVersion) 70 | // then 71 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.SUCCESS) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/SpockBasicTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome 9 | import org.gradle.testkit.runner.TaskOutcome.SUCCESS 10 | import org.junit.jupiter.api.AfterEach 11 | import org.junit.jupiter.api.AutoClose 12 | import org.junit.jupiter.params.ParameterizedTest 13 | import org.junit.jupiter.params.provider.ValueSource 14 | 15 | class SpockBasicTest { 16 | companion object { 17 | @AutoClose 18 | private val project = createProject() 19 | 20 | @AutoClose 21 | private val failingProject = createProject(passingIntgTests = false) 22 | 23 | private fun createProject(passingIntgTests: Boolean = true): TestProject { 24 | val name = listOf( 25 | "project", 26 | SpockBasicTest::class.simpleName, 27 | if (passingIntgTests) "passing" else "failing", 28 | ).joinToString("-") 29 | return TestProjectBuilder 30 | .project(name) 31 | .withBuildGradle( 32 | """ 33 | plugins { 34 | id 'groovy' 35 | id 'com.coditory.integration-test' 36 | } 37 | 38 | repositories { 39 | mavenCentral() 40 | } 41 | 42 | dependencies { 43 | testImplementation "org.spockframework:spock-core:${Versions.spock}" 44 | } 45 | 46 | tasks.withType(Test) { 47 | testLogging { 48 | events("passed", "failed", "skipped") 49 | setExceptionFormat("full") 50 | } 51 | } 52 | """, 53 | ).withFile( 54 | "src/integration/groovy/TestIntgSpec.groovy", 55 | """ 56 | import spock.lang.Specification 57 | 58 | class TestIntgSpec extends Specification { 59 | def "should pass"() { 60 | expect: 61 | $passingIntgTests 62 | } 63 | } 64 | """, 65 | ).withFile( 66 | "src/test/groovy/TestUnitSpec.groovy", 67 | """ 68 | import spock.lang.Specification 69 | 70 | class TestUnitSpec extends Specification { 71 | def "should pass"() { 72 | expect: 73 | 2 + 2 == 4 74 | } 75 | } 76 | """, 77 | ).build() 78 | } 79 | } 80 | 81 | @AfterEach 82 | fun cleanProjects() { 83 | project.clean() 84 | failingProject.clean() 85 | } 86 | 87 | @ParameterizedTest(name = "should pass unit tests and integration tests on check command for gradle {0}") 88 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 89 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 90 | // when 91 | val result = project.runGradle(listOf("check"), gradleVersion) 92 | // then 93 | assertThat(result.task(":test")?.outcome).isEqualTo(SUCCESS) 94 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(SUCCESS) 95 | } 96 | 97 | @ParameterizedTest(name = "should fail integration tests on test failure for gradle {0}") 98 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 99 | fun `should fail integration tests on test failure`(gradleVersion: String?) { 100 | // when 101 | val result = failingProject.runGradleAndFail(listOf("integration"), gradleVersion) 102 | // then 103 | assertThat(result.task(":integration")?.outcome).isEqualTo(TaskOutcome.FAILED) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/SpockClasspathTest.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MAX_SUPPORTED_VERSION 4 | import com.coditory.gradle.integration.base.GradleTestVersions.GRADLE_MIN_SUPPORTED_VERSION 5 | import com.coditory.gradle.integration.base.TestProject 6 | import com.coditory.gradle.integration.base.TestProjectBuilder 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.gradle.testkit.runner.TaskOutcome.SUCCESS 9 | import org.junit.jupiter.api.AfterEach 10 | import org.junit.jupiter.api.AutoClose 11 | import org.junit.jupiter.params.ParameterizedTest 12 | import org.junit.jupiter.params.provider.ValueSource 13 | 14 | class SpockClasspathTest { 15 | companion object { 16 | @AutoClose 17 | private val project = createProject() 18 | 19 | private fun createProject(): TestProject { 20 | val builder = TestProjectBuilder 21 | .project("project-${SpockClasspathTest::class.simpleName}") 22 | .withBuildGradle( 23 | """ 24 | plugins { 25 | id 'groovy' 26 | id 'com.coditory.integration-test' 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | dependencies { 34 | testImplementation "org.spockframework:spock-core:${Versions.spock}" 35 | // sample integration test dependency 36 | integrationImplementation "com.google.code.gson:gson:${Versions.gson}" 37 | } 38 | 39 | tasks.withType(Test) { 40 | testLogging { 41 | events("passed", "failed", "skipped") 42 | setExceptionFormat("full") 43 | } 44 | } 45 | """, 46 | ).withFile( 47 | "src/test/groovy/ClasspathFileReader.groovy", 48 | """ 49 | class ClasspathFileReader { 50 | static String readFile(String name) throws Exception { 51 | return ClasspathFileReader.class.getResource("/" + name).getText() 52 | } 53 | } 54 | """, 55 | ).withFile( 56 | "src/integration/groovy/TestIntgSpec.groovy", 57 | """ 58 | import spock.lang.Specification 59 | import static ClasspathFileReader.readFile; 60 | 61 | class TestIntgSpec extends Specification { 62 | def "should read a.txt from main"() { 63 | expect: 64 | readFile("a.txt") == "main-a"; 65 | } 66 | 67 | def "should read b.txt from test"() { 68 | expect: 69 | readFile('b.txt') == 'test-b' 70 | } 71 | 72 | def "should read c.txt from integration"() { 73 | expect: 74 | readFile('c.txt') == 'integration-c' 75 | } 76 | 77 | def "should read constant value A from main"() { 78 | expect: 79 | ConstantValuesA.MODULE == 'main-a' 80 | } 81 | 82 | def "should read constant value B from test"() { 83 | expect: 84 | ConstantValuesB.MODULE == 'test-b' 85 | } 86 | 87 | def "should read constant value C from integration"() { 88 | expect: 89 | ConstantValuesC.MODULE == 'integration-c' 90 | } 91 | 92 | def "should resolve integration dependency"() { 93 | when: 94 | Class.forName("com.google.gson.Gson") 95 | then: 96 | noExceptionThrown() 97 | } 98 | } 99 | """, 100 | ).withFile( 101 | "src/test/groovy/TestUnitSpec.groovy", 102 | """ 103 | import spock.lang.Specification 104 | import static ClasspathFileReader.readFile 105 | 106 | class TestUnitSpec extends Specification { 107 | def "should read a.txt from main"() { 108 | expect: 109 | readFile('a.txt') == 'main-a' 110 | } 111 | 112 | def "should read b.txt from test"() { 113 | expect: 114 | readFile('b.txt') == 'test-b' 115 | } 116 | 117 | def "should read constant value A from main"() { 118 | expect: 119 | ConstantValuesA.MODULE == 'main' 120 | } 121 | 122 | def "should read constant value B from test"() { 123 | expect: 124 | ConstantValuesB.MODULE == 'test' 125 | } 126 | } 127 | """, 128 | ) 129 | val lang = { module: String -> if (module == "main") "java" else "groovy" } 130 | listOf("main").forEach { 131 | builder 132 | .withFile("src/$it/resources/a.txt", "$it-a") 133 | .withFile( 134 | "src/$it/${lang(it)}/ConstantValuesA.${lang(it)}", 135 | """ 136 | public class ConstantValuesA { 137 | public static final String MODULE = "$it-a"; 138 | } 139 | """, 140 | ) 141 | } 142 | listOf("main", "test").forEach { 143 | builder 144 | .withFile("src/$it/resources/b.txt", "$it-b") 145 | .withFile( 146 | "src/$it/${lang(it)}/ConstantValuesB.${lang(it)}", 147 | """ 148 | public class ConstantValuesB { 149 | public static final String MODULE = "$it-b"; 150 | } 151 | """, 152 | ) 153 | } 154 | listOf("main", "test", "integration").forEach { 155 | builder 156 | .withFile("src/$it/resources/c.txt", "$it-c") 157 | .withFile( 158 | "src/$it/${lang(it)}/ConstantValuesC.${lang(it)}", 159 | """ 160 | public class ConstantValuesC { 161 | public static final String MODULE = "$it-c"; 162 | } 163 | """, 164 | ) 165 | } 166 | return builder.build() 167 | } 168 | } 169 | 170 | @AfterEach 171 | fun cleanProject() { 172 | project.clean() 173 | } 174 | 175 | @ParameterizedTest(name = "should read files from classpath for gradle {0}") 176 | @ValueSource(strings = [GRADLE_MAX_SUPPORTED_VERSION, GRADLE_MIN_SUPPORTED_VERSION]) 177 | fun `should run unit tests and integration tests on check command`(gradleVersion: String?) { 178 | // when 179 | val result = project.runGradle(listOf("check"), gradleVersion) 180 | // then 181 | assertThat(result.task(":test")?.outcome).isEqualTo(SUCCESS) 182 | assertThat(result.task(":integrationTest")?.outcome).isEqualTo(SUCCESS) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/test/kotlin/com/coditory/gradle/integration/cli/Versions.kt: -------------------------------------------------------------------------------- 1 | package com.coditory.gradle.integration.cli 2 | 3 | object Versions { 4 | val kotlin = "2.0.20" 5 | val junit = "5.11.0" 6 | val kotest = "5.9.1" 7 | val spock = "2.4-M4-groovy-4.0" 8 | val gson = "2.11.0" 9 | val lombok = "1.18.34" 10 | val spring = "3.3.4" 11 | } 12 | --------------------------------------------------------------------------------