diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..21333db --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +indent_style = space + +[{*.sh,gradlew}] +end_of_line = lf + +[{*.bat,*.cmd}] +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8204e5e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +# Auto detect text files and perform LF normalization +* text=auto + +*.java text +*.html text +*.css text +*.js text +*.sql text +*.q text + +*.sh text eol=lf + +*.bat text eol=crlf +*.cmd text eol=crlf diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml new file mode 100644 index 0000000..405a2b3 --- /dev/null +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -0,0 +1,10 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] + +jobs: + validation: + name: "Validation" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..aa7c0c1 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,43 @@ +# For more information on how to modify this file check the following link: +# https://help.github.com/en/actions/automating-your-workflow-with-github-actions + +name: CI + +on: + push: + branches: + - '*' + pull_request: + branches: + - '*' + +# Throw OutOfMemoryError in case less than 35% is free after full GC +# This avoids never-ending GC trashing if memory gets too low in case of a memory leak +env: + _JAVA_OPTIONS: '-XX:GCTimeLimit=90 -XX:GCHeapFreeLimit=35' + +jobs: + test: + name: 'Tests (JDK ${{ matrix.jdk }}, ${{ matrix.os }})' + runs-on: ${{ matrix.os }}-latest + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu + jdk: 11 + - os: windows + jdk: 8 + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 50 + - name: 'Set up JDK ${{ matrix.jdk }}' + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.jdk }} + - uses: burrunan/gradle-cache-action@v1 + name: Test + with: + job-id: jdk${{ matrix.jdk }} + arguments: --scan --no-parallel --no-daemon build diff --git a/.gitignore b/.gitignore index acb3231..651136b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .gradle /build/ +/*/build/ *.iml .idea/ diff --git a/LICENSE b/LICENSE index 8728479..f433b1a 100644 --- a/LICENSE +++ b/LICENSE @@ -175,28 +175,3 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2021 Qameta Software OÜ - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..e036970 --- /dev/null +++ b/NOTICE @@ -0,0 +1 @@ +Copyright 2017-2021 Qameta Software diff --git a/README.md b/README.md index 3a70282..0ad7a14 100644 --- a/README.md +++ b/README.md @@ -4,105 +4,479 @@ [release]: https://github.com/allure-framework/allure-gradle/releases/latest "Release" [release-badge]: https://img.shields.io/github/release/allure-framework/allure-gradle.svg -[bintray]: https://bintray.com/qameta/maven/allure-gradle "Bintray" -[bintray-badge]: https://img.shields.io/bintray/v/qameta/maven/allure-gradle.svg?style=flat +# Allure plugin for Gradle [![build-badge][]][build] [![release-badge][]][release] -# Allure plugin for Gradle [![build-badge][]][build] [![release-badge][]][release] [![bintray-badge][]][bintray] - -Now Allure Plugin allows you to integrate -[Allure](https://docs.qameta.io/allure/latest/) into TestNG, Junit4 and Cucumber JVM gradle projects +Gradle projects plugins for building [Allure](https://docs.qameta.io/allure/latest/) reports for TestNG, JUnit4, JUnit5, Cucumber JVM, and Spock tests. ## Basic usage -this configuration will use gradle integration for Junit4 and TestNG and generate report for a single-module project +`allure-gradle` plugin implements Allure data gathering (e.g. Test` tasks), and data reporting (both individual and aggregate reports). + +Data gathering and reporting are split to different Gradle plugins, so you could apply the ones you need. + +Note: 2.9+ requires Gradle 5.0+ + +The minimal configuration is as follows. +It would configure test tasks to gather Allure results and add `allureReport` and `allureServe` +tasks for report inspection. + +Groovy DSL: ```groovy -buildscript { - repositories { - jcenter() - } - dependencies { - classpath "io.qameta.allure:allure-gradle:" - } +plugins { + id'io.qameta.allure' version '' } -apply plugin: 'io.qameta.allure' +repositories { + // Repository is needed for downloading allure-commandline for building the report + mavenCentral() +} +``` -allure { - autoconfigure = true - version = '2.4.1' +Kotlin DSL: + +```kotlin +plugins { + id("io.qameta.allure") version "" +} + +repositories { + // Repository is needed for downloading allure-commandline for building the report + mavenCentral() } ``` -## Full configuration +`io.qameta.allure` is a shortcut for `io.qameta.allure-gather` + `io.qameta.allure-report`, +so you could apply the plugins you need. + +### Configuring Allure version + +Groovy DSL: ```groovy allure { - version = '2.4.1' - aspectjweaver = true - autoconfigure = true + value = "2.8.0" +} +``` - resultsGlob = { - include '/path/to/project/**/build/**/allure-results' - exclude '/path/to/project/some-project/build' - } - - resultsDir = file('/path/to/project/module1/build/allure-results') - reportDir = file('build/allure-results') - - useJUnit4 { - version = '2.0-BETA10' +Kotlin DSL: + +```kotlin +allure { + value.set("2.8.0") +} +``` + +### Building Allure report + +To build a report, and browse it use the following command: + + ./gradlew allureServe + +Note: by default, `allureServe` does not execute tests, so if you want to execute the relevant +tests and build report, use the following: + + ./gradlew allureReport --depends-on-tests + +To build an aggregate report, and browse it, apply `io.qameta.allure-aggregate-report` plugin and +use the following command: + + ./gradlew allureAggregateServe + +If you need a report only, please use `allureReport` and `allureAggregateReport`. + +By default, `allureAggregate*` aggregates data from the current `project` and its `subprojects`. +However, you need to apply `io.qameta.allure-gather` plugin to the relevant subprojects, so they +provide Allure results. + +## Customizing data gathering + +Data gathering is implemented via `io.qameta.allure-gather` Gradle plugin. + +The values in the sample below are the defaults. +The sample uses Kotlin DSL. In Groovy DSL you could use `allureJavaVersion = "2.13.9"`, however, that is the only difference. + +```kotlin +allure { + gather { + // Configure version for io.qameta.allure:allure-* adapters + allureJavaVersion.set("2.13.9") + aspectjVersion.set("1.9.5") + + autoconfigure.set(true) + autoconfigureListeners.set(true) + aspectjWeaver.set(true) + + // By default, categories.json is detected in src/test/resources/../categories.json, + // However, it would be better to put the file in a well-known location and configure it explicitly + categoriesFile.set(layout.projectDirectory.file("config/allure/categories.json")) + adapters { + junit5 { + // Defaults to allureJavaVersion + adapterVersion.set("...") + enabled.set(true) + // Enables allure-junit4 default test listeners via META-INF/services/... + autoconfigureListeners.set(true) + } + junit4 { + // same as junit5 + } + testng { + // same as junit5 + } + spock + cucumberJvm + // Alternative syntax: cucumberJvm(2) {...} + cucumber2Jvm + cucumber3Jvm + cucumber4Jvm + cucumber5Jvm + cucumber6Jvm + } } - - useJUnit5 { - version = '2.0-BETA10' +} +``` + +### What if I have both JUnit5, JUnit4, and CucumberJVM on the classpath? + +By default, `allure-gradle` would detect all of them and apply all the listeners yielding 3 reports. +If you need only one or two, specify the required ones via `adapters {...}` block. + +### Adding custom results for reporting + +You could add a folder with custom results via `allureRawResultElements` Gradle configuration. + +```kotlin +plugins { + id("io.qameta.allure-gather-base") +} + +dependencies { + allureRawResultElements(files(layout.buildDirectory.dir("custom-allure-results"))) + // or + allureRawResultElements(files("$buildDir/custom-allure-results")) +} + +// If the results are built with a task, you might want adding a dependency so aggregate report +// knows which tasks to run before building the report +allureRawResultElements.outgoing.artifact(file("...")) { + builtBy(customTask) +} +``` + +### Using custom JUnit5 listeners instead of the default ones + +`allure-java` comes with a set of default listeners for JUnit4, JUnit5, and TestNG. +However, you might want to disable them and use your own ones. + +Here's how you disable default listeners: + +```kotlin +allure.gather.adapters.junit5.autoconfigureListeners.set(false) +``` + +An alternative syntax is as follows: + +```kotlin +allure { + gather { + adapters { + // Note: every time you mention an adapter, it is added to the classpath, + // so refrain from mentioning unused adapters here + junit5 { + // Disable allure-junit5 default test listeners + autoconfigureListeners.set(false) + } + testng { + // Disable allure-testng default test listeners + autoconfigureListeners.set(false) + } + } } +} +``` + +## Report generation + +### Aggregating results from multiple projects + +Suppose you have a couple of modules `/module1/build.gradle.kts`, +`/module2/build.gradle.kts` that gather raw results for Allure: - useTestNG { - version = '2.0-BETA10' +```kotlin +// Each submodule +plugin { + `java-library` + id("io.qameta.allure-gather") +} + +allure { + gather { + adapters { + junit5 + } } - - useCucumberJVM { - version = '2.0-BETA10' +} + +// Each Test task will write raw data for Allure automatically +``` + +Here's how you can aggregate that in their parent project (e.g. `root` project): + +`/build.gradle.kts` + +```kotlin +plugin { + id("io.qameta.allure-aggregate-report") +} + +// allure-aggregate-report requires allure-commandline, so we need a repository here +repositories { + mavenCentral() +} +``` + +Browse report: + + ./gradlew allureAggregateServe + +By default `io.qameta.allure-aggregate-report` would aggregate results +from `allprojects` (==current project + its subprojects), however, +you can configure the set of modules as follows: + +```kotlin +// By default, aggregate-report aggregates allprojects (current + subprojects) +// So we want to exclude module3 since it has no data for Allure +configurations.allureAggregateReport.dependencies.remove( + project.dependencies.create(project(":module3")) +) + +// Removing the default allprojects: +configurations.allureAggregateReport.dependencies.clear() + +// Adding a custom dependency +dependencies { + allureAggregateReport(project(":module3")) +} +``` + +### Customizing report folders + +Report generation is implemented via `io.qameta.allure-report` Gradle plugin, so if you need reports, +apply the plugin as follows: + +```kotlin +plugins { + id("io.qameta.allure-report") +} +``` + +By default, the report is produced into Gradle's default reporting folder under `task.name` subfolder: + + $buildDir/reports/allure-report/allureReport + $buildDir/reports/allure-report/allureAggregateReport + +You could adjust the default location as follows: +```kotlin +plugins { + id("io.qameta.allure-report") // the plugin is packaged with Gradle by default +} + +// See https://docs.gradle.org/current/dsl/org.gradle.api.reporting.ReportingExtension.html +// Extension is provided via Gradle's `reporting-base` plugin +reporting { + baseDir = "$buildDir/reports" +} + +allure { + report { + // There might be several tasks producing the report, so the property + // configures a base directory for all the reports + // Each task creates its own subfolder there + reportDir.set(project.reporting.baseDirectory.dir("allure-report")) } - - useCucumber2JVM { - version = '2.0-BETA10' +} +``` + +### Running tests before building the report + +By default, `allureReport` task will NOT execute tests. +This enables trying new `categories.json` faster, however, if you need to see the latest results, the following +might help: + +* Execute tests separately: `./gradlew test` +* Use `--depends-on-tests` as follows (the option should come after the task name): `./gradlew allureReport --depends-on-tests` +* Configure `allure.report.dependsOnTest.set(true)` + +```kotlin +allure { + report { + // By default, allureReport will NOT execute tests + // If the tests are fast (e.g. UP-TO-DATE or FROM-CACHE), + // then you might want configure dependsOnTests.set(true) so you always + // get the latest report from allureReport + dependsOnTests.set(false) } - - useSpock { - version = '2.0-BETA10' +} +``` + +### Customizing allure-commandline download + +Allure download is handled with `io.qameta.allure-download` plugin which adds `allureDownload` task. +Typically, applying `io.qameta.allure-report` is enough, however, you could use `io.qameta.allure-download` +if you do not need reporting and you need just a fresh `allure-commandline` binary. + +By default `allure-commandline` is downloaded from Sonatype OSSRH (also known as Maven Central). + +The plugin receives `allure-commandline` via `io.qameta.allure:allure-commandline:$version@zip` dependency. + +If you have a customized version, you could configure it as follows: + +```kotlin +allure { + // This configures the common Allure version, so it is used for commandline as well + version.set("2.8.0") + + commandline { + // The following patterns are supported: `[group]`, `[module]`, `[version]`, `[extension]` + // The patterns can appear severs times if you need + // By default, downloadUrlPattern is NOT set. + downloadUrlPattern.set("https://server/path/[group]/[module]-[version].[extension]") + + // groupId for allure-commandline + group.set("io.qameta.allure") + // module for allure-commandline + module.set("allure-commandline") + // extension for allure-commandline + extension.set("zip") } - - downloadLink = 'https://dl.bintray.com/qameta/generic/io/qameta/allure/allure/2.1.1/allure-2.1.1.zip' } ``` -`autoconfigure` *boolean* - a flag to specify usage of autoconfiguration, plugin will attempt to find test platform -integration provided by Gradle (currently works only for Junit4 and TestNG) -`aspectjveaver` *boolean* - a flag to specify inclusion/exclusion of aspectjweaver runtime agent +Note: if you configure `downloadUrlPattern`, then `io.qameta.allure-download` plugin configures +an extra `ivy` repository with the provided URL, and it uses `custom.io.qameta.allure:allure-commandline:...` +coordinates to identify custom distribution is needed. + +If you use Gradle 6.2+, then the custom repository is configured with `exclusive content filtering` +which means the repository would be used exclusively for `custom.io.qameta.allure:allure-commandline`. + +If you use Gradle 5.1+, then the repository would be configured with regular filtering, so it would be +slightly less secure and slightly less efficient. + +### Using local allure-commandline binary + +`allure-commandline` is resolved via `allureCommandline` configuration, so you could configure +local file as follows. + +Remember: NEVER use relative paths in your build files since "current directory" does not exist +in a multi-threaded project execution (see https://youtrack.jetbrains.com/issue/IDEA-265203#focus=Comments-27-4795223.0-0). + +```kotlin +dependencies { + // allureCommandline must resolve to a single zip file + // You could use regular Gradle syntax to specify the dependency + allureCommandline(files("/path/to/allure-commandline.zip")) +} +``` + +## Technical details + +### io.qameta.allure-base plugin + +Extensions: +* io.qameta.allure.gradle.base.AllureExtension + + `allure` extension for `project` + +### io.qameta.allure-gather-base plugin + +Extensions: +* io.qameta.allure.gradle.gather.AllureGatherExtension + + `gather` extension for `allure` + +Configurations: +* `allureRawResultElements` + + A consumable configuration that exposes the gathered raw data for building the report + +Tasks: +* `copyCategories: io.qameta.allure.gradle.gather.tasks.CopyCategories` + + Copies `categories.json` to the raw results folders. + See https://github.com/allure-framework/allure2/issues/1236 + +### io.qameta.allure-gather plugin + +Configures automatic gathering of raw data from test tasks, adds `allure-java` adapters to the classpath. + +Configurations: +* `allureAspectjWeaverAgent` + + A configuration to declare AspectJ agent jar for data gathering + +### io.qameta.allure-download plugin + +Downloads and unpacks `allure-commandline` + +Extensions: +* `io.qameta.allure.gradle.download.AllureCommandlineExtension` + + `commandline` extension for `allure` + +Configurations: +* `allureCommandline` + + A configuration to resolve `allure-commandline` zip + +Tasks: +* `allureDownload: io.qameta.allure.gradle.download.tasks.DownloadAllure` + + Retrieves and unpacks `allure-commandline` + +### io.qameta.allure-report-base plugin + +Applies `reporting-base` plugin and adds `allure.report` extension. + +Extensions: +* `io.qameta.allure.gradle.report.AllureReportExtension` + + `report` extension for `allure` + +### io.qameta.allure-report plugin + +Builds Allure report for the current project. + +Configurations: +* `allureReport` + + Note: prefer exposing raw results via `allureRawResultElements` configuration + rather than declaring them in `allureReport` configuration. + +Tasks: +* `allureReport: io.qameta.allure.gradle.report.tasks.AllureReport` -`clean` *boolean* - enable `--clean` option for the Allure commandline tool + Builds Allure report for the current project -`version` *String* - specify report generator version, note, this property is necessary to enable `allure` and -`aggregatedAllureReport` tasks +* `allureServe: io.qameta.allure.gradle.report.tasks.AllureServe` -`configuration` *String* (`default = 'testCompile'`) - configuration name where to bind plugin dependencies + Launches a web server for browsing Allure report -`resultsDir` *File* - directory for Allure results in the current project, `build\allure-results` by default +### io.qameta.allure-aggregate-report plugin -`reportDir` *File* - directory for Allure results in the current project, `build\allure-results` by default +Builds Allure aggregate report. -`allureJavaVersion` *String* - version of allure java release to be used for autoconfiguration +Configurations: +* `allureAggregateReport` -`downloadLink` *String* - custom location of Allure distribution to download from, by default allure is downloaded from -bintray by sspecified version and installed to `.allure` folder in the project root. + A configuration for declaring projects to aggregate the results from. + Each project exposes its raw results via `allureRawResultElements` configuration. -## Tasks +Tasks: +* `allureAggregateReport: io.qameta.allure.gradle.report.tasks.AllureReport` -### `allureReport` + Builds Allure aggregate report -Creates Allure report for a single-module project +* `allureAggregateServe: io.qameta.allure.gradle.report.tasks.AllureServe` -### `allureServe` -Creates Allure report for a single-module project in the tmp folder and opens it in the default browser. + Launches a web server for browsing Allure aggregate report diff --git a/allure-base-plugin/build.gradle.kts b/allure-base-plugin/build.gradle.kts new file mode 100644 index 0000000..a8bb974 --- /dev/null +++ b/allure-base-plugin/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + id("allure-gradle.kotlin-dsl-published-plugin") +} + +group = "io.qameta.allure.gradle.base" + +dependencies { + testImplementation(project(":testkit-junit4")) + testImplementation("org.assertj:assertj-core:_") +} + +tasks.test { + // Treat test task out-of-date if src/it changes + inputs.dir(layout.projectDirectory.dir("src/it")).optional() +} + +gradlePlugin { + plugins { + create("allureBasePlugin") { + id = "io.qameta.allure-base" + description = "Adds a common allure extension to the project" + implementationClass = "io.qameta.allure.gradle.base.AllureBasePlugin" + } + } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureBasePlugin.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureBasePlugin.kt new file mode 100644 index 0000000..0ebb7f7 --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureBasePlugin.kt @@ -0,0 +1,33 @@ +package io.qameta.allure.gradle.base + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.attributes.Usage +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.named + +/** + * The plugin adds [AllureExtension] extension which is used by most of the other Allure plugins. + */ +open class AllureBasePlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-base" + } + + override fun apply(target: Project): Unit = target.run { + // TODO: migrate to precompiled script plugin once Gradle 6.0 could be the minimal supported Gradle version + // See https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins + extensions.create(AllureExtension.NAME, objects) + + configurations.create("allureWorkaroundGradleBug") { + isCanBeConsumed = true + isCanBeResolved = false + isVisible = false + description = + "This configuration workarounds Gradle's <> error" + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named("please_do_not_use_this")) + } + } + } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureExtension.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureExtension.kt new file mode 100644 index 0000000..0a8a34c --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/AllureExtension.kt @@ -0,0 +1,98 @@ +package io.qameta.allure.gradle.base + +import io.qameta.allure.gradle.util.conv +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.provideDelegate + +/** + * Provides API for configuring common properties for Allure. + */ +open class AllureExtension( + objects: ObjectFactory +) { + companion object { + const val NAME = "allure" + } + + /** + * `allure-commandline` version + */ + val version: Property = objects.property().conv("2.13.6") + + // TODO: remove when deprecated [aspectjweaver] is removed + private val aspectjWeaver by lazy { + @Suppress("unchecked_cast") + gatherExtension + .let { + it::class.java.getMethod("getAspectjWeaver").invoke(it) + } as Property + } + + @Deprecated(level = DeprecationLevel.WARNING, message = "Use gather.aspectjWeaver") + var aspectjweaver: Boolean + get() = aspectjWeaver.get() + set(value) = aspectjWeaver.set(value) + + // TODO: remove when deprecated [aspectjweaver] is removed + private val autoconfigureProperty by lazy { + @Suppress("unchecked_cast") + gatherExtension + .let { + it::class.java.getMethod("getAutoconfigure").invoke(it) + } as Property + } + + @Deprecated(level = DeprecationLevel.WARNING, message = "Use gather.autoconfigure") + var autoconfigure: Boolean + get() = autoconfigureProperty.get() + set(value) = autoconfigureProperty.set(value) + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.cucumberJvm") + fun useCucumberJVM(action: Action) { + action.execute(getAdapter("getCucumberJvm")) + } + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.cucumber2Jvm") + fun useCucumber2JVM(action: Action) { + action.execute(getAdapter("getCucumber2Jvm")) + } + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.junit4") + fun useJUnit4(action: Action) { + action.execute(getAdapter("getJunit4")) + } + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.junit5") + fun useJUnit5(action: Action) { + action.execute(getAdapter("getJunit5")) + } + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.testng") + fun useTestNG(action: Action) { + action.execute(getAdapter("getTestng")) + } + + // visible for Groovy DSL + @Deprecated(level = DeprecationLevel.HIDDEN, message = "Use adapters.spock") + fun useSpock(action: Action) { + action.execute(getAdapter("getSpock")) + } + + private val gatherExtension: Any + get() = let { it as ExtensionAware }.extensions.getByName("gather") + + private fun getAdapter(adapterName: String) = + gatherExtension + .let { it::class.java.getMethod("getAdapters").invoke(it) } + .let { it::class.java.getMethod(adapterName).invoke(it) } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/backport/GradleExtensions.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/backport/GradleExtensions.kt new file mode 100644 index 0000000..c7f3e92 --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/backport/GradleExtensions.kt @@ -0,0 +1,57 @@ +package io.qameta.allure.gradle.util + +import org.gradle.api.DomainObjectSet +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.named +import org.gradle.util.GradleVersion +import org.gradle.util.WrapUtil + +val gradleGe51 = GradleVersion.current() >= GradleVersion.version("5.1") +val gradleGe53 = GradleVersion.current() >= GradleVersion.version("5.3") +val gradleGe55 = GradleVersion.current() >= GradleVersion.version("5.5") +val gradleGe56 = GradleVersion.current() >= GradleVersion.version("5.6") +val gradleGe65 = GradleVersion.current() >= GradleVersion.version("6.5") + +fun Property.conv(v: T) = if (gradleGe51) convention(v) else apply { set(v) } +fun Property.conv(v: Provider) = if (gradleGe51) convention(v) else apply { set(v) } + +fun ListProperty.conv(v: Iterable) = if (gradleGe51) convention(v) else apply { set(v) } +fun ListProperty.conv(v: Provider>) = if (gradleGe51) convention(v) else apply { set(v) } + +fun MapProperty.conv(v: Map) = if (gradleGe51) convention(v) else apply { set(v) } +fun MapProperty.conv(v: Provider>) = if (gradleGe51) convention(v) else apply { set(v) } + +fun Property.forUseAtConfigurationTimeBackport(): Property = apply { + if (gradleGe65) { + // forUseAtConfigurationTime is Gradle 6.5+ feature + forUseAtConfigurationTime() + } +} + +inline fun ObjectFactory.domainObjectSetBackport(): DomainObjectSet = + if (gradleGe55) domainObjectSet(T::class.java) else WrapUtil.toDomainObjectSet(T::class.java) + +fun AttributeContainer.categoryLibrary(objects: ObjectFactory) { + if (gradleGe53) { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + } +} + +fun AttributeContainer.categoryDocumentation(objects: ObjectFactory) { + if (gradleGe53) { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + } +} + +fun AttributeContainer.libraryElementsJar(objects: ObjectFactory) { + if (gradleGe56) { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) + } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/dsl/AllureExtensions.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/dsl/AllureExtensions.kt new file mode 100644 index 0000000..e263e1e --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/dsl/AllureExtensions.kt @@ -0,0 +1,7 @@ +package io.qameta.allure.gradle.base.dsl + +import io.qameta.allure.gradle.base.AllureExtension +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.ExtensionContainer + +val AllureExtension.extensions: ExtensionContainer get() = (this as ExtensionAware).extensions diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/metadata/AllureResultType.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/metadata/AllureResultType.kt new file mode 100644 index 0000000..48a5479 --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/metadata/AllureResultType.kt @@ -0,0 +1,19 @@ +package io.qameta.allure.gradle.base.metadata + +import org.gradle.api.attributes.Attribute + +enum class AllureResultType { + RAW, + CATEGORIES, + + @Deprecated( + message = "This category would be removed once Allure would support multiple categories.json files", + level = DeprecationLevel.WARNING + ) + COPY_CATEGORIES + ; + + companion object { + val ATTRIBUTE = Attribute.of("io.qameta.allure", AllureResultType::class.java) + } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/AllureExecTask.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/AllureExecTask.kt new file mode 100644 index 0000000..9a12bfb --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/AllureExecTask.kt @@ -0,0 +1,70 @@ +package io.qameta.allure.gradle.base.tasks + +import io.qameta.allure.gradle.util.conv +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.FileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.* +import org.gradle.api.tasks.options.Option +import org.gradle.kotlin.dsl.property +import java.io.File + +abstract class AllureExecTask constructor(objects: ObjectFactory) : DefaultTask() { + @InputDirectory + @PathSensitive(PathSensitivity.NONE) + val allureHome = objects.directoryProperty() + + @Internal + @Option(option = "verbose", description = "Switch on the verbose mode") + val verbose = objects.property().conv(false) + + @get:Internal + protected val allureExecutable: File + get() { + val homeDir = allureHome.get().asFile + val binDir = homeDir.resolve("bin") + + val allureExecutable = if (!Os.isFamily(Os.FAMILY_WINDOWS)) { + binDir.resolve("allure") + } else { + binDir.resolve("allure.cmd").takeIf { it.exists() } ?: binDir.resolve("allure.bat") + } + if (!allureExecutable.exists()) { + throw IllegalArgumentException("Cannot find allure commandline in $homeDir") + } + allureExecutable.setExecutable(true) + return allureExecutable + } + + // InputDirectories does not exist yet: https://github.com/gradle/gradle/issues/7485#issuecomment-585289792 + @Internal + val resultsDirs = objects.property() + + @Option(option = "depends-on-tests", description = "Execute the relevant test tasks before launching Allure") + fun dependsOnTests() { + dependsOnTests.set(true) + } + + /** + * Typically, [allureReport] would execute all the tests it depends upon. + * However, in certain cases only report re-execution is needed, then `skipDependsOn` would be useful. + */ + @Input + val dependsOnTests = objects.property().conv(false) + + @get:Internal + protected val rawResults: FileCollection + get() = + resultsDirs.get().filter { it.exists() && it.isDirectory } + + @InputFiles + @SkipWhenEmpty + @PathSensitive(PathSensitivity.RELATIVE) + protected val inputFiles = project.files(resultsDirs.map { dirs -> dirs.map { project.fileTree(it) } }) + + init { + dependsOn(dependsOnTests.map { if (it) emptyList() else resultsDirs }) + } +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/ConditionalArgumentProvider.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/ConditionalArgumentProvider.kt new file mode 100644 index 0000000..6b6a75a --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/ConditionalArgumentProvider.kt @@ -0,0 +1,17 @@ +package io.qameta.allure.gradle.base.tasks + +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.process.CommandLineArgumentProvider + +/** + * Enables lazy computation of the command line arguments. + * Note: do not use this class if the value of the option depends on the file contents. + */ +class ConditionalArgumentProvider( + @Input + val args: Provider> +) : CommandLineArgumentProvider { + + override fun asArguments(): Iterable = args.get() +} diff --git a/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/JavaAgentArgumentProvider.kt b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/JavaAgentArgumentProvider.kt new file mode 100644 index 0000000..d402f64 --- /dev/null +++ b/allure-base-plugin/src/main/kotlin/io/qameta/allure/gradle/base/tasks/JavaAgentArgumentProvider.kt @@ -0,0 +1,12 @@ +package io.qameta.allure.gradle.base.tasks + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.Classpath +import org.gradle.process.CommandLineArgumentProvider + +class JavaAgentArgumentProvider(classPath: Configuration) : CommandLineArgumentProvider { + @get:Classpath + val agentJar: Configuration = classPath + + override fun asArguments() = listOf("-javaagent:${agentJar.singleFile}") +} diff --git a/allure-gather-plugin/build.gradle.kts b/allure-gather-plugin/build.gradle.kts new file mode 100644 index 0000000..7a0546c --- /dev/null +++ b/allure-gather-plugin/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("allure-gradle.kotlin-dsl-published-plugin") +} + +group = "io.qameta.allure.gradle.gather" + +dependencies { + implementation(project(":allure-base-plugin")) + testImplementation(project(":testkit-junit4")) + testImplementation("org.assertj:assertj-core:_") +} + +tasks.test { + // Treat test task out-of-date if src/it changes + inputs.dir(layout.projectDirectory.dir("src/it")).optional() +} + +gradlePlugin { + plugins { + create("allureCollectBasePlugin") { + id = "io.qameta.allure-gather-base" + description = "Declares common configurations for producing and consuming Allure results and reports" + implementationClass = "io.qameta.allure.gradle.gather.AllureGatherBasePlugin" + } + create("allureCollectPlugin") { + id = "io.qameta.allure-gather" + description = "Implements autoconfiguration for gathering data for Allure" + implementationClass = "io.qameta.allure.gradle.gather.AllureGatherPlugin" + } + } +} diff --git a/allure-gather-plugin/src/it/adapter-all/build.gradle b/allure-gather-plugin/src/it/adapter-all/build.gradle new file mode 100644 index 0000000..e90567d --- /dev/null +++ b/allure-gather-plugin/src/it/adapter-all/build.gradle @@ -0,0 +1,17 @@ +plugins { + id("java") + id("io.qameta.allure-gather") +} + +repositories { + mavenCentral() +} + +tasks.register("printAdapters") { + doLast { + buildDir.mkdirs() + file("$buildDir/printAdapters.txt").write( + allure.gather.adapters.toList().collect { it.toString() }.sort().toString() + ) + } +} diff --git a/allure-gather-plugin/src/it/adapter-junit5-spock-kts/build.gradle.kts b/allure-gather-plugin/src/it/adapter-junit5-spock-kts/build.gradle.kts new file mode 100644 index 0000000..a48173a --- /dev/null +++ b/allure-gather-plugin/src/it/adapter-junit5-spock-kts/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("io.qameta.allure-gather") +} + +repositories { + mavenCentral() +} + +allure { + gather { + adapters { + junit5 { + adapterVersion.set("42.0") + } + spock + } + } +} + +val printAdapters by tasks.registering { + doLast { + buildDir.mkdirs() + file("$buildDir/printAdapters.txt").writeText( + allure.gather.adapters.toList().map { it.toString() }.sorted().toString() + ) + } +} diff --git a/allure-gather-plugin/src/main/kotlin/AllureExtensionGatherExtensions.kt b/allure-gather-plugin/src/main/kotlin/AllureExtensionGatherExtensions.kt new file mode 100644 index 0000000..7beb725 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/AllureExtensionGatherExtensions.kt @@ -0,0 +1,11 @@ +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.gather.AllureGatherExtension +import org.gradle.api.Action +import org.gradle.api.plugins.ExtensionAware +import org.gradle.kotlin.dsl.the + +val AllureExtension.gather: AllureGatherExtension get() = (this as ExtensionAware).the() + +fun AllureExtension.gather(configureAction: Action) { + configureAction.execute(gather) +} diff --git a/allure-gather-plugin/src/main/kotlin/DeprecatedAllureGatherExtensions.kt b/allure-gather-plugin/src/main/kotlin/DeprecatedAllureGatherExtensions.kt new file mode 100644 index 0000000..9351cb0 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/DeprecatedAllureGatherExtensions.kt @@ -0,0 +1,63 @@ +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.gather.config.AdapterConfig +import org.gradle.api.Action + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.cucumberJvm", + replaceWith = ReplaceWith("adapters.cucumberJvm.configure(action)") +) +fun AllureExtension.useCucumberJVM(action: Action) { + gather.adapters { + action.execute(cucumberJvm) + } +} + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.cucumber2Jvm", + replaceWith = ReplaceWith("adapters.cucumber2Jvm { }") +) +fun AllureExtension.useCucumber2JVM(action: Action) { + gather.adapters { + action.execute(cucumber2Jvm) + } +} + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.junit4", + replaceWith = ReplaceWith("adapters.junit4 { }") +) +fun AllureExtension.useJUnit4(action: Action) { + gather.adapters { + action.execute(junit4) + } +} + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.junit4", + replaceWith = ReplaceWith("adapters.junit5 { }") +) +fun AllureExtension.useJUnit5(action: Action) { + gather.adapters { + action.execute(junit5) + } +} + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.testng", + replaceWith = ReplaceWith("adapters.testng { }") +) +fun AllureExtension.useTestNG(action: Action) { + gather.adapters { + action.execute(testng) + } +} + +@Deprecated( + level = DeprecationLevel.WARNING, message = "Use adapters.spock", + replaceWith = ReplaceWith("adapters.spock { }") +) +fun AllureExtension.useSpock(action: Action) { + gather.adapters { + action.execute(spock) + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherBasePlugin.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherBasePlugin.kt new file mode 100644 index 0000000..7815f1c --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherBasePlugin.kt @@ -0,0 +1,78 @@ +package io.qameta.allure.gradle.gather + +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.base.dsl.extensions +import io.qameta.allure.gradle.base.metadata.AllureResultType +import io.qameta.allure.gradle.gather.AllureGatherBasePlugin.Companion.ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME +import io.qameta.allure.gradle.gather.tasks.CopyCategories +import io.qameta.allure.gradle.util.categoryDocumentation +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.attributes.Usage +import org.gradle.kotlin.dsl.* + +/** + * The plugin adds [ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME] configuration so the project + * can share raw Allure results for aggregation. + */ +open class AllureGatherBasePlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-gather-base" + const val ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME = "allureRawResultElements" + const val ALLURE_COPY_CATEGORIES_ELEMENTS_CONFIGURATION_NAME = "allureCopyCategoriesElements" + const val ALLURE_USAGE = "Allure" + } + + override fun apply(target: Project): Unit = target.run { + val allureExtension = the() + allureExtension.extensions.create( + AllureGatherExtension.NAME, + project, + // Gradle 5 can't inject objects yet + // TODO: remove when Gradle 5 support can be dropped + objects + ) + + val rawResultElements = configurations.create(ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME) { + description = + "The configuration exposes Allure raw results (simple-result.json, executor.json) for reporting" + isCanBeConsumed = true + isCanBeResolved = false + attributes { + categoryDocumentation(objects) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(ALLURE_USAGE)) + attribute(AllureResultType.ATTRIBUTE, AllureResultType.RAW) + } + } + + val copyCategoriesElements = configurations.create(ALLURE_COPY_CATEGORIES_ELEMENTS_CONFIGURATION_NAME) { + description = + "The configuration Allows registering tasks that would copy updated categories.json files without re-running tests" + isVisible = false + isCanBeConsumed = true + isCanBeResolved = false + attributes { + categoryDocumentation(objects) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(ALLURE_USAGE)) + attribute(AllureResultType.ATTRIBUTE, @Suppress("deprecation") AllureResultType.COPY_CATEGORIES) + } + } + + val allureRawResultDirs by configurations.creating { + description = "gather all allure-results folders in the current project" + isVisible = false + isCanBeConsumed = false + isCanBeResolved = true + extendsFrom(rawResultElements) + } + + val copyCategories by tasks.registering(CopyCategories::class) { + description = "Copies categories.json to allure-results folders" + destinationDirs.set(allureRawResultDirs) + } + + copyCategoriesElements.outgoing.artifact(copyCategories.flatMap { it.markerFile }) { + builtBy(copyCategories) + } + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherExtension.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherExtension.kt new file mode 100644 index 0000000..f4f4ac6 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherExtension.kt @@ -0,0 +1,208 @@ +package io.qameta.allure.gradle.gather + +import groovy.json.JsonOutput +import io.qameta.allure.gradle.base.tasks.ConditionalArgumentProvider +import io.qameta.allure.gradle.base.tasks.JavaAgentArgumentProvider +import io.qameta.allure.gradle.gather.config.* +import io.qameta.allure.gradle.util.conv +import io.qameta.allure.gradle.util.forUseAtConfigurationTimeBackport +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskCollection +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.* +import org.gradle.process.JavaForkOptions +import java.io.File +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Paths +import javax.inject.Inject + +/** + * Configures Allure raw result adapters (e.g. [junit5], [testng], ...) as `allure { gather {...` extension. + */ +open class AllureGatherExtension @Inject constructor( + private val project: Project, + private val objects: ObjectFactory +) { + companion object { + const val NAME = "gather" + const val EXECUTOR_FILE_NAME = "executor.json" + } + + /** + * `allure-java` version (adapters for test engines) + */ + val allureJavaVersion: Property = objects.property().conv("2.13.9") + .forUseAtConfigurationTimeBackport() + val aspectjVersion: Property = objects.property().conv("1.9.5") + + + /** + * Automatically add the relevant test engine adapters + */ + val autoconfigure: Property = objects.property().conv(true) + .forUseAtConfigurationTimeBackport() + + /** + * Configure default listeners by default (e.g. JUnit5, TestNG). + * This should be disabled if the project uses custom listeners + */ + val autoconfigureListeners: Property = objects.property().conv(autoconfigure) + + /** + * Automatically add AspectJ waver + */ + val aspectjWeaver = objects.property().conv(autoconfigure) + .forUseAtConfigurationTimeBackport() + + /** + * Path to `categories.json` file for Allure. + * The default path is `test/resources/**/categories.json`. + */ + val categoriesFile: Property = objects.fileProperty().conv(defaultCategoriesFile(project)) + + val adapters = AdapterHandler(project.container { + objects.newInstance(it, objects, this).also { adapter -> + AllureJavaAdapter.find(it)?.apply { + config.execute(adapter) + } + } + }) + + fun adapters(action: Action) { + // Custom type is for better Kotlin DSL + action.execute(adapters) + } + + fun addAspectjTo(tasks: TaskCollection) = tasks.configureEach { + addAspectjTo(this) + } + + fun addAspectjTo(task: TaskProvider) = task.configure { + addAspectjTo(this) + } + + fun addAspectjTo(task: Task): Unit = task.run { + if (aspectjWeaver.get() && this is JavaForkOptions) { + val aspectJAgent = project.configurations[AllureGatherPlugin.ASPECTJ_WEAVER_CONFIGURATION] + jvmArgumentProviders.add(JavaAgentArgumentProvider(aspectJAgent)) + } + } + + fun gatherResultsFrom(tasks: TaskCollection) { + project.apply() + tasks.configureEach { + internalGatherResultsFrom(this) + } + } + + fun gatherResultsFrom(task: TaskProvider) { + project.apply() + task { + internalGatherResultsFrom(this) + } + } + + fun gatherResultsFrom(task: Task) { + project.apply() + internalGatherResultsFrom(task) + } + + // TODO: move to [AllureGatherBasePlugin] like `allure { gatherResults { fromTask(..) } } + private fun internalGatherResultsFrom(task: Task) { + task.run { + // Each task should store results in its own folder + // End user should not depend on the folder name, so we do not expose it + val rawResults = project.layout.buildDirectory.dir("allure-results/${task.name}").get().asFile + // See https://github.com/allure-framework/allure2/issues/1236 + // We exclude categories.json since report task would copy categories right to the folder + // of the current task + outputs.files(project.fileTree(rawResults).matching { exclude("categories.json") }) + + // Pass the path to the task + if (this is JavaForkOptions) { + systemProperty(AllureGatherPlugin.ALLURE_DIR_PROPERTY, rawResults.absolutePath) + // We don't know if the task will execute JUnit5 engine or not, + // so we add extensions.autodetection.enabled to all the tasks if + // junit5.autoconfigureListeners is enabled + jvmArgumentProviders += ConditionalArgumentProvider( + project.provider { + adapters.configuredAdapters[AllureJavaAdapter.junit5]?.let { + listOf( + if (it.autoconfigureListeners.get()) { + "-Djunit.jupiter.extensions.autodetection.enabled=true" + } else { + "-Dskipped.junit.jupiter.extensions.autodetection.enabled=true" + } + ) + } ?: emptyList() + } + ) + } + + doFirst { + rawResults.mkdirs() + // TODO: remove dependence on project at the execution time for compatibility with configuration cache + generateExecutorInfo(rawResults, project, task.name) + } + + // Expose the gathered raw results + val allureResults = + project.configurations[AllureGatherBasePlugin.ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME] + allureResults.outgoing.artifact(rawResults) { + builtBy(task) + } + } + } + + private fun generateExecutorInfo(resultsDir: File, project: Project, taskName: String) { + val executorInfo = mapOf( + "name" to "Gradle", + "type" to "gradle", + "taskName" to taskName, + "buildName" to project.name, + "projectPath" to project.path, + "projectVersion" to project.version + ) + val resultsPath = Paths.get(resultsDir.absoluteFile.path) + Files.createDirectories(resultsPath) + val executorPath = resultsPath.resolve(EXECUTOR_FILE_NAME) + Files.write(executorPath, JsonOutput.toJson(executorInfo).toByteArray(StandardCharsets.UTF_8)) + } + + private fun defaultCategoriesFile(project: Project): Provider { + val categoriesInResources = categoriesInResources(project) + + val sourceSets = project.findProperty("sourceSets") as? SourceSetContainer + + val categoriesProvider = if (sourceSets == null) { + // SourceSets are missing, so will try regular test/resources path + categoriesInResources + } else { + // SourceSets detected, will try test sourceset first + project.provider { + val test = sourceSets.findByName(SourceSet.TEST_SOURCE_SET_NAME) ?: return@provider null + val file = test.resources.matching { include("**/categories.json") }.firstOrNull() + file ?: categoriesInResources.orNull + } + } + return project.layout.file(categoriesProvider) + } + + private fun categoriesInResources(project: Project): Provider { + val tree = project.fileTree(project.layout.projectDirectory.dir("test/resources")) { + matching { include("**/categories.json") } + } + return project.provider { + tree.firstOrNull() + } + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherPlugin.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherPlugin.kt new file mode 100644 index 0000000..1cd799e --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/AllureGatherPlugin.kt @@ -0,0 +1,138 @@ +package io.qameta.allure.gradle.gather + +import gather +import io.qameta.allure.gradle.base.AllureBasePlugin +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.gather.autoconfigure.BaseTrimMetaInfServices +import io.qameta.allure.gradle.gather.autoconfigure.TrimMetaInfServices53 +import io.qameta.allure.gradle.gather.autoconfigure.TrimMetaInfServices54 +import io.qameta.allure.gradle.gather.config.AdapterHandler +import io.qameta.allure.gradle.gather.config.AllureJavaAdapter +import io.qameta.allure.gradle.util.categoryLibrary +import io.qameta.allure.gradle.util.libraryElementsJar +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.Usage +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.* +import org.gradle.util.GradleVersion + +/** + * The plugin instruments [Test] and [JavaExec] tasks so they gather data for Allure. + * The data is gathered into [AllureGatherBasePlugin.ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME] configuration. + */ +open class AllureGatherPlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-gather" + const val ASPECTJ_WEAVER_CONFIGURATION = "allureAspectjWeaverAgent" + const val ALLURE_DIR_PROPERTY = "allure.results.directory" + } + + override fun apply(target: Project): Unit = target.run { + apply() + apply() + + val allureExtension = the() + val gatherExtension = allureExtension.gather + + // Configuration for AspectJ agent + configurations.create(ASPECTJ_WEAVER_CONFIGURATION) { + description = "Classpath for AspectJ to be used for Allure waver" + isCanBeResolved = true + isCanBeConsumed = false + defaultDependencies { + add(project.dependencies.create("org.aspectj:aspectjweaver:${gatherExtension.aspectjVersion.get()}")) + } + attributes { + categoryLibrary(objects) + libraryElementsJar(objects) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + } + } + + autoconfigureDependencyRules(gatherExtension) + configureTestTasks(gatherExtension) + val artifactType = Attribute.of("artifactType", String::class.java) + + afterEvaluate { + // We don't know if the user updates autoconfigure value, so we delay the decision till afterEvaluate + gatherExtension.takeIf { it.adapters.configuredAdapters.isEmpty() }?.run { + for (adapter in AllureJavaAdapter.values()) { + adapters.create(adapter.name) + } + } + + if (GradleVersion.current() >= GradleVersion.version("6.6")) { + configureSpiOffSubstitution(gatherExtension.adapters) + } else if (GradleVersion.current() >= GradleVersion.version("5.3")) { + // Older Gradle do not have "substitute with classifier" feature + // so we use ArtifactTransformation to trim META-INF/services from the jar + dependencies { + val transformClass = if (GradleVersion.current() >= GradleVersion.version("5.4")) { + TrimMetaInfServices54::class + } else { + TrimMetaInfServices53::class + } + registerTransform(transformClass) { + from.attribute(artifactType, "jar") + to.attribute(artifactType, BaseTrimMetaInfServices.NO_SPI_JAR) + } + } + } + } + } + + private fun Project.configureSpiOffSubstitution(adapters: AdapterHandler) { + // Substitute adapter with spi-off classifier when trimServicesFromJar == true + adapters.matching { it.trimServicesFromJar.get() } + .all { + val adapterConfig = this + configurations.all { + resolutionStrategy { + dependencySubstitution { + eachDependency { + if (requested.group == "io.qameta.allure" + && requested.name == adapterConfig.module + ) { + artifactSelection { + selectArtifact("jar", null, "spi-off") + } + } + } + } + } + } + } + } + + private fun Project.autoconfigureDependencyRules(extension: AllureGatherExtension) { + // We need to initialize all the adapters, so we don't need lazy evaluation configureEach + extension.adapters.all { + activateOn.all { + val rule = this + dependencies { + components { + rule.configure(this) + } + } + } + } + } + + private fun Project.configureTestTasks(extension: AllureGatherExtension) { + extension.run { + tasks.withType().let { + gatherResultsFrom(it) + addAspectjTo(it) + } + tasks.withType() + .matching { task -> task.name == "junitPlatformTest" } + .let { + gatherResultsFrom(it) + addAspectjTo(it) + } + } + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRuleBuilder.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRuleBuilder.kt new file mode 100644 index 0000000..bf2d357 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRuleBuilder.kt @@ -0,0 +1,45 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.Action +import org.gradle.api.artifacts.DependencyMetadata +import org.gradle.api.artifacts.ModuleVersionIdentifier +import org.gradle.api.specs.Spec + +interface AutoconfigureRuleBuilder { + /** + * By default the rule would apply to all modules specified in [triggerDependency]. + * This method sets a filter to distinguish adapters like `cucumber2-jvm` and `cucumber3-jvm`. + * Note: each call overwrites the filter. + */ + fun matching(predicate: Spec) + + /** + * Adds a dependency to the Maven's `compile` and `runtime` scopes. + */ + fun compileAndRuntime(dependencyNotation: Any) + + /** + * Adds a dependency to the Maven's `compile` and `runtime` scopes. + */ + fun compileAndRuntime(dependencyNotation: Any, configureAction: Action>?) + + /** + * Adds a dependency to the Maven's `compile` scope only. + */ + fun compileOnly(dependencyNotation: Any) + + /** + * Adds a dependency to the Maven's `compile` scope only. + */ + fun compileOnly(dependencyNotation: Any, configureAction: Action>?) + + /** + * Adds a dependency to the Maven's `runtime` scope only. + */ + fun runtimeOnly(dependencyNotation: Any) + + /** + * Adds a dependency to the Maven's `runtime` scope only. + */ + fun runtimeOnly(dependencyNotation: Any, configureAction: Action>? = null) +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRules.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRules.kt new file mode 100644 index 0000000..ed008d7 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/AutoconfigureRules.kt @@ -0,0 +1,45 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.artifacts.ModuleVersionIdentifier +import org.gradle.api.artifacts.dsl.ComponentMetadataHandler +import org.gradle.api.specs.Spec +import org.slf4j.LoggerFactory + +interface AutoconfigureRule { + fun configure(context: ComponentMetadataHandler) +} + +class SimpleRule( + val triggerDependency: String, + private val predicate: Spec?, + private val extraDependencies: Map> +) : AutoconfigureRule { + companion object { + private val logger = LoggerFactory.getLogger(SimpleRule::class.java) + } + + override fun configure(context: ComponentMetadataHandler) { + logger.debug("Configuring Allure autoconfigure rule for {}", triggerDependency) + context.withModule(triggerDependency) { + logger.debug("allure-gradle: detected {}", triggerDependency) + if (predicate?.isSatisfiedBy(id) == false) { + return@withModule + } + for ((variantName, deps) in extraDependencies) { + withVariant(variantName) { + withDependencies { + for (dependency in deps) { + val addedId = dependency.addTo(this) + logger.info( + "allure-gradle: added dependency {} to {} scope of {}", + addedId, + variantName, + id + ) + } + } + } + } + } + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/BaseTrimMetaInfServices.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/BaseTrimMetaInfServices.kt new file mode 100644 index 0000000..0905945 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/BaseTrimMetaInfServices.kt @@ -0,0 +1,39 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.artifacts.transform.TransformAction +import org.gradle.api.artifacts.transform.TransformOutputs +import org.gradle.api.artifacts.transform.TransformParameters +import org.gradle.api.attributes.Attribute +import java.io.File +import java.util.jar.JarFile +import java.util.jar.JarOutputStream + +/** + * Removes `META-INF/services` folder from a jar. + * It enables to automatically transform `allure-junit5` to `allure-junit5:spi-off`. + */ +abstract class BaseTrimMetaInfServices : TransformAction { + companion object { + val ARTIFACT_TYPE_ATTRIBUTE = Attribute.of("artifactType", String::class.java) + const val NO_SPI_JAR = "jar-no-spi" + } + + protected fun doTransform(inputFile: File, outputs: TransformOutputs) { + val outputFile = outputs.file(inputFile.name.removeSuffix(".jar") + "-spi-off.jar") + + outputFile.outputStream().buffered().use { outStream -> + JarOutputStream(outStream).use { outFile -> + JarFile(inputFile).use { inputJar -> + inputJar.stream() + .filter { !it.name.startsWith("META-INF/services") } + .forEachOrdered { + val entryStream = inputJar.getInputStream(it) + outFile.putNextEntry(it) + entryStream.copyTo(outFile) + outFile.closeEntry() + } + } + } + } + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DefaultAutoconfigureRuleBuilder.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DefaultAutoconfigureRuleBuilder.kt new file mode 100644 index 0000000..308f797 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DefaultAutoconfigureRuleBuilder.kt @@ -0,0 +1,50 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.Action +import org.gradle.api.artifacts.DependencyMetadata +import org.gradle.api.artifacts.ModuleVersionIdentifier +import org.gradle.api.provider.Provider +import org.gradle.api.specs.Spec + +class DefaultAutoconfigureRuleBuilder( + val triggerDependency: String, + val enabled: Provider +) : AutoconfigureRuleBuilder { + private val deps = mutableMapOf>() + private var predicate: Spec? = null + + override fun matching(predicate: Spec) { + this.predicate = predicate + } + + override fun compileAndRuntime(dependencyNotation: Any) = compileAndRuntime(dependencyNotation, null) + + override fun compileAndRuntime(dependencyNotation: Any, configureAction: Action>?) { + compileOnly(dependencyNotation, configureAction) + runtimeOnly(dependencyNotation, configureAction) + } + + override fun compileOnly(dependencyNotation: Any) = compileOnly(dependencyNotation, null) + + override fun compileOnly(dependencyNotation: Any, configureAction: Action>?) { + deps.getOrPut("compile") { mutableListOf() } += + DependencyDeclaration(dependencyNotation, configureAction) + } + + override fun runtimeOnly(dependencyNotation: Any) = runtimeOnly(dependencyNotation, null) + + override fun runtimeOnly(dependencyNotation: Any, configureAction: Action>?) { + deps.getOrPut("runtime") { mutableListOf() } += + DependencyDeclaration(dependencyNotation, configureAction) + } + + fun build(): AutoconfigureRule = SimpleRule( + triggerDependency, + { + enabled.get() && predicate?.isSatisfiedBy(it) != false + }, + deps.ifEmpty { + throw IllegalStateException("Please add at least one dependency via .compile(..) or .runtime(..) method") + } + ) +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DependencyDeclaration.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DependencyDeclaration.kt new file mode 100644 index 0000000..b67cbaf --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/DependencyDeclaration.kt @@ -0,0 +1,50 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.Action +import org.gradle.api.artifacts.DependenciesMetadata +import org.gradle.api.artifacts.DependencyMetadata +import org.gradle.api.provider.Provider +import org.slf4j.LoggerFactory + +class DependencyDeclaration( + val id: Any, + private val configureAction: Action>? = null +) { + companion object { + private val logger = LoggerFactory.getLogger(DependencyDeclaration::class.java) + } + + init { + verifyDependency(id) + } + + private fun Any.unwrapProviders(): Any = when (this) { + is Provider<*> -> get().unwrapProviders() + else -> this + } + + fun addTo(deps: DependenciesMetadata<*>): String = when (val actualId = id.unwrapProviders()) { + is Map<*, *> -> { + @Suppress("unchecked_cast") + val map = actualId as Map + logger.info("Allure: adding {}") + if (configureAction == null) deps.add(map) else deps.add(map, configureAction) + map.toString() + } + else -> { + val dep = actualId.toString() + if (configureAction == null) deps.add(dep) else deps.add(dep, configureAction) + dep + } + } + + private fun verifyDependency(dependencyNotation: Any) { + when (dependencyNotation) { + is String, is Provider<*>, is Map<*, *> -> return + } + throw IllegalArgumentException( + "Please use Provider or String for dependency notation. " + + "Input object $dependencyNotation is ${dependencyNotation::class.java}" + ) + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices53.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices53.kt new file mode 100644 index 0000000..fd5fa23 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices53.kt @@ -0,0 +1,20 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.artifacts.transform.CacheableTransform +import org.gradle.api.artifacts.transform.InputArtifact +import org.gradle.api.artifacts.transform.TransformOutputs +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import java.io.File + +@CacheableTransform +abstract class TrimMetaInfServices53 : BaseTrimMetaInfServices() { + @get:PathSensitive(PathSensitivity.NONE) + @get:InputArtifact + // Gradle 5.3 supports File inputs only + abstract val inputArtifact: File + + override fun transform(outputs: TransformOutputs) { + doTransform(inputArtifact, outputs) + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices54.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices54.kt new file mode 100644 index 0000000..f9c8e99 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/autoconfigure/TrimMetaInfServices54.kt @@ -0,0 +1,21 @@ +package io.qameta.allure.gradle.gather.autoconfigure + +import org.gradle.api.artifacts.transform.CacheableTransform +import org.gradle.api.artifacts.transform.InputArtifact +import org.gradle.api.artifacts.transform.TransformOutputs +import org.gradle.api.file.FileSystemLocation +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity + +@CacheableTransform +abstract class TrimMetaInfServices54 : BaseTrimMetaInfServices() { + @get:PathSensitive(PathSensitivity.NONE) + @get:InputArtifact + // Provider is Gradle 5.4+ + abstract val inputFile: Provider + + override fun transform(outputs: TransformOutputs) { + doTransform(inputFile.get().asFile, outputs) + } +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterConfig.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterConfig.kt new file mode 100644 index 0000000..58d52b4 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterConfig.kt @@ -0,0 +1,108 @@ +package io.qameta.allure.gradle.gather.config + +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.gather.AllureGatherExtension +import io.qameta.allure.gradle.gather.autoconfigure.AutoconfigureRule +import io.qameta.allure.gradle.gather.autoconfigure.AutoconfigureRuleBuilder +import io.qameta.allure.gradle.gather.autoconfigure.DefaultAutoconfigureRuleBuilder +import io.qameta.allure.gradle.util.conv +import io.qameta.allure.gradle.util.domainObjectSetBackport +import io.qameta.allure.gradle.util.forUseAtConfigurationTimeBackport +import org.gradle.api.Action +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.property +import javax.inject.Inject + +open class AdapterConfig @Inject constructor( + val name: String, + objects: ObjectFactory, + allureGatherExtension: AllureGatherExtension +) { + /** + * Configures `allure-java` version for the current adapter. + * The value defaults to [AllureExtension.allureJavaVersion] + */ + val adapterVersion = objects.property() + .conv(allureGatherExtension.allureJavaVersion) + + @Deprecated( + level = DeprecationLevel.WARNING, + message = "Use adapterVersion", + replaceWith = ReplaceWith("adapterVersion") + ) + var version: String + get() = adapterVersion.get() + set(value) = adapterVersion.set(value) + + /** + * By default, the adapter is enabled. This property allows deactivating the adapter. + */ + val enabled = objects.property().conv(true) + + val autoconfigureListeners = objects.property() + .forUseAtConfigurationTimeBackport() + .conv( + enabled.map { it && allureGatherExtension.autoconfigureListeners.get() } + ) + + @Deprecated( + level = DeprecationLevel.WARNING, message = "Use autoconfigureListeners", + replaceWith = ReplaceWith("autoconfigureListeners") + ) + var spiOff: Boolean + get() = !autoconfigureListeners.get() + set(value) = autoconfigureListeners.set(!value) + + /** + * Autoconfigure listeners is available only for the subset of adapters only (e.g [AdapterHandlerScope.testng], + * [AdapterHandlerScope.junit5]) + */ + val supportsAutoconfigureListeners = objects.property().conv(false) + .forUseAtConfigurationTimeBackport() + + /** + * Returns `true` if `META-INF/services` should be removed from the dependency. + */ + internal val trimServicesFromJar = + supportsAutoconfigureListeners.map { it && !autoconfigureListeners.get() } + + internal val activateOn = objects.domainObjectSetBackport() + + /** + * Adds a basic autoconfigure rule: add [adapterDependency] to `compile` and `runtime` classpath + * if [dependency] is detected. + */ + fun activateOn(dependency: String) { + activateOn(dependency) { + compileOnly(adapterDependency) + runtimeOnly(adapterDependency) + } + } + + /** + * Adds an autoconfigure rule that triggers when [dependency] is detected. + * Note: you need to add at least one dependency via [AutoconfigureRuleBuilder.compileOnly] + * or [AutoconfigureRuleBuilder.runtimeOnly] methods. + */ + fun activateOn(dependency: String, configureAction: Action) { + activateOn.add( + DefaultAutoconfigureRuleBuilder(dependency, enabled).apply { + configureAction.execute(this) + }.build() + ) + } + + /** + * Dependency coordinates for the adapter (e.g. `io.qameta.allure:allure-junit5:2.8.0`) + */ + val adapterDependency = adapterVersion.map { "io.qameta.allure:$module:$it" } + + internal val module get() = "allure-$adapterModule" + + /** + * Name of the artifact (e.g. `allure-junit5`) + */ + val adapterModule get() = AllureJavaAdapter.find(name)?.adapterName ?: name + + override fun toString() = "AdapterConfig{$name}" +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterHandler.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterHandler.kt new file mode 100644 index 0000000..657bd84 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AdapterHandler.kt @@ -0,0 +1,59 @@ +package io.qameta.allure.gradle.gather.config + +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectContainer +import javax.inject.Inject +import kotlin.reflect.KProperty + +open class AdapterHandler @Inject constructor( + private val data: NamedDomainObjectContainer +) : NamedDomainObjectContainer by data { + internal val configuredAdapters = mutableMapOf() + + val junit4 by lazyCreating + val junit5 by lazyCreating + val testng by lazyCreating + val spock by lazyCreating + val cucumberJvm by lazyCreating + val cucumber2Jvm by lazyCreating + val cucumber3Jvm by lazyCreating + val cucumber4Jvm by lazyCreating + val cucumber5Jvm by lazyCreating + val cucumber6Jvm by lazyCreating + + fun cucumberJvm(majorVersion: Int) = maybeCreate( + if (majorVersion == 1) "cucumberJvm" else "cucumber${majorVersion}Jvm" + ) + + operator fun AdapterConfig.invoke(configureAction: Action) { + configureAction.execute(this) + } + + init { + whenObjectAdded { + val newConfig = this + val adapter = AllureJavaAdapter.find(name) ?: return@whenObjectAdded + configuredAdapters[adapter] = newConfig + } + whenObjectRemoved { + val adapter = AllureJavaAdapter.find(name) ?: return@whenObjectRemoved + configuredAdapters.remove(adapter) + } + } + +} + +// Retrieves an element from AdapterHandler lazily using property name +private val AdapterHandler.lazyCreating get() = LazyCreating(this) + +private class LazyCreating(val container: AdapterHandler) + +private operator fun LazyCreating.provideDelegate( + receiver: Any?, + property: KProperty<*> +): Lazy = lazy { + // We return non-lazy object since it makes DSL simpler to use (less .get() calls) + container.maybeCreate(property.name) + // Here's maybeRegister alternative: + // if (name in container.names) container.named(name) else container.register(name) +} diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AllureJavaAdapter.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AllureJavaAdapter.kt new file mode 100644 index 0000000..cdc8c28 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/config/AllureJavaAdapter.kt @@ -0,0 +1,100 @@ +package io.qameta.allure.gradle.gather.config + +import io.qameta.allure.gradle.gather.autoconfigure.AutoconfigureRuleBuilder +import io.qameta.allure.gradle.gather.autoconfigure.BaseTrimMetaInfServices +import org.gradle.api.Action +import org.gradle.api.artifacts.DependencyMetadata +import org.gradle.api.provider.Provider +import org.gradle.util.GradleVersion + +internal enum class AllureJavaAdapter( + val adapterName: String, + val config: Action +) { + junit4("junit4", { + activateOn("junit:junit") { + compileAndRuntime(adapterDependency) + runtimeOnly(adapterVersion.map { "io.qameta.allure:allure-junit4-aspect:$it" }) + } + }), + junit5("junit5", { + supportsAutoconfigureListeners.set(true) + activateOn("org.junit.jupiter:junit-jupiter-api") { + compileAndRuntimeWithServices(adapterDependency, trimServicesFromJar) + } + }), + testng("testng", { + supportsAutoconfigureListeners.set(true) + activateOn("org.testng:testng") { + compileAndRuntimeWithServices(adapterDependency, trimServicesFromJar) + } + }), + spock("spock", { + activateOn("org.spockframework:spock-core") + }), + cucumberJvm("cucumber-jvm", cucumberJvm(1)), + cucumber2Jvm("cucumber2-jvm", cucumberJvm(2)), + cucumber3Jvm("cucumber3-jvm", cucumberJvm(3)), + cucumber4Jvm("cucumber4-jvm", cucumberJvm(4)), + cucumber5Jvm("cucumber5-jvm", cucumberJvm(5)), + cucumber6Jvm("cucumber6-jvm", cucumberJvm(6)), + ; + + companion object { + private val adapters = values().associateBy { it.adapterName } + private val values = values().associateBy { it.name } + + fun find(name: String) = values[name] ?: adapters[name]?.let { + // We don't want to have both cucumberJvm and cucumber-jvm used at the same time + // so we allow only cucumberJvm + throw IllegalStateException("Please use ${it.name} name for adapter instead of $name") + } + + private fun AutoconfigureRuleBuilder.compileAndRuntimeWithServices( + dep: Provider, + trimServices: Provider + ) { + // Gradle does not provide a way to add a dependency with capability via metadata rule + // So we use "substitute with classifier" (6.6+) or artifact transformation (5.3+) workarounds + // https://github.com/gradle/gradle/issues/17035 + compileAndRuntime(dep) { + val gradleVersion = GradleVersion.current() + if (gradleVersion < GradleVersion.version("6.6") && trimServices.get()) { + if (gradleVersion < GradleVersion.version("5.3")) { + throw IllegalStateException( + "Autoconfiguration for $name with autoconfigureListeners=false" + + " requires Gradle 5.3+. Please upgrade Gradle to 5.3+ or add ${dep.get()}:spi-off " + + "to the relevant configurations (e.g. testImplementation) manually and " + + "turn off autoconfiguration with $name { enabled.set(false) }" + ) + } + jarWithoutMetainfServices() + } + } + } + + private fun DependencyMetadata<*>.jarWithoutMetainfServices() { + attributes { + attribute( + BaseTrimMetaInfServices.ARTIFACT_TYPE_ATTRIBUTE, + BaseTrimMetaInfServices.NO_SPI_JAR + ) + } + } + } +} + + +private fun cucumberJvm(majorVersion: Int): Action = + Action { + if (majorVersion == 1) { + activateOn("info.cukes:cucumber-junit") + } else { + activateOn("io.cucumber:cucumber-core") { + matching { + it.version.startsWith("$majorVersion.") + } + compileAndRuntime(adapterDependency) + } + } + } diff --git a/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/tasks/CopyCategories.kt b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/tasks/CopyCategories.kt new file mode 100644 index 0000000..5b71376 --- /dev/null +++ b/allure-gather-plugin/src/main/kotlin/io/qameta/allure/gradle/gather/tasks/CopyCategories.kt @@ -0,0 +1,52 @@ +package io.qameta.allure.gradle.gather.tasks + +import gather +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.util.conv +import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.setProperty +import org.gradle.kotlin.dsl.the +import java.io.File +import javax.inject.Inject + +open class CopyCategories @Inject constructor(objects: ObjectFactory) : DefaultTask() { + @Optional + @InputFile + @SkipWhenEmpty + @PathSensitive(PathSensitivity.NONE) + val categoriesFile = objects.fileProperty() + .conv(project.the().gather.categoriesFile) + + @Internal + val destinationDirs = objects.setProperty() + + @OutputFiles + protected val outputFiles = destinationDirs.map { set -> + set.map { it.resolve("categories.json") } + } + + @OutputFile + val markerFile = objects.directoryProperty() + .conv(project.layout.buildDirectory.dir("copy-categories/$name")) + + @TaskAction + fun run() { + val categories = categoriesFile.get().asFile + var didWork = false + for (dir in destinationDirs.get()) { + logger.warn("Copying $categories to $dir") + didWork = didWork or project.copy { + into(dir) + from(categories) { + rename { + "categories.json" + } + } + }.didWork + } + didWork = didWork or markerFile.get().asFile.mkdirs() + this.didWork = didWork + } +} diff --git a/allure-gather-plugin/src/test/kotlin/io/qameta/allure/gradle/gather/AdaptersTest.kt b/allure-gather-plugin/src/test/kotlin/io/qameta/allure/gradle/gather/AdaptersTest.kt new file mode 100644 index 0000000..d3409a6 --- /dev/null +++ b/allure-gather-plugin/src/test/kotlin/io/qameta/allure/gradle/gather/AdaptersTest.kt @@ -0,0 +1,68 @@ +package io.qameta.allure.gradle.gather + +import io.qameta.allure.gradle.rule.GradleRunnerRule +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class AdaptersTest { + @Rule + @JvmField + val gradleRunner = GradleRunnerRule() + .version { version } + .project { project } + .tasks { tasks } + + + @Parameterized.Parameter(0) + lateinit var version: String + + @Parameterized.Parameter(1) + lateinit var project: String + + @Parameterized.Parameter(2) + lateinit var tasks: Array + + @Parameterized.Parameter(3) + lateinit var expected: String + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{1} [{0}]") + fun getFrameworks() = listOf( + arrayOf( + "7.0", + "src/it/adapter-junit5-spock-kts", + arrayOf("printAdapters"), + "[AdapterConfig{junit5}, AdapterConfig{spock}]" + ), + arrayOf( + "5.0", + "src/it/adapter-junit5-spock-kts", + arrayOf("printAdapters"), + "[AdapterConfig{junit5}, AdapterConfig{spock}]" + ), + arrayOf( + "7.0", + "src/it/adapter-all", + arrayOf("printAdapters"), + "[AdapterConfig{cucumber2Jvm}, AdapterConfig{cucumber3Jvm}, AdapterConfig{cucumber4Jvm}, AdapterConfig{cucumber5Jvm}, AdapterConfig{cucumber6Jvm}, AdapterConfig{cucumberJvm}, AdapterConfig{junit4}, AdapterConfig{junit5}, AdapterConfig{spock}, AdapterConfig{testng}]" + ), + arrayOf( + "5.0", + "src/it/adapter-all", + arrayOf("printAdapters"), + "[AdapterConfig{cucumber2Jvm}, AdapterConfig{cucumber3Jvm}, AdapterConfig{cucumber4Jvm}, AdapterConfig{cucumber5Jvm}, AdapterConfig{cucumber6Jvm}, AdapterConfig{cucumberJvm}, AdapterConfig{junit4}, AdapterConfig{junit5}, AdapterConfig{spock}, AdapterConfig{testng}]" + ) + ) + } + + @Test + fun `list of configured adapters changes on explicit adapter configuration`() { + assertThat(gradleRunner.projectDir.resolve("build/printAdapters.txt")) + .hasContent(expected) + } +} diff --git a/allure-plugin/build.gradle.kts b/allure-plugin/build.gradle.kts new file mode 100644 index 0000000..c60e91f --- /dev/null +++ b/allure-plugin/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("allure-gradle.kotlin-dsl-published-plugin") +} + +group = "io.qameta.allure.gradle.allure" + +dependencies { + implementation(project(":allure-gather-plugin")) + implementation(project(":allure-report-plugin")) + + testImplementation(project(":testkit-junit4")) + testImplementation("org.assertj:assertj-core:_") +} + +tasks.test { + // Treat test task out-of-date if src/it changes + inputs.dir(layout.projectDirectory.dir("src/it")).optional() +} + +gradlePlugin { + plugins { + create("allurePlugin") { + id = "io.qameta.allure" + implementationClass = "io.qameta.allure.gradle.allure.AllurePlugin" + } + } +} diff --git a/allure-plugin/src/it/categories/build.gradle b/allure-plugin/src/it/categories/build.gradle new file mode 100644 index 0000000..996b6f8 --- /dev/null +++ b/allure-plugin/src/it/categories/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'java' + id 'io.qameta.allure' +} + +dependencies { + allureRawResultElements(files("$buildDir/allure-results")) +} + +repositories { + mavenCentral() +} + +allure { +} diff --git a/src/it/categories/build/allure-results/simple-result.json b/allure-plugin/src/it/categories/build/allure-results/simple-result.json similarity index 100% rename from src/it/categories/build/allure-results/simple-result.json rename to allure-plugin/src/it/categories/build/allure-results/simple-result.json diff --git a/src/it/categories/src/test/resources/categories.json b/allure-plugin/src/it/categories/src/test/resources/categories.json similarity index 100% rename from src/it/categories/src/test/resources/categories.json rename to allure-plugin/src/it/categories/src/test/resources/categories.json diff --git a/allure-plugin/src/it/cucumber-jvm/build.gradle b/allure-plugin/src/it/cucumber-jvm/build.gradle new file mode 100644 index 0000000..0b5d195 --- /dev/null +++ b/allure-plugin/src/it/cucumber-jvm/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'java' + id 'io.qameta.allure' +} + +allure { + aspectjweaver = true + + useCucumberJVM { + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'commons-io:commons-io:2.5' + implementation 'info.cukes:gherkin:2.12.2' + implementation 'info.cukes:cucumber-core:1.2.5' + implementation 'info.cukes:cucumber-java:1.2.5' + implementation 'info.cukes:cucumber-junit:1.2.5' + + testImplementation 'junit:junit:4.12' +} + +test { + useJUnit() +} diff --git a/src/it/cucumber-jvm/src/test/java/tests/CucumberJvmTest.java b/allure-plugin/src/it/cucumber-jvm/src/test/java/tests/CucumberJvmTest.java similarity index 100% rename from src/it/cucumber-jvm/src/test/java/tests/CucumberJvmTest.java rename to allure-plugin/src/it/cucumber-jvm/src/test/java/tests/CucumberJvmTest.java diff --git a/src/it/cucumber-jvm/src/test/java/tests/Steps.java b/allure-plugin/src/it/cucumber-jvm/src/test/java/tests/Steps.java similarity index 100% rename from src/it/cucumber-jvm/src/test/java/tests/Steps.java rename to allure-plugin/src/it/cucumber-jvm/src/test/java/tests/Steps.java diff --git a/src/it/cucumber-jvm/src/test/resources/features/test.feature b/allure-plugin/src/it/cucumber-jvm/src/test/resources/features/test.feature similarity index 100% rename from src/it/cucumber-jvm/src/test/resources/features/test.feature rename to allure-plugin/src/it/cucumber-jvm/src/test/resources/features/test.feature diff --git a/allure-plugin/src/it/cucumber2-jvm/build.gradle b/allure-plugin/src/it/cucumber2-jvm/build.gradle new file mode 100644 index 0000000..9dc212d --- /dev/null +++ b/allure-plugin/src/it/cucumber2-jvm/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'java' + id 'io.qameta.allure' +} + +allure { + useCucumber2JVM { + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'commons-io:commons-io:2.5' + implementation 'info.cukes:gherkin:2.12.2' + implementation 'io.cucumber:cucumber-core:2.3.1' + implementation 'io.cucumber:cucumber-java:2.3.1' + implementation 'io.cucumber:cucumber-junit:2.3.1' + + testImplementation 'junit:junit:4.12' +} + +test { + useJUnit() +} diff --git a/src/it/cucumber2-jvm/src/test/java/tests/Cucumber2JvmTest.java b/allure-plugin/src/it/cucumber2-jvm/src/test/java/tests/Cucumber2JvmTest.java similarity index 100% rename from src/it/cucumber2-jvm/src/test/java/tests/Cucumber2JvmTest.java rename to allure-plugin/src/it/cucumber2-jvm/src/test/java/tests/Cucumber2JvmTest.java diff --git a/src/it/cucumber2-jvm/src/test/java/tests/Steps.java b/allure-plugin/src/it/cucumber2-jvm/src/test/java/tests/Steps.java similarity index 100% rename from src/it/cucumber2-jvm/src/test/java/tests/Steps.java rename to allure-plugin/src/it/cucumber2-jvm/src/test/java/tests/Steps.java diff --git a/src/it/cucumber2-jvm/src/test/resources/features/test.feature b/allure-plugin/src/it/cucumber2-jvm/src/test/resources/features/test.feature similarity index 100% rename from src/it/cucumber2-jvm/src/test/resources/features/test.feature rename to allure-plugin/src/it/cucumber2-jvm/src/test/resources/features/test.feature diff --git a/allure-plugin/src/it/full-dsl-groovy/build.gradle b/allure-plugin/src/it/full-dsl-groovy/build.gradle new file mode 100644 index 0000000..dc79f22 --- /dev/null +++ b/allure-plugin/src/it/full-dsl-groovy/build.gradle @@ -0,0 +1,58 @@ +plugins { + id("io.qameta.allure-gather") + id("io.qameta.allure-report") +} + +// The folowing tests different syntax variations to verify if they compile + +allure { + version = "42.0" + gather { + adapters { + junit5 { + adapterVersion = "42.0" + enabled = true + } + spock + testng.adapterVersion = "43" + testng.enabled = false + // cucumberJvm(3) is not supported in Groovy DSL syntax + cucumber3Jvm.supportsAutoconfigureListeners = true + cucumber5Jvm { + activateOn("com.example.custom.cucumber:cucumber-core") + } + cucumber6Jvm { + activateOn("com.example.custom.cucumber:cucumber-core") { + compileOnly("com.acme:compile-dep") + runtimeOnly("com.acme:runtime-dep") + } + } + } + adapters.junit5 + adapters.junit5.enabled = false + } + gather.adapters.spock.enabled = true + commandline { + group = "com.example" + module = "test" + extension = "jar" + downloadUrlPattern = "https://..." + } + commandline.group = "abcd" + report { + reportDir = layout.buildDirectory.dir("allure/reports") + dependsOnTests = true + dependsOnTests() + } + report.reportDir = file("$buildDir/allure/reports") + report.dependsOnTests = true +} +allure.gather.adapters.cucumberJvm.enabled = true +allure.commandline.downloadUrlPattern = "localhost" +allure.report.dependsOnTests = true + +tasks.register("testDsl") { + doLast { + println("groovy-dsl is ok") + } +} diff --git a/allure-plugin/src/it/full-dsl-kotlin/build.gradle.kts b/allure-plugin/src/it/full-dsl-kotlin/build.gradle.kts new file mode 100644 index 0000000..ecfc5bd --- /dev/null +++ b/allure-plugin/src/it/full-dsl-kotlin/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + id("io.qameta.allure-gather") + id("io.qameta.allure-report") +} + +// The folowing tests different syntax variations to verify if they compile + +allure { + version.set("42.0") + gather { + adapters { + junit5 { + adapterVersion.set("42.0") + enabled.set(true) + } + spock + testng.adapterVersion.set("43") + testng.enabled.set(false) + cucumberJvm(3).supportsAutoconfigureListeners.set(true) + cucumber5Jvm { + activateOn("com.example.custom.cucumber:cucumber-core") + } + cucumber6Jvm { + activateOn("com.example.custom.cucumber:cucumber-core") { + compileOnly("com.acme:compile-dep") + runtimeOnly("com.acme:runtime-dep") + } + } + } + adapters.junit5 + adapters.junit5.enabled.set(false) + } + gather.adapters.spock.enabled.set(true) + commandline { + group.set("com.example") + module.set("test") + extension.set("jar") + downloadUrlPattern.set("https://...") + } + commandline.group.set("abcd") + report { + reportDir.set(layout.buildDirectory.dir("allure/reports")) + dependsOnTests.set(true) + dependsOnTests() + } + report.reportDir.set(buildDir.resolve("allure/reports")) + report.dependsOnTests.set(true) +} +allure.gather.adapters.cucumberJvm.enabled.set(true) +allure.commandline.downloadUrlPattern.set("localhost") +allure.report.dependsOnTests.set(true) + +val testDsl by tasks.registering { + doLast { + println("kotlin-dsl is ok") + } +} diff --git a/src/it/junit4-autoconfigure/build.gradle b/allure-plugin/src/it/junit4-autoconfigure/build.gradle similarity index 59% rename from src/it/junit4-autoconfigure/build.gradle rename to allure-plugin/src/it/junit4-autoconfigure/build.gradle index 0d3b871..45903cf 100644 --- a/src/it/junit4-autoconfigure/build.gradle +++ b/allure-plugin/src/it/junit4-autoconfigure/build.gradle @@ -4,19 +4,17 @@ plugins { } allure { - version = '2.4.1' - autoconfigure = true + version = '2.8.0' } repositories { mavenCentral() - jcenter() } dependencies { - testCompile 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' } test { useJUnit() -} \ No newline at end of file +} diff --git a/src/it/junit4-autoconfigure/src/test/java/tests/Junit4Test.java b/allure-plugin/src/it/junit4-autoconfigure/src/test/java/tests/Junit4Test.java similarity index 100% rename from src/it/junit4-autoconfigure/src/test/java/tests/Junit4Test.java rename to allure-plugin/src/it/junit4-autoconfigure/src/test/java/tests/Junit4Test.java diff --git a/src/it/junit4-kotlin/build.gradle.kts b/allure-plugin/src/it/junit4-kotlin/build.gradle.kts similarity index 62% rename from src/it/junit4-kotlin/build.gradle.kts rename to allure-plugin/src/it/junit4-kotlin/build.gradle.kts index 45eb69d..17bdf7b 100644 --- a/src/it/junit4-kotlin/build.gradle.kts +++ b/allure-plugin/src/it/junit4-kotlin/build.gradle.kts @@ -4,8 +4,7 @@ plugins { } allure { - version = "2.8.1" - autoconfigure = true + version.set("2.8.1") useJUnit4 { version = "2.9.0" @@ -14,9 +13,8 @@ allure { repositories { mavenCentral() - jcenter() } dependencies { - testCompile("junit:junit:4.12") + testImplementation("junit:junit:4.12") } diff --git a/src/it/junit4-kotlin/src/test/java/tests/Junit4Test.java b/allure-plugin/src/it/junit4-kotlin/src/test/java/tests/Junit4Test.java similarity index 100% rename from src/it/junit4-kotlin/src/test/java/tests/Junit4Test.java rename to allure-plugin/src/it/junit4-kotlin/src/test/java/tests/Junit4Test.java diff --git a/src/it/junit4/build.gradle b/allure-plugin/src/it/junit4/build.gradle similarity index 75% rename from src/it/junit4/build.gradle rename to allure-plugin/src/it/junit4/build.gradle index 1ee9634..feb87f6 100644 --- a/src/it/junit4/build.gradle +++ b/allure-plugin/src/it/junit4/build.gradle @@ -4,7 +4,7 @@ plugins { } allure { - version = '2.4.1' + version = '2.8.0' aspectjweaver = true useJUnit4 { @@ -14,11 +14,10 @@ allure { repositories { mavenCentral() - jcenter() } dependencies { - testCompile 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' } test { diff --git a/src/it/junit4/src/test/java/tests/Junit4Test.java b/allure-plugin/src/it/junit4/src/test/java/tests/Junit4Test.java similarity index 100% rename from src/it/junit4/src/test/java/tests/Junit4Test.java rename to allure-plugin/src/it/junit4/src/test/java/tests/Junit4Test.java diff --git a/allure-plugin/src/it/junit5/build.gradle b/allure-plugin/src/it/junit5/build.gradle new file mode 100644 index 0000000..e74e3b3 --- /dev/null +++ b/allure-plugin/src/it/junit5/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java' + id 'io.qameta.allure' +} + +repositories { + mavenCentral() +} + +test { + useJUnitPlatform() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.0' +} + +allure { + // Newer allure-java fail to gather data from JUnit5 :( + gather.allureJavaVersion = "2.13.5" +} diff --git a/src/it/junit5-native/src/test/java/tests/Junit5Test.java b/allure-plugin/src/it/junit5/src/test/java/tests/Junit5Test.java similarity index 100% rename from src/it/junit5-native/src/test/java/tests/Junit5Test.java rename to allure-plugin/src/it/junit5/src/test/java/tests/Junit5Test.java diff --git a/allure-plugin/src/it/report-multi/build.gradle b/allure-plugin/src/it/report-multi/build.gradle new file mode 100644 index 0000000..94abfe3 --- /dev/null +++ b/allure-plugin/src/it/report-multi/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'io.qameta.allure-aggregate-report' +} + +// By default, aggregate-report aggregates allprojects (current + subprojects) +// So we want to exclude module3 since it has no data for Allure +configurations.allureAggregateReport.dependencies.remove( + project.dependencies.create(project(":module3")) +) + +// Alternative option: +// configurations.allureAggregateReport.dependencies.clear() + +// Adding custom dependency +// dependencies { +// allureAggregateReport(project(":module3")) +// } + +// allure-aggregate-report requires allure-commandline, so we need a repository here +repositories { + mavenCentral() +} diff --git a/allure-plugin/src/it/report-multi/module1/build.gradle b/allure-plugin/src/it/report-multi/module1/build.gradle new file mode 100644 index 0000000..450db59 --- /dev/null +++ b/allure-plugin/src/it/report-multi/module1/build.gradle @@ -0,0 +1,7 @@ +plugins { + id 'io.qameta.allure-gather' +} + +dependencies { + allureRawResultElements(files("$buildDir/allure-results")) +} diff --git a/src/it/report-multi/module1/build/allure-results/first-result.json b/allure-plugin/src/it/report-multi/module1/build/allure-results/first-result.json similarity index 100% rename from src/it/report-multi/module1/build/allure-results/first-result.json rename to allure-plugin/src/it/report-multi/module1/build/allure-results/first-result.json diff --git a/allure-plugin/src/it/report-multi/module2/build.gradle b/allure-plugin/src/it/report-multi/module2/build.gradle new file mode 100644 index 0000000..450db59 --- /dev/null +++ b/allure-plugin/src/it/report-multi/module2/build.gradle @@ -0,0 +1,7 @@ +plugins { + id 'io.qameta.allure-gather' +} + +dependencies { + allureRawResultElements(files("$buildDir/allure-results")) +} diff --git a/src/it/report-multi/module2/build/allure-results/second-result.json b/allure-plugin/src/it/report-multi/module2/build/allure-results/second-result.json similarity index 100% rename from src/it/report-multi/module2/build/allure-results/second-result.json rename to allure-plugin/src/it/report-multi/module2/build/allure-results/second-result.json diff --git a/allure-plugin/src/it/report-multi/module2/build/executor.json b/allure-plugin/src/it/report-multi/module2/build/executor.json new file mode 100644 index 0000000..759d5dc --- /dev/null +++ b/allure-plugin/src/it/report-multi/module2/build/executor.json @@ -0,0 +1,8 @@ +{ + "name": "Gradle", + "type": "gradle", + "taskName": "test", + "buildName": "module2", + "projectPath": ":module2", + "projectVersion": "1.1" +} diff --git a/allure-plugin/src/it/report-multi/module3/build.gradle b/allure-plugin/src/it/report-multi/module3/build.gradle new file mode 100644 index 0000000..e69de29 diff --git a/src/it/report-multi/settings.gradle b/allure-plugin/src/it/report-multi/settings.gradle similarity index 66% rename from src/it/report-multi/settings.gradle rename to allure-plugin/src/it/report-multi/settings.gradle index 8f12a83..d7fca0e 100644 --- a/src/it/report-multi/settings.gradle +++ b/allure-plugin/src/it/report-multi/settings.gradle @@ -1,2 +1,3 @@ include ':module1' include ':module2' +include ':module3' diff --git a/src/it/spock/build.gradle b/allure-plugin/src/it/spock/build.gradle similarity index 59% rename from src/it/spock/build.gradle rename to allure-plugin/src/it/spock/build.gradle index 05be972..e7b2019 100644 --- a/src/it/spock/build.gradle +++ b/allure-plugin/src/it/spock/build.gradle @@ -4,19 +4,16 @@ plugins { } allure { - version = '2.4.1' aspectjweaver = true useSpock { - version = "2.0-BETA21" } } repositories { mavenCentral() - jcenter() } dependencies { - testCompile 'org.spockframework:spock-core' + testImplementation 'org.spockframework:spock-core:1.3-groovy-2.5' } diff --git a/src/it/spock/src/test/groovy/tests/SpockTest.groovy b/allure-plugin/src/it/spock/src/test/groovy/tests/SpockTest.groovy similarity index 100% rename from src/it/spock/src/test/groovy/tests/SpockTest.groovy rename to allure-plugin/src/it/spock/src/test/groovy/tests/SpockTest.groovy diff --git a/src/it/testng-autoconfigure/build.gradle b/allure-plugin/src/it/testng-autoconfigure/build.gradle similarity index 69% rename from src/it/testng-autoconfigure/build.gradle rename to allure-plugin/src/it/testng-autoconfigure/build.gradle index fe42009..73677cc 100644 --- a/src/it/testng-autoconfigure/build.gradle +++ b/allure-plugin/src/it/testng-autoconfigure/build.gradle @@ -4,17 +4,15 @@ plugins { } allure { - version = '2.4.1' autoconfigure = true } repositories { mavenCentral() - jcenter() } dependencies { - testCompile 'org.testng:testng:6.8' + testImplementation 'org.testng:testng:6.8' } test { diff --git a/src/it/testng-autoconfigure/src/test/java/TestNgTest.java b/allure-plugin/src/it/testng-autoconfigure/src/test/java/TestNgTest.java similarity index 100% rename from src/it/testng-autoconfigure/src/test/java/TestNgTest.java rename to allure-plugin/src/it/testng-autoconfigure/src/test/java/TestNgTest.java diff --git a/src/it/testng/build.gradle b/allure-plugin/src/it/testng-spi-off/build.gradle similarity index 64% rename from src/it/testng/build.gradle rename to allure-plugin/src/it/testng-spi-off/build.gradle index 02b0086..5b80553 100644 --- a/src/it/testng/build.gradle +++ b/allure-plugin/src/it/testng-spi-off/build.gradle @@ -4,21 +4,19 @@ plugins { } allure { - version = '2.4.1' aspectjweaver = true useTestNG { - version = '2.0-BETA21' + spiOff = true } } repositories { mavenCentral() - jcenter() } dependencies { - testCompile 'org.testng:testng:6.8' + testImplementation 'org.testng:testng:6.8' } test { diff --git a/src/it/testng-spi-off/src/test/java/TestNgTest.java b/allure-plugin/src/it/testng-spi-off/src/test/java/TestNgTest.java similarity index 100% rename from src/it/testng-spi-off/src/test/java/TestNgTest.java rename to allure-plugin/src/it/testng-spi-off/src/test/java/TestNgTest.java diff --git a/allure-plugin/src/it/testng/build.gradle b/allure-plugin/src/it/testng/build.gradle new file mode 100644 index 0000000..eadcf22 --- /dev/null +++ b/allure-plugin/src/it/testng/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'java' + id 'io.qameta.allure' +} + +allure { + aspectjweaver = true + + useTestNG { + } +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.testng:testng:6.8' +} + +test { + useTestNG() +} diff --git a/src/it/testng/src/test/java/TestNgTest.java b/allure-plugin/src/it/testng/src/test/java/TestNgTest.java similarity index 100% rename from src/it/testng/src/test/java/TestNgTest.java rename to allure-plugin/src/it/testng/src/test/java/TestNgTest.java diff --git a/allure-plugin/src/main/kotlin/io/qameta/allure/gradle/allure/AllurePlugin.kt b/allure-plugin/src/main/kotlin/io/qameta/allure/gradle/allure/AllurePlugin.kt new file mode 100644 index 0000000..ba82511 --- /dev/null +++ b/allure-plugin/src/main/kotlin/io/qameta/allure/gradle/allure/AllurePlugin.kt @@ -0,0 +1,22 @@ +package io.qameta.allure.gradle.allure + +import io.qameta.allure.gradle.gather.AllureGatherPlugin +import io.qameta.allure.gradle.report.AllureReportPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply + +/** + * This is a shortcut for [AllureGatherPlugin.PLUGIN_NAME] and [AllureReportPlugin.PLUGIN_NAME] plugins. + * If you need an aggregate report, then use [AllureAggregateReportPlugin.PLUGIN_NAME] plugin. + */ +open class AllurePlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure" + } + + override fun apply(target: Project): Unit = target.run { + apply() + apply() + } +} diff --git a/src/test/java/io/qameta/allure/gradle/AggregatedReportTest.java b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/AggregatedReportTest.java similarity index 85% rename from src/test/java/io/qameta/allure/gradle/AggregatedReportTest.java rename to allure-plugin/src/test/java/io/qameta/allure/gradle/allure/AggregatedReportTest.java index 181d5f3..e46b8fc 100644 --- a/src/test/java/io/qameta/allure/gradle/AggregatedReportTest.java +++ b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/AggregatedReportTest.java @@ -1,4 +1,4 @@ -package io.qameta.allure.gradle; +package io.qameta.allure.gradle.allure; import io.qameta.allure.gradle.rule.GradleRunnerRule; import org.gradle.testkit.runner.BuildResult; @@ -35,9 +35,9 @@ public class AggregatedReportTest { @Parameterized.Parameters(name = "{1} [{0}]") public static Collection getFrameworks() { return Arrays.asList( - new Object[]{"3.5", "src/it/report-multi", new String[]{"allureAggregatedReport"}}, - new Object[]{"4.0", "src/it/report-multi", new String[]{"allureAggregatedReport"}}, - new Object[]{"5.0", "src/it/report-multi", new String[]{"allureAggregatedReport"}} + new Object[]{"7.0", "src/it/report-multi", new String[]{"allureAggregateReport"}}, + new Object[]{"5.0", "src/it/report-multi", new String[]{"allureAggregateReport"}}, + new Object[]{"6.0", "src/it/report-multi", new String[]{"allureAggregateReport"}} ); } @@ -51,7 +51,7 @@ public void shouldGenerateAllureReport() { .containsExactly(SUCCESS); File projectDir = gradleRunner.getProjectDir(); - File reportDir = new File(projectDir, "build/reports/allure-report"); + File reportDir = new File(projectDir, "build/reports/allure-report/allureAggregateReport"); assertThat(reportDir).as("Allure report directory") .exists(); diff --git a/src/test/java/io/qameta/allure/gradle/CategoriesTest.java b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/CategoriesTest.java similarity index 76% rename from src/test/java/io/qameta/allure/gradle/CategoriesTest.java rename to allure-plugin/src/test/java/io/qameta/allure/gradle/allure/CategoriesTest.java index e3a8dc9..83492f5 100644 --- a/src/test/java/io/qameta/allure/gradle/CategoriesTest.java +++ b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/CategoriesTest.java @@ -1,4 +1,4 @@ -package io.qameta.allure.gradle; +package io.qameta.allure.gradle.allure; import io.qameta.allure.gradle.rule.GradleRunnerRule; import org.gradle.testkit.runner.BuildResult; @@ -12,7 +12,6 @@ import java.util.Collection; import static org.assertj.core.api.Assertions.assertThat; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; /** * eroshenkoam @@ -39,9 +38,9 @@ public class CategoriesTest { @Parameterized.Parameters(name = "{1} [{0}]") public static Collection getFrameworks() { return Arrays.asList( - new Object[]{"3.5", "src/it/categories", new String[]{"allureReport"}}, - new Object[]{"4.0", "src/it/categories", new String[]{"allureReport"}}, - new Object[]{"5.0", "src/it/categories", new String[]{"allureReport"}} + new Object[]{"7.0", "src/it/categories", new String[]{"allureReport"}}, + new Object[]{"5.0", "src/it/categories", new String[]{"allureReport"}}, + new Object[]{"6.0", "src/it/categories", new String[]{"allureReport"}} ); } @@ -50,11 +49,6 @@ public void shouldCopyCategoriesInfo() { BuildResult buildResult = gradleRunner.getBuildResult(); File resultsDir = new File(gradleRunner.getProjectDir(), "build/allure-results"); - assertThat(buildResult.getTasks()).as("Download allure task status") - .filteredOn(task -> task.getPath().equals(":downloadAllure")) - .extracting("outcome") - .containsExactly(SUCCESS); - assertThat(resultsDir.listFiles()).as("Allure executor info") .filteredOn(file -> file.getName().endsWith("categories.json")) .hasSize(1); diff --git a/src/test/java/io/qameta/allure/gradle/DependenciesTest.java b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/DependenciesTest.java similarity index 75% rename from src/test/java/io/qameta/allure/gradle/DependenciesTest.java rename to allure-plugin/src/test/java/io/qameta/allure/gradle/allure/DependenciesTest.java index b0c7a31..9974de8 100644 --- a/src/test/java/io/qameta/allure/gradle/DependenciesTest.java +++ b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/DependenciesTest.java @@ -1,4 +1,4 @@ -package io.qameta.allure.gradle; +package io.qameta.allure.gradle.allure; import io.qameta.allure.gradle.rule.GradleRunnerRule; import org.gradle.testkit.runner.BuildResult; @@ -26,17 +26,17 @@ @RunWith(Parameterized.class) public class DependenciesTest { + // The order of versions is newest, oldest, rest private static final String[][] IT_MATRIX = { - { "src/it/cucumber-jvm", "3.5", "4.0", "5.0" }, - { "src/it/cucumber2-jvm", "3.5", "4.0", "5.0" }, - { "src/it/junit4", "3.5", "4.0", "5.0" }, - { "src/it/junit4-autoconfigure", "3.5", "4.0", "5.0" }, - { "src/it/junit4-kotlin", "5.0", "5.1" }, - { "src/it/junit5", "3.5", "4.0", "5.0" }, - { "src/it/testng", "3.5", "4.0", "5.0" }, - { "src/it/testng-autoconfigure", "3.5", "4.0", "5.0" }, - { "src/it/spock", "3.5", "4.0", "5.0" }, - { "src/it/junit5-native", "4.6", "5.0" }, + { "src/it/cucumber-jvm", "7.0", "5.0", "6.0" }, + { "src/it/cucumber2-jvm", "7.0", "5.0", "6.0" }, + { "src/it/junit4", "7.0", "5.0", "6.0" }, + { "src/it/junit4-autoconfigure", "7.0", "5.0", "6.0" }, + { "src/it/junit4-kotlin", "7.0", "5.1", "5.0" }, + { "src/it/junit5", "7.0", "5.0", "6.0" }, + { "src/it/testng", "7.0", "5.0", "6.0" }, + { "src/it/testng-autoconfigure", "7.0", "5.0", "6.0" }, + { "src/it/spock", "7.0", "5.0", "6.0" }, }; @Parameterized.Parameter(0) @@ -66,7 +66,7 @@ public void shouldCreateAllureResults() { .extracting("outcome") .containsExactly(SUCCESS); - File resultsDir = new File(gradleRunner.getProjectDir(), "build/allure-results"); + File resultsDir = new File(gradleRunner.getProjectDir(), "build/allure-results/test"); assertThat(resultsDir).as("Allure results directory") .exists(); diff --git a/src/test/java/io/qameta/allure/gradle/TestNgSpiOffTest.java b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/TestNgSpiOffTest.java similarity index 54% rename from src/test/java/io/qameta/allure/gradle/TestNgSpiOffTest.java rename to allure-plugin/src/test/java/io/qameta/allure/gradle/allure/TestNgSpiOffTest.java index ebe4be6..17849e3 100644 --- a/src/test/java/io/qameta/allure/gradle/TestNgSpiOffTest.java +++ b/allure-plugin/src/test/java/io/qameta/allure/gradle/allure/TestNgSpiOffTest.java @@ -1,23 +1,39 @@ -package io.qameta.allure.gradle; +package io.qameta.allure.gradle.allure; import io.qameta.allure.gradle.rule.GradleRunnerRule; import org.gradle.testkit.runner.BuildResult; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.File; +import java.util.Arrays; +import java.util.Collection; import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +/** + * Verifies that plugin properly adds `spi-off` dependency (the one without META-INF/services) + * when {@link io.qameta.allure.gradle.gather.config.AdapterConfig#getAutoconfigureListeners} is {@code false}. + */ +@RunWith(Parameterized.class) public class TestNgSpiOffTest { + @Parameterized.Parameter(0) + public String version; @Rule public GradleRunnerRule gradleRunner = new GradleRunnerRule() - .version("4.0") + .version(() -> version) .project("src/it/testng-spi-off") .tasks("test"); + @Parameterized.Parameters(name = "{0}") + public static Collection getFrameworks() { + return Arrays.asList("7.0", "6.0", "5.4", "5.3"); + } + @Test public void allureReportIsNotGenerated() { BuildResult buildResult = gradleRunner.getBuildResult(); @@ -29,7 +45,10 @@ public void allureReportIsNotGenerated() { .filteredOn(task -> task.getPath().equals(":test") || task.getPath().equals(":allureReport")) .extracting("outcome") .containsExactly(SUCCESS); - File resultsDir = new File(projectDir.getAbsolutePath() + "/build/allure-results"); - assertThat(resultsDir.list()).isNull(); + File resultsDir = new File(projectDir.getAbsolutePath() + "/build/allure-results/test"); + // executor.json is always generated + assertThat(resultsDir.listFiles()) + .filteredOn(file -> !file.getName().equals("executor.json")) + .isEmpty(); } } diff --git a/allure-plugin/src/test/kotlin/io/qameta/allure/gradle/report/DslTest.kt b/allure-plugin/src/test/kotlin/io/qameta/allure/gradle/report/DslTest.kt new file mode 100644 index 0000000..55757d0 --- /dev/null +++ b/allure-plugin/src/test/kotlin/io/qameta/allure/gradle/report/DslTest.kt @@ -0,0 +1,47 @@ +package io.qameta.allure.gradle.report + +import io.qameta.allure.gradle.rule.GradleRunnerRule +import org.assertj.core.api.Assertions.assertThat +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class DslTest { + @Rule + @JvmField + val gradleRunner = GradleRunnerRule() + .version { version } + .project { project } + .tasks("testDsl") + + + @Parameterized.Parameter(0) + lateinit var version: String + + @Parameterized.Parameter(1) + lateinit var project: String + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{1} [{0}]") + fun getFrameworks() = listOf( + arrayOf("7.0", "src/it/full-dsl-kotlin"), + arrayOf("6.0", "src/it/full-dsl-kotlin"), + arrayOf("5.0", "src/it/full-dsl-kotlin"), + arrayOf("7.0", "src/it/full-dsl-groovy"), + arrayOf("6.0", "src/it/full-dsl-groovy"), + arrayOf("5.0", "src/it/full-dsl-groovy") + ) + } + + @Test + fun `build script should compile`() { + assertThat(gradleRunner.buildResult.tasks).`as`("testDsl task status") + .filteredOn { task -> task.path == ":testDsl" } + .extracting("outcome") + .containsExactly(TaskOutcome.SUCCESS) + } +} diff --git a/allure-report-plugin/build.gradle.kts b/allure-report-plugin/build.gradle.kts new file mode 100644 index 0000000..f7c520a --- /dev/null +++ b/allure-report-plugin/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("allure-gradle.kotlin-dsl-published-plugin") +} + +group = "io.qameta.allure.gradle.report" + +dependencies { + implementation(project(":allure-base-plugin")) + + testImplementation(project(":testkit-junit4")) + testImplementation("org.assertj:assertj-core:_") +} + +tasks.test { + // Treat test task out-of-date if src/it changes + inputs.dir(layout.projectDirectory.dir("src/it")).optional() +} + +gradlePlugin { + plugins { + create("allureDownloadPlugin") { + id = "io.qameta.allure-download" + description = "Adds a task to download the required Allure version" + implementationClass = "io.qameta.allure.gradle.download.AllureDownloadPlugin" + } + create("allureReportPlugin") { + id = "io.qameta.allure-report" + description = "Adds a task to build Allure report for the current project" + implementationClass = "io.qameta.allure.gradle.report.AllureReportPlugin" + } + create("allureAggregateReportPlugin") { + id = "io.qameta.allure-aggregate-report" + description = "Adds a task to aggregate the results from multiple projects" + implementationClass = "io.qameta.allure.gradle.report.AllureAggregateReportPlugin" + } + } +} diff --git a/allure-report-plugin/src/it/report-only/build.gradle b/allure-report-plugin/src/it/report-only/build.gradle new file mode 100644 index 0000000..9606c57 --- /dev/null +++ b/allure-report-plugin/src/it/report-only/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'io.qameta.allure-report' +} + +repositories { + mavenCentral() +} + +dependencies { + allureReport(files("$buildDir/allure-results")) +} + +allure { + version = '2.8.0' +} diff --git a/src/it/report-bintray-task/build/allure-results/simple-result.json b/allure-report-plugin/src/it/report-only/build/allure-results/simple-result.json similarity index 100% rename from src/it/report-bintray-task/build/allure-results/simple-result.json rename to allure-report-plugin/src/it/report-only/build/allure-results/simple-result.json diff --git a/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.downloadExtensions.kt b/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.downloadExtensions.kt new file mode 100644 index 0000000..187e2ad --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.downloadExtensions.kt @@ -0,0 +1,11 @@ +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.download.AllureCommandlineExtension +import org.gradle.api.Action +import org.gradle.api.plugins.ExtensionAware +import org.gradle.kotlin.dsl.the + +val AllureExtension.commandline: AllureCommandlineExtension get() = (this as ExtensionAware).the() + +fun AllureExtension.commandline(configureAction: Action) { + configureAction.execute(commandline) +} diff --git a/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.reportExtensions.kt b/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.reportExtensions.kt new file mode 100644 index 0000000..7046351 --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io.qameta.allure.gradle.reportExtensions.kt @@ -0,0 +1,11 @@ +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.report.AllureReportExtension +import org.gradle.api.Action +import org.gradle.api.plugins.ExtensionAware +import org.gradle.kotlin.dsl.the + +val AllureExtension.report: AllureReportExtension get() = (this as ExtensionAware).the() + +fun AllureExtension.report(configureAction: Action) { + configureAction.execute(report) +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureCommandlineExtension.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureCommandlineExtension.kt new file mode 100644 index 0000000..b12e89b --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureCommandlineExtension.kt @@ -0,0 +1,68 @@ +package io.qameta.allure.gradle.download + +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.util.conv +import org.gradle.api.Project +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.property +import javax.inject.Inject + +open class AllureCommandlineExtension @Inject constructor( + allureExtension: AllureExtension, + project: Project, + objects: ObjectFactory +) { + companion object { + const val NAME = "commandline" + } + + /** + * Configure `groupId` for `allure-commandline` artifact retrieval. + * Default value is `io.qameta.allure` if [downloadUrlPattern] is not set + * or 'custom.io.qameta.allure` if [downloadUrlPattern] is set. + * The use of `custom.io...` group id enables to use Gradle's repository filtering + * for better security and resolution performance. + * The property corresponds to `[organization]` pattern in [downloadUrlPattern] + */ + val group = objects.property() + .conv(project.provider { + // TODO: Provider.orElse(Provider) is Gradle 5.6+ + if (downloadUrlPattern.isPresent) { + "custom.io.qameta.allure" + } else { + "io.qameta.allure" + } + }) + + /** + * Configure `artifactId` for `allure-commandline` artifact retrieval. + * Default value is `allure-commandline` for Allure 2.8.0+ and `allure` for earlier versions. + * It corresponds to `[module]` pattern in [downloadUrlPattern] + */ + val module = objects.property() + .conv(allureExtension.version.map { + val normalized = it.replace(Regex("\\d+")) { + it.value.padStart(5, '0') + } + if (normalized >= "00002.00008.00000") { + "allure-commandline" + } else { + "allure" + } + }) + + /** + * Configure `groupId` for `allure-commandline` artifact retrieval. + * Default value is `zip` + * It corresponds to `[ext]` pattern in [downloadUrlPattern] + */ + val extension = objects.property() + .conv("zip") + + /** + * By default, allure-commandline is received from Maven Central, so the property is unset. + * This property allows overriding the url. + * The following patterns are supported: `[group]`, `[module]`, `[version]`, `[extension]` + */ + val downloadUrlPattern = objects.property() +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureDownloadPlugin.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureDownloadPlugin.kt new file mode 100644 index 0000000..8834ce8 --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/AllureDownloadPlugin.kt @@ -0,0 +1,127 @@ +package io.qameta.allure.gradle.download + +import commandline +import io.qameta.allure.gradle.base.AllureBasePlugin +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.download.tasks.DownloadAllure +import io.qameta.allure.gradle.report.AllureReportBasePlugin +import io.qameta.allure.gradle.util.gradleGe51 +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.artifacts.repositories.ArtifactRepository +import org.gradle.api.plugins.ExtensionAware +import org.gradle.internal.Factory +import org.gradle.kotlin.dsl.* +import org.gradle.util.GradleVersion + +/** + * The plugin Adds [DownloadAllure] task. + * Note: the end-user needs to specify repositories manually (e.g. `repositories { mavenCentral() }`) + */ +open class AllureDownloadPlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-download" + const val ALLURE_DOWNLOAD_TASK_NAME = "downloadAllure" + const val ALLURE_COMMANDLINE_CONFIGURATION = "allureCommandline" + } + + override fun apply(target: Project): Unit = target.run { + apply() + + // allure.report extension + apply() + + val allureExtension = the() + + val reportExtension = (allureExtension as ExtensionAware).extensions.create( + AllureCommandlineExtension.NAME, + allureExtension, + project, + objects + ) + + val allureCommandLine = configurations.create(ALLURE_COMMANDLINE_CONFIGURATION) { + isCanBeResolved = true + isCanBeConsumed = false + defaultDependencies { + val fileExtension = reportExtension.extension.map { "@$it" }.orNull ?: "" + val group = reportExtension.group.get() + val module = reportExtension.module.get() + val version = allureExtension.version.get() + add(project.dependencies.create("$group:$module:$version$fileExtension")) + + allureExtension.commandline.downloadUrlPattern.orNull?.let { link -> + val formattedLink = formatLink(link, group, module, version) + declareCustomAllureCommandlineRepository(group, module, formattedLink) + } + } + } + + tasks.register(ALLURE_DOWNLOAD_TASK_NAME) { + this.allureCommandLine.set(allureCommandLine) + } + } + + /** + * `IvyArtifactRepository` supports patterns, however, we do not use it since + * artifact declaration requries separate `repository url` + `layoutPattern`. + * Parsing URLs is non-trivial, so we replace patterns and build a single URL. + * That works as long as we need a single binary from the said repository + * which is good enough. + */ + private fun formatLink(link: String, group: String, module: String, version: String) = + link.replace(Regex("\\[([^]]+)]")) { + when (it.groups[1]!!.value) { + "organization", "group" -> group + "module" -> module + "version" -> version + else -> throw IllegalArgumentException( + "Unexpected pattern ${it.value} detected in allure.commandline.downloadUrlPattern $link." + + " The following patterns are supported: [organization] = $group, [group] = $group, [module] = $module, [version] = $version" + ) + } + } + + /** + * Adds a repository so Gradle can retrieve `group:module` artifact with its regular dependency resolution. + */ + private fun Project.declareCustomAllureCommandlineRepository(group: String, module: String, link: String) { + repositories { + exclusiveRepo(group, module) { + ivy { + url = uri(link) + patternLayout { + // Link should already be formatted, so no pattern needed here + artifact("") + } + metadataSources { // skip downloading ivy.xml + artifact() + } + } + } + } + } + + fun RepositoryHandler.exclusiveRepo(group: String, module: String, repository: Factory) { + val gradleGe62 = GradleVersion.current() >= GradleVersion.version("6.2") + if (gradleGe62) { + // exclusiveContent is Gradle 6.2+ feature + exclusiveContent { + filter { + includeModule(group, module) + } + forRepository(repository) + } + return + } + repository.create()!!.apply { + if (gradleGe51) { + // content filtering is Gradle 6.2+ feature + content { + includeModule(group, module) + } + } + } + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/tasks/DownloadAllure.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/tasks/DownloadAllure.kt new file mode 100644 index 0000000..866f947 --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/download/tasks/DownloadAllure.kt @@ -0,0 +1,39 @@ +package io.qameta.allure.gradle.download.tasks + +import org.gradle.api.DefaultTask +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.RelativePath +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.property +import javax.inject.Inject + +/** + * The task downloads Allure distribution. + * It would probably be better to use Gradle 6.1 [org.gradle.api.services.BuildService] to keep + * a single Allure binary across all subprojects. + */ +open class DownloadAllure @Inject constructor(objects: ObjectFactory) : DefaultTask() { + @InputFiles + @PathSensitive(PathSensitivity.NONE) + val allureCommandLine = objects.property() + + @OutputDirectory + val destinationDir = project.layout.buildDirectory.dir("allure/commandline") + + @TaskAction + fun downloadAllure() { + logger.info("Unpacking Allure Commandline to ${destinationDir.get()}") + val result = project.sync { + into(destinationDir) + from(project.zipTree(allureCommandLine.get().singleFile)) { + eachFile { + // Replace "allure-.../abc/..." with "abc/..." for predictable folder locations + relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) + } + includeEmptyDirs = false + } + } + didWork = result.didWork + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportBasePlugin.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportBasePlugin.kt new file mode 100644 index 0000000..e89906b --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportBasePlugin.kt @@ -0,0 +1,32 @@ +package io.qameta.allure.gradle.report + +import io.qameta.allure.gradle.base.AllureBasePlugin +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.base.dsl.extensions +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.* + +/** + * The plugin adds [ALLURE_RAW_RESULT_ELEMENTS_CONFIGURATION_NAME] configuration so the project + * can share raw Allure results for aggregation. + */ +open class AllureReportBasePlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-report-base" + } + + override fun apply(target: Project): Unit = target.run { + // reporting-base adds ReportingExtension to project so users can configure default report folder + apply(plugin = "reporting-base") + + apply() + the().extensions.create( + AllureReportExtension.NAME, + project, + // Gradle 5 can't inject objects yet + // TODO: remove when Gradle 5 support can be dropped + objects + ) + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportExtension.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportExtension.kt new file mode 100644 index 0000000..e8205e7 --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportExtension.kt @@ -0,0 +1,52 @@ +package io.qameta.allure.gradle.report + +import io.qameta.allure.gradle.util.conv +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.reporting.ReportingExtension +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.the +import javax.inject.Inject + +open class AllureReportExtension @Inject constructor( + private val project: Project, + objects: ObjectFactory +) { + companion object { + const val NAME = "report" + } + + /** + * Base directory for Allure report. + * Note: since the same project can have both aggregate and regular reports, + * the actual reports would be placed to `$reportDir/allureReport` and `$reportDir/allureAggregateReport` + * folders. + */ + val reportDir: DirectoryProperty = objects.directoryProperty().apply { + conv(project.the().baseDirectory.dir("allure-report")) + } + + /** + * By default, `allureReport` and `allureServe` tasks do not execute tests since it might be time-consuming. + * However, in case tests are up-to-date or from-cache, it might be a nice idea to follow Gradle's default + * approach: "the task should (re)build all its prerequisites", so the user could edit the source file, + * launch `allureReport` and get the updated report with all the tests updated. + */ + val dependsOnTests: Property = objects.property().conv( + project.provider { + false + } + ) + + /** + * By default, `allureReport` and `allureServe` tasks do not execute tests since it might be time-consuming. + * However, in case tests are up-to-date or from-cache, it might be a nice idea to follow Gradle's default + * approach: "the task should (re)build all its prerequisites", so the user could edit the source file, + * launch `allureReport` and get the updated report with all the tests updated. + */ + fun dependsOnTests() { + dependsOnTests.set(true) + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportPlugin.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportPlugin.kt new file mode 100644 index 0000000..5daff5e --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/AllureReportPlugin.kt @@ -0,0 +1,127 @@ +package io.qameta.allure.gradle.report + +import io.qameta.allure.gradle.base.metadata.AllureResultType +import io.qameta.allure.gradle.download.AllureDownloadPlugin +import io.qameta.allure.gradle.download.tasks.DownloadAllure +import io.qameta.allure.gradle.report.AllureAggregateReportPlugin.Companion.REPORT_TASK_NAME +import io.qameta.allure.gradle.report.AllureAggregateReportPlugin.Companion.SERVE_TASK_NAME +import io.qameta.allure.gradle.report.AllureReportPlugin.Companion.REPORT_TASK_NAME +import io.qameta.allure.gradle.report.AllureReportPlugin.Companion.SERVE_TASK_NAME +import io.qameta.allure.gradle.report.tasks.AllureReport +import io.qameta.allure.gradle.report.tasks.AllureServe +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.* +import org.gradle.language.base.plugins.LifecycleBasePlugin + +/** + * The plugin adds tasks to build Allure reports for the current project ([REPORT_TASK_NAME] and [SERVE_TASK_NAME]). + */ +open class AllureReportPlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-report" + const val AGGREGATE_CONFIGURATION = "allureReport" + const val REPORT_TASK_NAME = "allureReport" + const val SERVE_TASK_NAME = "allureServe" + } + + override fun apply(target: Project): Unit = target.run { + apply() + + registerReportTasks( + AGGREGATE_CONFIGURATION, + REPORT_TASK_NAME, + SERVE_TASK_NAME, + listOf(project) + ) + } +} + +/** + * The plugin adds tasks to aggregate Allure reports for the current project and its subprojects ( + * [REPORT_TASK_NAME] and [SERVE_TASK_NAME]). + * Note: if you need to gather the data from the current project, then you need [AllureGatherPlugin.PLUGIN_NAME] + */ +open class AllureAggregateReportPlugin : Plugin { + companion object { + const val PLUGIN_NAME = "io.qameta.allure-aggregate-report" + const val AGGREGATE_CONFIGURATION = "allureAggregateReport" + const val REPORT_TASK_NAME = "allureAggregateReport" + const val SERVE_TASK_NAME = "allureAggregateServe" + } + + override fun apply(target: Project): Unit = target.run { + apply() + + registerReportTasks( + AGGREGATE_CONFIGURATION, + REPORT_TASK_NAME, + SERVE_TASK_NAME, + allprojects + ) + } +} + +internal fun Project.registerReportTasks( + aggConfigurationName: String, + reportTaskName: String, + serveTaskName: String, + projects: Iterable +) { + val allureAggregate = configurations.create(aggConfigurationName) { + description = "Contains all the projects for aggregating Allure results" + isCanBeConsumed = false + isCanBeResolved = true + attributes { + attribute(AllureResultType.ATTRIBUTE, AllureResultType.RAW) + } + } + + val allureGenerateCategories by configurations.creating { + extendsFrom(allureAggregate) + + description = "Contains all the projects for aggregating Allure results" + isCanBeConsumed = false + isCanBeResolved = true + attributes { + attribute(AllureResultType.ATTRIBUTE, @Suppress("deprecation") AllureResultType.COPY_CATEGORIES) + } + } + + // It would be better to declare these dependencies via allureAggregate.defaultDependencies + // However, it defeats Gradle's task dependency tracking: https://github.com/gradle/gradle/issues/16910 + dependencies { + for (p in projects) { + allureAggregate(create(p)) + } + } + + // Ensure download task exists + apply() + val download = tasks.named(AllureDownloadPlugin.ALLURE_DOWNLOAD_TASK_NAME) + + tasks.register(reportTaskName) { + description = "Builds Allure report from $aggConfigurationName dependencies" + group = LifecycleBasePlugin.VERIFICATION_GROUP + dependsOn(download) + // This dependency ensures categories.json files are copied by the relevant copyCategories task + // It enables users to update cagetories.json file in src/..., launch allureReport + // and see the improved report without running the tests again + dependsOn(allureGenerateCategories) + allureHome.set(download.flatMap { it.destinationDir }) + // allureAggregate uses defaultDependencies, and Gradle seems to miss task dependencies there + resultsDirs.set(allureAggregate) + } + tasks.register(serveTaskName) { + description = "Builds Allure report from $aggConfigurationName dependencies and launches Allure server" + group = LifecycleBasePlugin.VERIFICATION_GROUP + dependsOn(download) + // This dependency ensures categories.json files are copied by the relevant copyCategories task + // It enables users to update cagetories.json file in src/..., launch allureReport + // and see the improved report without running the tests again + dependsOn(allureGenerateCategories) + allureHome.set(download.flatMap { it.destinationDir }) + // allureAggregate uses defaultDependencies, and Gradle seems to miss task dependencies there + resultsDirs.set(allureAggregate) + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureReport.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureReport.kt new file mode 100644 index 0000000..e2d276c --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureReport.kt @@ -0,0 +1,48 @@ +package io.qameta.allure.gradle.report.tasks + +import io.qameta.allure.gradle.base.AllureExtension +import io.qameta.allure.gradle.base.tasks.AllureExecTask +import io.qameta.allure.gradle.util.conv +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.* +import org.gradle.api.tasks.options.Option +import org.gradle.kotlin.dsl.property +import org.gradle.kotlin.dsl.the +import report +import javax.inject.Inject + +open class AllureReport @Inject constructor(objects: ObjectFactory) : AllureExecTask(objects) { + @OutputDirectory + @Option(option = "report-dir", description = "The directory to generate Allure report into") + val reportDir = objects.directoryProperty().conv( + project.the().report.reportDir.map { it.dir(this@AllureReport.name) } + ) + + @Input + @Option(option = "clean", description = "Clean Allure report directory before generating a new one") + val clean = objects.property().conv(false) + + companion object { + const val NAME = "allureReport" + const val GENERATE_COMMAND = "generate" + } + + @TaskAction + fun generateAllureReport() { + val rawResults = rawResults.map { it.absolutePath } + logger.info("Input directories for $name: $rawResults") + project.exec { + executable(allureExecutable) + if (verbose.get()) { + args("--verbose") + } + args(GENERATE_COMMAND) + args(rawResults) + args("-o", reportDir.get().asFile.absolutePath) + // TODO: replace with Gradle's delete? + if (clean.get()) { + args("--clean") + } + } + } +} diff --git a/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureServe.kt b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureServe.kt new file mode 100644 index 0000000..944c753 --- /dev/null +++ b/allure-report-plugin/src/main/kotlin/io/qameta/allure/gradle/report/tasks/AllureServe.kt @@ -0,0 +1,49 @@ +package io.qameta.allure.gradle.report.tasks + +import io.qameta.allure.gradle.base.tasks.AllureExecTask +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.options.Option +import org.gradle.kotlin.dsl.property +import javax.inject.Inject + +open class AllureServe @Inject constructor(objects: ObjectFactory) : AllureExecTask(objects) { + companion object { + const val NAME = "allureServe" + const val SERVE_COMMAND = "serve" + } + + init { + outputs.upToDateWhen { false } + } + + @Internal + @Option(option = "host", description = "This host will be used to start web server for the report") + val host = objects.property() + + @Internal + @Option(option = "port", description = "This port will be used to start web server for the report") + val port = objects.property() + + @TaskAction + fun serveAllureReport() { + val rawResults = rawResults.map { it.absolutePath } + logger.info("Input directories for $name: $rawResults") + project.exec { + executable(allureExecutable) + if (verbose.get()) { + args("--verbose") + } + args(SERVE_COMMAND) + host.orNull?.let { + args("--host", it) + } + port.orNull?.let { + args("--port", it) + } + args(rawResults) + } + } +} diff --git a/src/test/java/io/qameta/allure/gradle/ReportOnlyTest.java b/allure-report-plugin/src/test/java/io/qameta/allure/gradle/report/ReportOnlyTest.java similarity index 50% rename from src/test/java/io/qameta/allure/gradle/ReportOnlyTest.java rename to allure-report-plugin/src/test/java/io/qameta/allure/gradle/report/ReportOnlyTest.java index 7875e61..2ce2671 100644 --- a/src/test/java/io/qameta/allure/gradle/ReportOnlyTest.java +++ b/allure-report-plugin/src/test/java/io/qameta/allure/gradle/report/ReportOnlyTest.java @@ -1,23 +1,42 @@ -package io.qameta.allure.gradle; +package io.qameta.allure.gradle.report; import io.qameta.allure.gradle.rule.GradleRunnerRule; import org.gradle.testkit.runner.BuildResult; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.File; +import java.util.Arrays; +import java.util.Collection; import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +@RunWith(Parameterized.class) public class ReportOnlyTest { + @Parameterized.Parameter(0) + public String project; + + @Parameterized.Parameter(1) + public String version; @Rule public GradleRunnerRule gradleRunner = new GradleRunnerRule() - .version("5.0") - .project("src/it/report-only") + .version(() -> version) + .project(() -> project) .tasks("allureReport"); + @Parameterized.Parameters(name = "{0} [{1}]") + public static Collection getFrameworks() { + return Arrays.asList( + new Object[]{"src/it/report-only", "7.0"}, + new Object[]{"src/it/report-only", "5.0"}, + new Object[]{"src/it/report-only", "6.0"} + ); + } + @Test public void allureReportGenerated() { BuildResult buildResult = gradleRunner.getBuildResult(); @@ -29,7 +48,8 @@ public void allureReportGenerated() { .extracting("outcome") .containsOnly(SUCCESS); - File resultsDir = new File(projectDir.getAbsolutePath() + "/build/allure-results"); - assertThat(resultsDir.list()).isNotEmpty(); + File resultsDir = new File(projectDir.getAbsolutePath() + "/build/reports/allure-report/allureReport"); + assertThat(resultsDir).as("folder with Allure reports") + .isNotEmptyDirectory(); } } diff --git a/allure-report-plugin/src/test/kotlin/io/qameta/allure/gradle/download/DownloadTaskTest.kt b/allure-report-plugin/src/test/kotlin/io/qameta/allure/gradle/download/DownloadTaskTest.kt new file mode 100644 index 0000000..21b095f --- /dev/null +++ b/allure-report-plugin/src/test/kotlin/io/qameta/allure/gradle/download/DownloadTaskTest.kt @@ -0,0 +1,82 @@ +package io.qameta.allure.gradle.download + +import commandline +import io.qameta.allure.gradle.base.AllureExtension +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.gradle.kotlin.dsl.* +import org.gradle.testfixtures.ProjectBuilder +import org.junit.Test + +class DownloadTaskTest { + @Test + fun `basic download`() { + ProjectBuilder.builder().build().run { + apply(plugin = "io.qameta.allure-download") + val allureCommandline by configurations + + // Repository is required to download allure-commandline + repositories { + mavenCentral() + } + + assertThat(allureCommandline.singleFile).`as`("allure-commandline binary") + .isNotEmpty + } + } + + @Test + fun `custom url`() { + ProjectBuilder.builder().build().run { + apply(plugin = "io.qameta.allure-report") + val allureCommandline by configurations + + val customUrl = "https://download-test-[group].test/[module]-custom-[version].zip" + + configure { + version.set("42.0") + // sam-with-receiver does not work in IDEA :( + commandline.apply { + // .test is reserved, see https://tools.ietf.org/html/rfc2606#section-2 + downloadUrlPattern.set(customUrl) + } + } + + assertThatThrownBy { + allureCommandline.singleFile + }.`as`("Custom URL configured as %s", customUrl) + .hasStackTraceContaining("https://download-test-custom.io.qameta.allure.test/allure-commandline-custom-42.0.zip") + } + } + + @Test + fun `illegal pattern in url`() { + + ProjectBuilder.builder().build().run { + apply(plugin = "io.qameta.allure-report") + val allureCommandline by configurations + + val customUrl = "https://localhost/[illegal-for-test].zip" + + configure { + version.set("42.0") + // sam-with-receiver does not work in IDEA :( + commandline.apply { + downloadUrlPattern.set("https://localhost/[illegal-for-test].zip") + } + } + + assertThatThrownBy { + allureCommandline.singleFile + }.`as`("Custom URL configured as %s", customUrl) + .hasMessageContaining( + "Unexpected pattern [illegal-for-test] detected in allure.commandline.downloadUrlPattern " + + "https://localhost/[illegal-for-test].zip. The following patterns are supported: " + + "[organization] = custom.io.qameta.allure, " + + "[group] = custom.io.qameta.allure, " + + "[module] = allure-commandline, " + + "[version] = 42.0" + ) + } + } +} diff --git a/build-logic-commons/.gitignore b/build-logic-commons/.gitignore new file mode 100644 index 0000000..de8b137 --- /dev/null +++ b/build-logic-commons/.gitignore @@ -0,0 +1 @@ +/*/build/ diff --git a/build-logic-commons/gradle-plugin/build.gradle.kts b/build-logic-commons/gradle-plugin/build.gradle.kts new file mode 100644 index 0000000..daa6f2e --- /dev/null +++ b/build-logic-commons/gradle-plugin/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `kotlin-dsl` +} + +group = "allure-gradle" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +val kotlinDslVersion = PluginDependenciesSpec { id -> + object : PluginDependencySpec { + var version: String? = null + override fun version(version: String?) = apply { this.version = version } + override fun apply(apply: Boolean) = this + override fun toString() = version ?: "" + } +}.`kotlin-dsl`.toString() + +dependencies { + implementation("org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:$kotlinDslVersion") +// implementation("org.gradle.kotlin:gradle-kotlin-dsl-conventions:0.7.0") +} + +kotlinDslPluginOptions { + experimentalWarning.set(false) +} diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/allure-gradle.kotlin-dsl-gradle-plugin.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/allure-gradle.kotlin-dsl-gradle-plugin.gradle.kts new file mode 100644 index 0000000..857ffad --- /dev/null +++ b/build-logic-commons/gradle-plugin/src/main/kotlin/allure-gradle.kotlin-dsl-gradle-plugin.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("java-library") + id("org.gradle.kotlin.kotlin-dsl") // this is 'kotlin-dsl' without version +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.validatePlugins { + failOnWarning.set(true) + enableStricterValidation.set(true) +} + +kotlinDslPluginOptions { + experimentalWarning.set(false) +} diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/buildlogic/DependencyHandlerScopeExtensions.kt b/build-logic-commons/gradle-plugin/src/main/kotlin/buildlogic/DependencyHandlerScopeExtensions.kt new file mode 100644 index 0000000..aa3e5eb --- /dev/null +++ b/build-logic-commons/gradle-plugin/src/main/kotlin/buildlogic/DependencyHandlerScopeExtensions.kt @@ -0,0 +1,22 @@ +package buildlogic + +import org.gradle.kotlin.dsl.DependencyHandlerScope +import org.gradle.kotlin.dsl.`kotlin-dsl` +import org.gradle.plugin.use.PluginDependenciesSpec +import org.gradle.plugin.use.PluginDependencySpec + +val DependencyHandlerScope.kotlinDslVersion: String + get() = PluginDependenciesSpec { + object : PluginDependencySpec { + var version: String? = null + override fun version(version: String?) = apply { this.version = version } + override fun apply(apply: Boolean) = this + override fun toString() = version ?: "" + } + }.`kotlin-dsl`.toString() + +fun DependencyHandlerScope.plugin(id: String, version: String) = + "$id:$id.gradle.plugin:$version" + +fun DependencyHandlerScope.embeddedKotlinDsl() = + plugin("org.gradle.kotlin.kotlin-dsl", kotlinDslVersion) diff --git a/build-logic-commons/gradle.properties b/build-logic-commons/gradle.properties new file mode 100644 index 0000000..288679a --- /dev/null +++ b/build-logic-commons/gradle.properties @@ -0,0 +1 @@ +systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true diff --git a/build-logic-commons/settings.gradle.kts b/build-logic-commons/settings.gradle.kts new file mode 100644 index 0000000..e32971b --- /dev/null +++ b/build-logic-commons/settings.gradle.kts @@ -0,0 +1,9 @@ +dependencyResolutionManagement { + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic-commons" + +include("gradle-plugin") diff --git a/build-logic/.gitignore b/build-logic/.gitignore new file mode 100644 index 0000000..de8b137 --- /dev/null +++ b/build-logic/.gitignore @@ -0,0 +1 @@ +/*/build/ diff --git a/build-logic/README.md b/build-logic/README.md new file mode 100644 index 0000000..8edfb1c --- /dev/null +++ b/build-logic/README.md @@ -0,0 +1,7 @@ +Build logic for Allure Gradle plugin +------------------------------------ + +This is a subset of extra plugins for factoring out +the common patterns from the common build logic. + +Note: diff --git a/build-logic/basics/build.gradle.kts b/build-logic/basics/build.gradle.kts new file mode 100644 index 0000000..72cb851 --- /dev/null +++ b/build-logic/basics/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("allure-gradle.kotlin-dsl-gradle-plugin") +} diff --git a/build-logic/basics/src/main/kotlin/allure-gradle.repositories.gradle.kts b/build-logic/basics/src/main/kotlin/allure-gradle.repositories.gradle.kts new file mode 100644 index 0000000..b965e9b --- /dev/null +++ b/build-logic/basics/src/main/kotlin/allure-gradle.repositories.gradle.kts @@ -0,0 +1,3 @@ +repositories { + mavenCentral() +} diff --git a/build-logic/basics/src/main/kotlin/allure-gradle.reproducible-builds.gradle.kts b/build-logic/basics/src/main/kotlin/allure-gradle.reproducible-builds.gradle.kts new file mode 100644 index 0000000..cab1514 --- /dev/null +++ b/build-logic/basics/src/main/kotlin/allure-gradle.reproducible-builds.gradle.kts @@ -0,0 +1,7 @@ +tasks.withType().configureEach { + // Ensure builds are reproducible + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + dirMode = "775".toInt(8) + fileMode = "664".toInt(8) +} diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 0000000..288679a --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1 @@ +systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true diff --git a/build-logic/jvm/build.gradle.kts b/build-logic/jvm/build.gradle.kts new file mode 100644 index 0000000..421dc03 --- /dev/null +++ b/build-logic/jvm/build.gradle.kts @@ -0,0 +1,19 @@ +import buildlogic.embeddedKotlinDsl +import buildlogic.plugin + +plugins { + id("allure-gradle.kotlin-dsl-gradle-plugin") +} + +group = "io.qameta.allure.buildlogic" + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(embeddedKotlinDsl()) + implementation(plugin("com.github.vlsi.gradle-extensions", "1.74")) + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin") + implementation(plugin("org.jetbrains.dokka", "1.4.32")) +} diff --git a/build-logic/jvm/src/main/kotlin/allure-gradle.dokka-javadoc.gradle.kts b/build-logic/jvm/src/main/kotlin/allure-gradle.dokka-javadoc.gradle.kts new file mode 100644 index 0000000..6d11249 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/allure-gradle.dokka-javadoc.gradle.kts @@ -0,0 +1,35 @@ +plugins { + id("org.jetbrains.dokka") +} + +// https://github.com/gradle/gradle/pull/16627 +inline fun AttributeContainer.attribute(attr: Attribute, value: String) = + attribute(attr, objects.named(value)) + +val javadocMainElements by configurations.creating { + isVisible = false + description = "Javadoc code elements" + isCanBeResolved = false + isCanBeConsumed = true + + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, Category.DOCUMENTATION) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, DocsType.JAVADOC) + attribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME) + attribute(Bundling.BUNDLING_ATTRIBUTE, Bundling.EXTERNAL) + } +} + +val javadocJar by tasks.registering(Jar::class) { + group = LifecycleBasePlugin.BUILD_GROUP + description = "Assembles a jar archive containing javadoc" + from(tasks.dokkaJavadoc) + archiveClassifier.set("javadoc") +} + +javadocMainElements.outgoing.artifact(javadocJar) + +(components["java"] as AdhocComponentWithVariants).addVariantsFromConfiguration(javadocMainElements) { + mapToOptional() + mapToMavenScope("runtime") +} diff --git a/build-logic/jvm/src/main/kotlin/allure-gradle.java.gradle.kts b/build-logic/jvm/src/main/kotlin/allure-gradle.java.gradle.kts new file mode 100644 index 0000000..e851c8b --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/allure-gradle.java.gradle.kts @@ -0,0 +1,37 @@ +import buillogic.filterEolSimple + +plugins { + `java-base` +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + inputs.property("java.version", System.getProperty("java.version")) + inputs.property("java.vm.version", System.getProperty("java.vm.version")) + options.apply { + encoding = "UTF-8" + compilerArgs.add("-Xlint:deprecation") + compilerArgs.add("-Werror") + } +} +// Add default license/notice when missing (e.g. see :src:config that overrides LICENSE) + +tasks.withType().configureEach { + into("META-INF") { + filterEolSimple("crlf") + from("$rootDir/LICENSE") + from("$rootDir/NOTICE") + } + manifest { + attributes["Bundle-License"] = "Apache-2.0" + attributes["Specification-Title"] = project.name + " " + project.description + attributes["Specification-Vendor"] = "Qameta Software" + attributes["Implementation-Vendor"] = "Qameta Software" + attributes["Implementation-Vendor-Id"] = "io.qameta.allure" + // Implementation-Version is not here to make jar reproducible across versions + } +} diff --git a/build-logic/jvm/src/main/kotlin/allure-gradle.kotlin.gradle.kts b/build-logic/jvm/src/main/kotlin/allure-gradle.kotlin.gradle.kts new file mode 100644 index 0000000..cfd4f99 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/allure-gradle.kotlin.gradle.kts @@ -0,0 +1,16 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `java-library` + id("com.github.vlsi.gradle-extensions") +} + +java { + withSourcesJar() +} + +tasks.withType { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt b/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt new file mode 100644 index 0000000..d0482b6 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt @@ -0,0 +1,16 @@ +package buillogic + +import org.apache.tools.ant.filters.FixCrLfFilter +import org.gradle.api.file.CopySpec +import org.gradle.kotlin.dsl.filter + +fun CopySpec.filterEolSimple(eol: String) { + filteringCharset = "UTF-8" + filter( + FixCrLfFilter::class, mapOf( + "eol" to FixCrLfFilter.CrLf.newInstance(eol), + "fixlast" to true, + "ctrlz" to FixCrLfFilter.AddAsisRemove.newInstance("asis") + ) + ) +} diff --git a/build-logic/publishing/build.gradle.kts b/build-logic/publishing/build.gradle.kts new file mode 100644 index 0000000..802064c --- /dev/null +++ b/build-logic/publishing/build.gradle.kts @@ -0,0 +1,20 @@ +import buildlogic.plugin + +plugins { + id("allure-gradle.kotlin-dsl-gradle-plugin") +} + +group = "io.qameta.allure.buildlogic" + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(project(":basics")) + implementation(project(":jvm")) + implementation("allure-gradle:gradle-plugin") + implementation(plugin("com.gradle.plugin-publish", "0.14.0")) + implementation(plugin("io.github.gradle-nexus.publish-plugin", "1.1.0")) + implementation("net.researchgate.release:net.researchgate.release.gradle.plugin:2.8.1") +} diff --git a/build-logic/publishing/src/main/kotlin/allure-gradle.kotlin-dsl-published-plugin.gradle.kts b/build-logic/publishing/src/main/kotlin/allure-gradle.kotlin-dsl-published-plugin.gradle.kts new file mode 100644 index 0000000..1465f52 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/allure-gradle.kotlin-dsl-published-plugin.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("com.gradle.plugin-publish") + id("allure-gradle.repositories") + id("allure-gradle.kotlin") + id("allure-gradle.kotlin-dsl-gradle-plugin") + id("allure-gradle.reproducible-builds") + id("allure-gradle.dokka-javadoc") + id("allure-gradle.publish-to-central") + id("allure-gradle.signing") +} diff --git a/build-logic/publishing/src/main/kotlin/allure-gradle.publish-to-central.gradle.kts b/build-logic/publishing/src/main/kotlin/allure-gradle.publish-to-central.gradle.kts new file mode 100644 index 0000000..dddc2a4 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/allure-gradle.publish-to-central.gradle.kts @@ -0,0 +1,68 @@ +import buildlogic.cleanupMavenPom + +plugins { + id("java-library") + id("maven-publish") +} + +val repoUrl = "https://github.com/allure-framework/allure-gradle" + +publishing { + publications.withType { + // Use the resolved versions in pom.xml + // Gradle might have different resolution rules, so we set the versions + // that were used in Gradle build/test. + versionMapping { + usage(Usage.JAVA_RUNTIME) { + fromResolutionResult() + } + usage(Usage.JAVA_API) { + fromResolutionOf("runtimeClasspath") + } + } + pom { + cleanupMavenPom() + name.set(project.description) + description.set(project.description) + inceptionYear.set("2017") + url.set(repoUrl) + organization { + name.set("Qameta IO") + url.set("https://qameta.io") + } + developers { + developer { + id.set("ehborisov") + name.set("Egor Borisov") + email.set("ehborisov@gmail.com") + } + developer { + id.set("eroshenkoam") + name.set("Artem Eroshenko") + email.set("eroshenkoam@qameta.io") + } + developer { + name.set("Vladimir Sitnikov") + id.set("vlsi") + email.set("sitnikov.vladmir@gmail.com") + } + } + issueManagement { + system.set("GitHub Issues") + url.set("$repoUrl/issues") + } + licenses { + license { + name.set("Apache-2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + scm { + connection.set("scm:git:$repoUrl.git") + developerConnection.set("scm:git:$repoUrl.git") + url.set(repoUrl) + tag.set("HEAD") + } + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/allure-gradle.signing.gradle.kts b/build-logic/publishing/src/main/kotlin/allure-gradle.signing.gradle.kts new file mode 100644 index 0000000..2715ef2 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/allure-gradle.signing.gradle.kts @@ -0,0 +1,14 @@ +plugins.withId("maven-publish") { + apply(plugin = "signing") +} + +// Developers do not always have PGP configured, +// so activate signing for release versions only +// Just in case Maven Central rejects signed snapshots for some reason +plugins.withId("signing") { + if (!version.toString().endsWith("-SNAPSHOT") && System.getenv("JITPACK")?.toBoolean() != true) { + configure { + sign(the().publications) + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/cleanupMavenPom.kt b/build-logic/publishing/src/main/kotlin/buildlogic/cleanupMavenPom.kt new file mode 100644 index 0000000..39c6d3c --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/cleanupMavenPom.kt @@ -0,0 +1,24 @@ +package buildlogic + +import org.gradle.api.publish.maven.MavenPom + +fun MavenPom.cleanupMavenPom() { + withXml { + val sb = asString() + var s = sb.toString() + // compile is Maven default, so delete it + s = s.replace("compile", "") + // Cut because all dependencies have the resolved versions + s = s.replace( + Regex( + ".*?", + RegexOption.DOT_MATCHES_ALL + ), + "" + ) + sb.setLength(0) + sb.append(s) + // Re-format the XML + asNode() + } +} diff --git a/build-logic/root-build/build.gradle.kts b/build-logic/root-build/build.gradle.kts new file mode 100644 index 0000000..2f742e9 --- /dev/null +++ b/build-logic/root-build/build.gradle.kts @@ -0,0 +1,15 @@ +import buildlogic.plugin + +plugins { + id("allure-gradle.kotlin-dsl-gradle-plugin") +} + +group = "io.qameta.allure.buildlogic" + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(plugin("io.github.gradle-nexus.publish-plugin", "1.1.0")) +} diff --git a/build-logic/root-build/src/main/kotlin/allure-gradle.root-build.gradle.kts b/build-logic/root-build/src/main/kotlin/allure-gradle.root-build.gradle.kts new file mode 100644 index 0000000..358d455 --- /dev/null +++ b/build-logic/root-build/src/main/kotlin/allure-gradle.root-build.gradle.kts @@ -0,0 +1,18 @@ +import io.github.gradlenexus.publishplugin.NexusPublishExtension + +plugins { + base +// id("net.researchgate.release") +} + +//project.release { +// tagTemplate = version.toString() +//} + +// io.github.gradle-nexus.publish-plugin is not compatible with precompiled Gradle plugins, +// so the plugin is applied in the root project, and here we configure it +extensions.findByType()?.apply { + repositories { + sonatype() + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..a04a4bd --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,13 @@ +dependencyResolutionManagement { + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic" +includeBuild("../build-logic-commons") + +include("basics") +include("jvm") +include("publishing") +include("root-build") diff --git a/build.gradle b/build.gradle deleted file mode 100644 index c293172..0000000 --- a/build.gradle +++ /dev/null @@ -1,84 +0,0 @@ -group = 'io.qameta.allure' - -buildscript { - repositories { - jcenter() - maven { url "https://plugins.gradle.org/m2/" } - } - - dependencies { - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.gradle.publish:plugin-publish-plugin:0.10.0' - classpath 'net.researchgate:gradle-release:2.8.0' - classpath 'ru.vyarus:gradle-quality-plugin:3.3.0' - } -} - -apply plugin: 'java' -apply plugin: 'groovy' -apply plugin: 'java-gradle-plugin' -apply plugin: 'ru.vyarus.quality' - -apply from: "${rootProject.projectDir}/gradle/plugin-publish.gradle" -apply from: "${rootProject.projectDir}/gradle/maven-publish.gradle" -apply from: "${rootProject.projectDir}/gradle/bintray.gradle" -apply from: "${rootProject.projectDir}/gradle/release.gradle" - -gradlePlugin { - plugins { - allurePlugin { - id = 'io.qameta.allure' - implementationClass = 'io.qameta.allure.gradle.AllurePlugin' - } - } -} - -compileJava { - sourceCompatibility = 1.7 - targetCompatibility = 1.7 - options.encoding = 'UTF-8' -} - -compileTestJava { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 - options.encoding = 'UTF-8' -} - -repositories { - mavenCentral() -} - -configurations { - junit5Plugin -} - -dependencies { - compile localGroovy() - - testCompile gradleTestKit() - testCompile 'junit:junit:4.12' - testCompile 'commons-io:commons-io:2.5' - testCompile 'org.assertj:assertj-core:3.6.2' - testCompile 'org.apache.commons:commons-text:1.1' - - junit5Plugin 'org.junit.platform:junit-platform-gradle-plugin:1.0.0' - testRuntime files(testPluginClasspath(sourceSets.main.runtimeClasspath.files + configurations.junit5Plugin.files)) -} - -quality { - pmdVersion = '5.5.4' - checkstyleVersion = '7.6' - findbugsVersion = '3.0.1' - codenarcVersion = '0.26.0' -} - -def testPluginClasspath(Collection collections) { - def generatedDir = new File(buildDir, "generated") - def pluginsClasspathFile = new File(generatedDir, "plugin-classpath.txt") - - generatedDir.mkdirs() - - pluginsClasspathFile.text = collections.join("\n") - return generatedDir -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..1973bc1 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + id("allure-gradle.root-build") + id("io.github.gradle-nexus.publish-plugin") +} + +group = "io.qameta.allure" diff --git a/gradle.properties b/gradle.properties index a9903a1..2c11adb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,5 @@ -version=2.9-SNAPSHOT \ No newline at end of file +org.gradle.parallel=true +systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true +kotlin.code.style=official + +version=2.9-SNAPSHOT diff --git a/gradle/bintray.gradle b/gradle/bintray.gradle deleted file mode 100644 index 10c23da..0000000 --- a/gradle/bintray.gradle +++ /dev/null @@ -1,33 +0,0 @@ -apply plugin: 'com.jfrog.bintray' - -bintray { - user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER') - key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY') - - configurations = ['archives'] - - publish = true - pkg { - userOrg = 'qameta' - repo = 'maven' - name = 'allure-gradle' - desc = 'Allure Gradle integration' - websiteUrl = 'https://github.com/allure-framework/allure-gradle' - issueTrackerUrl = 'https://github.com/allure-framework/allure-gradle' - vcsUrl = 'https://github.com/allure-framework/allure-gradle.git' - - labels = ['allure', 'report' ,'gradle', 'plugin'] - licenses = ['Apache-2.0'] - - githubRepo = 'allure-framework/allure-gradle' - githubReleaseNotesFile = 'README.md' - - version { - name = project.version - released = new Date() - gpg { - sign = true - } - } - } -} \ No newline at end of file diff --git a/gradle/maven-publish.gradle b/gradle/maven-publish.gradle deleted file mode 100644 index 4b62ea4..0000000 --- a/gradle/maven-publish.gradle +++ /dev/null @@ -1,60 +0,0 @@ -apply plugin: 'maven' - -install { - repositories.mavenInstaller { - customizePom(pom, project) - } -} - -def customizePom(pom, gradleProject) { - pom.whenConfigured { generatedPom -> - // eliminate test-scoped dependencies (no need in maven central poms) - generatedPom.dependencies.removeAll { dep -> - dep.scope == "test" - } - - // sort to make pom dependencies order consistent to ease comparison of older poms - generatedPom.dependencies = generatedPom.dependencies.sort { dep -> - "$dep.scope:$dep.groupId:$dep.artifactId" - } - - // add all items necessary for maven central publication - generatedPom.project { - name = gradleProject.description - description = gradleProject.description - url = "https://github.com/allure-framework/allure-gradle" - organization { - name = "Qameta IO" - url = "https://qameta.io" - } - licenses { - license { - name = 'The Apache Software License, Version 2.0' - url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' - distribution = 'repo' - } - } - scm { - url = 'https://github.com/allure-framework/allure-gradle' - connection = 'scm:git:git://github.com/allure-framework/allure-gradle' - developerConnection = 'scm:git:git://github.com/allure-framework/allure-gradle' - } - developers { - developer { - id = 'ehborisov' - name = 'Egor Borisov' - email = 'ehborisov@gmail.com' - } - developer { - id = 'eroshenkoam' - name = 'Artem Eroshenko' - email = 'eroshenkoam@qameta.io' - } - } - issueManagement { - system = 'Github Issues' - url = 'https://github.com/allure-framework/allure-gradle/issues' - } - } - } -} diff --git a/gradle/plugin-publish.gradle b/gradle/plugin-publish.gradle deleted file mode 100644 index 5410aed..0000000 --- a/gradle/plugin-publish.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: "com.gradle.plugin-publish" - -pluginBundle { - website = 'https://github.com/allure-framework/allure-gradle' - vcsUrl = 'https://github.com/allure-framework/allure-gradle.git' - description = 'Allure Gradle integration' - tags = ['allure', 'report', 'gradle', 'plugin'] - - plugins { - allurePlugin { - id = 'io.qameta.allure' - displayName = 'allure-gradle' - } - } - - mavenCoordinates { - groupId = project.group - artifactId = project.name - version = project.version - } - -} \ No newline at end of file diff --git a/gradle/release.gradle b/gradle/release.gradle deleted file mode 100644 index 2dc319d..0000000 --- a/gradle/release.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'net.researchgate.release' - -release { - tagTemplate = '${version}' -} - -afterReleaseBuild.dependsOn('bintrayUpload', 'publishPlugins') \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7957be8..e708b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0209450..63fded4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jan 18 19:37:27 MSK 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip +distributionSha256Sum=7faa7198769f872826c8ef4f1450f839ec27f0b4d5d1e51bade63667cbccd205 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip diff --git a/gradlew b/gradlew index cccdd3d..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -66,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -109,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -138,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/plugins.txt b/plugins.txt new file mode 100644 index 0000000..3e33b52 --- /dev/null +++ b/plugins.txt @@ -0,0 +1,6 @@ + plugins { + id("ru.vyarus.quality") version "4.5.0" + id("com.gradle.plugin-publish") version "0.14.0" + id("com.github.vlsi.gradle-extensions") version "1.74" + } + diff --git a/sandbox/README.md b/sandbox/README.md new file mode 100644 index 0000000..71f2485 --- /dev/null +++ b/sandbox/README.md @@ -0,0 +1,28 @@ +Allure Gradle Plugin Sandbox +---------------------------- + +This is a sandbox project that simplifies Allure Gradle Plugin development. + +You could try DSL in `dsl-groovy` and `dsl-kotlin` projects, and you see the full +IDE experience of the plugin. + +The `allure-gradle` plugin is imported via Gradle's `includeBuild`, so +you do not need to build and publish the plugin before trying the plugin. + +# Known issues + +https://github.com/gradle/gradle/issues/16979 + +``` +Execution failed for task ':xxxx-xxxxxx-build:spring-boot:generatePrecompiledScriptPluginAccessors'. +> Could not create service of type ScriptHandlerInternal using ProjectScopeServices.createScriptHandler(). + > Cannot create service of type DependencyLockingHandler using method DefaultDependencyManagementServices$DependencyResolutionScopeServices.createDependencyLockingHandler() as there is a problem with parameter #2 of type ConfigurationContainerInternal. + > Cannot create service of type ConfigurationContainerInternal using method DefaultDependencyManagementServices$DependencyResolutionScopeServices.createConfigurationContainer() as there is a problem with parameter #2 of type ConfigurationResolver. + > Cannot create service of type ConfigurationResolver using method DefaultDependencyManagementServices$DependencyResolutionScopeServices.createDependencyResolver() as there is a problem with parameter #1 of type ArtifactDependencyResolver. + > Cannot create service of type ArtifactDependencyResolver using method DependencyManagementBuildScopeServices.createArtifactDependencyResolver() as there is a problem with parameter #4 of type List. + > Could not create service of type VersionControlRepositoryConnectionFactory using VersionControlBuildSessionServices.createVersionControlSystemFactory(). + > Failed to create parent directory '/.gradle' when creating directory '/.gradle/vcs-1' +``` + +Solution: run Gradle task manually + diff --git a/sandbox/build.gradle.kts b/sandbox/build.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/sandbox/dsl-groovy/build.gradle b/sandbox/dsl-groovy/build.gradle new file mode 100644 index 0000000..fa8e59d --- /dev/null +++ b/sandbox/dsl-groovy/build.gradle @@ -0,0 +1,12 @@ +plugins { + id("io.qameta.allure") +} + +allure { + gather { + + } + report { + + } +} diff --git a/sandbox/dsl-kotlin/build.gradle.kts b/sandbox/dsl-kotlin/build.gradle.kts new file mode 100644 index 0000000..fcc3fbd --- /dev/null +++ b/sandbox/dsl-kotlin/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("io.qameta.allure") +} + +allure { + gather.adapters.spock.enabled.set(true) + gather { + allureJavaVersion.set("213") + adapters { + junit5 + cucumber2Jvm { + } + } + } + report { + } +} diff --git a/sandbox/gradle.properties b/sandbox/gradle.properties new file mode 100644 index 0000000..efe6e00 --- /dev/null +++ b/sandbox/gradle.properties @@ -0,0 +1,5 @@ +org.gradle.parallel=true +systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true +kotlin.code.style=official + +version=1.0 diff --git a/sandbox/gradle/wrapper/gradle-wrapper.jar b/sandbox/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/sandbox/gradle/wrapper/gradle-wrapper.jar differ diff --git a/sandbox/gradle/wrapper/gradle-wrapper.properties b/sandbox/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e095a6d --- /dev/null +++ b/sandbox/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionSha256Sum=eb8b89184261025b0430f5b2233701ff1377f96da1ef5e278af6ae8bac5cc305 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip diff --git a/sandbox/gradlew b/sandbox/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/sandbox/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/sandbox/gradlew.bat b/sandbox/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/sandbox/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/sandbox/settings.gradle.kts b/sandbox/settings.gradle.kts new file mode 100644 index 0000000..905beaa --- /dev/null +++ b/sandbox/settings.gradle.kts @@ -0,0 +1,15 @@ +import org.gradle.util.GradleVersion + +rootProject.name = "allure-gradle-sandbox" + +// Gradle 6.x needs to see all the included buidls :-/ +// https://github.com/gradle/gradle/issues/17061#issuecomment-836330587 +if (GradleVersion.current() < GradleVersion.version("7.0")) { + includeBuild("../build-logic-commons") + includeBuild("../build-logic") +} + +includeBuild("../") + +include("dsl-kotlin") +include("dsl-groovy") diff --git a/sandbox/src/main/kotlin/allure-gradle.build-logic.reproducible-builds.gradle.kts b/sandbox/src/main/kotlin/allure-gradle.build-logic.reproducible-builds.gradle.kts new file mode 100644 index 0000000..cab1514 --- /dev/null +++ b/sandbox/src/main/kotlin/allure-gradle.build-logic.reproducible-builds.gradle.kts @@ -0,0 +1,7 @@ +tasks.withType().configureEach { + // Ensure builds are reproducible + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + dirMode = "775".toInt(8) + fileMode = "664".toInt(8) +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index a561471..0000000 --- a/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'allure-gradle' - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..67b213d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,44 @@ +import de.fayard.refreshVersions.bootstrapRefreshVersions + +pluginManagement { + repositories { + gradlePluginPortal() + } +} + +plugins { + `gradle-enterprise` +} + +buildscript { + repositories { + gradlePluginPortal() + } + // See https://jmfayard.github.io/refreshVersions/setup/ + dependencies.classpath("de.fayard.refreshVersions:refreshVersions:0.9.7") +} + +bootstrapRefreshVersions() + +val isCiServer = System.getenv().containsKey("CI") + +if (isCiServer) { + gradleEnterprise { + buildScan { + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + tag("CI") + } + } +} + +rootProject.name = "allure-gradle" + +includeBuild("build-logic-commons") +includeBuild("build-logic") + +include("allure-base-plugin") +include("allure-gather-plugin") +include("allure-report-plugin") +include("allure-plugin") +include("testkit-junit4") diff --git a/src/it/categories/build.gradle b/src/it/categories/build.gradle deleted file mode 100644 index 5181bd3..0000000 --- a/src/it/categories/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' -} diff --git a/src/it/cucumber-jvm/build.gradle b/src/it/cucumber-jvm/build.gradle deleted file mode 100644 index 581f2c3..0000000 --- a/src/it/cucumber-jvm/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' - aspectjweaver = true - - useCucumberJVM { - version = '2.0-BETA21' - } -} - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - compile 'commons-io:commons-io:2.5' - compile 'info.cukes:gherkin:2.12.2' - compile 'info.cukes:cucumber-core:1.2.5' - compile 'info.cukes:cucumber-java:1.2.5' - compile 'info.cukes:cucumber-junit:1.2.5' - - testCompile 'junit:junit:4.12' -} - -test { - useJUnit() -} \ No newline at end of file diff --git a/src/it/cucumber2-jvm/build.gradle b/src/it/cucumber2-jvm/build.gradle deleted file mode 100644 index e1f4238..0000000 --- a/src/it/cucumber2-jvm/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' - aspectjweaver = true - - useCucumber2JVM { - version = '2.0-BETA21' - } -} - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - compile 'commons-io:commons-io:2.5' - compile 'info.cukes:gherkin:2.12.2' - compile 'io.cucumber:cucumber-core:2.3.1' - compile 'io.cucumber:cucumber-java:2.3.1' - compile 'io.cucumber:cucumber-junit:2.3.1' - - testCompile 'junit:junit:4.12' -} - -test { - useJUnit() -} \ No newline at end of file diff --git a/src/it/junit5-native/build.gradle b/src/it/junit5-native/build.gradle deleted file mode 100644 index c410019..0000000 --- a/src/it/junit5-native/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -repositories { - mavenCentral() - jcenter() -} - -allure { - version = '2.4.1' - aspectjweaver = true - - useJUnit5 { - version = "2.0-BETA21" - } -} - -test { - useJUnitPlatform() -} - -dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2' -} diff --git a/src/it/junit5/build.gradle b/src/it/junit5/build.gradle deleted file mode 100644 index 75b21cc..0000000 --- a/src/it/junit5/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -plugins { - id 'java' - id 'org.junit.platform.gradle.plugin' - id 'io.qameta.allure' -} - -repositories { - mavenCentral() - jcenter() -} - -allure { - version = '2.4.1' - aspectjweaver = true - - useJUnit5 { - version = "2.0-BETA21" - } -} - -junitPlatform { - enableStandardTestTask true -} - -dependencies { - testCompile 'org.junit.jupiter:junit-jupiter-engine:5.0.0' -} diff --git a/src/it/junit5/src/test/java/tests/Junit5Test.java b/src/it/junit5/src/test/java/tests/Junit5Test.java deleted file mode 100644 index 6c4e60e..0000000 --- a/src/it/junit5/src/test/java/tests/Junit5Test.java +++ /dev/null @@ -1,27 +0,0 @@ -package tests; - -import io.qameta.allure.Attachment; -import io.qameta.allure.Step; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class Junit5Test { - - @Test - public void testWithAttachment() { - stepMethod(); - assertTrue(true); - } - - @Step("step") - public void stepMethod() { - attachment(); - } - - @Attachment(value = "attachment", type = "text/plain") - public String attachment() { - return "

HELLO

"; - } - -} \ No newline at end of file diff --git a/src/it/junit5/src/test/resources/allure.properties b/src/it/junit5/src/test/resources/allure.properties deleted file mode 100644 index cb77d0a..0000000 --- a/src/it/junit5/src/test/resources/allure.properties +++ /dev/null @@ -1 +0,0 @@ -allure.results.directory=build/allure-results \ No newline at end of file diff --git a/src/it/report-bintray-task/build.gradle b/src/it/report-bintray-task/build.gradle deleted file mode 100644 index 5181bd3..0000000 --- a/src/it/report-bintray-task/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' -} diff --git a/src/it/report-central-task/build.gradle b/src/it/report-central-task/build.gradle deleted file mode 100644 index ef62380..0000000 --- a/src/it/report-central-task/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.8.0' -} diff --git a/src/it/report-central-task/build/allure-results/simple-result.json b/src/it/report-central-task/build/allure-results/simple-result.json deleted file mode 100644 index 291100d..0000000 --- a/src/it/report-central-task/build/allure-results/simple-result.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "uuid": "e8f6fd85-b5aa-4adc-afc7-d101e47537c6", - "historyId": "1d9b83988bf21cd8c54ca5f4cf3cedec", - "fullName": "FirstTest.testOutput", - "labels": [], - "links": [], - "name": "testOutput", - "status": "passed", - "statusDetails": { - "known": false, - "muted": false, - "flaky": false - }, - "stage": "finished", - "start": 1497973191961, - "stop": 1497973192003, - "steps": [], - "parameters": [] -} \ No newline at end of file diff --git a/src/it/report-multi/build.gradle b/src/it/report-multi/build.gradle deleted file mode 100644 index baa9f93..0000000 --- a/src/it/report-multi/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allprojects { project -> - - apply plugin: 'io.qameta.allure' - - allure { - version = '2.4.1' - } -} - -import io.qameta.allure.gradle.task.AllureReport - -task allureAggregatedReport(type: AllureReport) { - resultsDirs = subprojects.allure.resultsDir -} diff --git a/src/it/report-multi/module1/build.gradle b/src/it/report-multi/module1/build.gradle deleted file mode 100644 index fcb5531..0000000 --- a/src/it/report-multi/module1/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() - jcenter() -} diff --git a/src/it/report-multi/module2/build.gradle b/src/it/report-multi/module2/build.gradle deleted file mode 100644 index fcb5531..0000000 --- a/src/it/report-multi/module2/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() - jcenter() -} diff --git a/src/it/report-only/build.gradle b/src/it/report-only/build.gradle deleted file mode 100644 index 3c86cca..0000000 --- a/src/it/report-only/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' -} diff --git a/src/it/report-only/build/allure-results/simple-result.json b/src/it/report-only/build/allure-results/simple-result.json deleted file mode 100644 index 291100d..0000000 --- a/src/it/report-only/build/allure-results/simple-result.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "uuid": "e8f6fd85-b5aa-4adc-afc7-d101e47537c6", - "historyId": "1d9b83988bf21cd8c54ca5f4cf3cedec", - "fullName": "FirstTest.testOutput", - "labels": [], - "links": [], - "name": "testOutput", - "status": "passed", - "statusDetails": { - "known": false, - "muted": false, - "flaky": false - }, - "stage": "finished", - "start": 1497973191961, - "stop": 1497973192003, - "steps": [], - "parameters": [] -} \ No newline at end of file diff --git a/src/it/test-finalized-by-report/build.gradle b/src/it/test-finalized-by-report/build.gradle deleted file mode 100644 index 0d948ea..0000000 --- a/src/it/test-finalized-by-report/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' -} - -test { - finalizedBy 'allureReport' -} diff --git a/src/it/test-finalized-by-report/build/allure-results/simple-result.json b/src/it/test-finalized-by-report/build/allure-results/simple-result.json deleted file mode 100644 index 291100d..0000000 --- a/src/it/test-finalized-by-report/build/allure-results/simple-result.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "uuid": "e8f6fd85-b5aa-4adc-afc7-d101e47537c6", - "historyId": "1d9b83988bf21cd8c54ca5f4cf3cedec", - "fullName": "FirstTest.testOutput", - "labels": [], - "links": [], - "name": "testOutput", - "status": "passed", - "statusDetails": { - "known": false, - "muted": false, - "flaky": false - }, - "stage": "finished", - "start": 1497973191961, - "stop": 1497973192003, - "steps": [], - "parameters": [] -} \ No newline at end of file diff --git a/src/it/testng-spi-off/build.gradle b/src/it/testng-spi-off/build.gradle deleted file mode 100644 index e884136..0000000 --- a/src/it/testng-spi-off/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id 'java' - id 'io.qameta.allure' -} - -allure { - version = '2.4.1' - aspectjweaver = true - - useTestNG { - version = '2.0-BETA21' - spiOff = true - } -} - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - testCompile 'org.testng:testng:6.8' -} - -test { - useTestNG() - systemProperty("allure.results.directory", project.buildDir) -} diff --git a/src/main/groovy/io/qameta/allure/gradle/AllureExtension.groovy b/src/main/groovy/io/qameta/allure/gradle/AllureExtension.groovy deleted file mode 100644 index 469f4cf..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/AllureExtension.groovy +++ /dev/null @@ -1,92 +0,0 @@ -package io.qameta.allure.gradle - -import groovy.transform.CompileStatic -import io.qameta.allure.gradle.config.Cucumber2JVMConfig -import io.qameta.allure.gradle.config.CucumberJVMConfig -import io.qameta.allure.gradle.config.JUnit4Config -import io.qameta.allure.gradle.config.JUnit5Config -import io.qameta.allure.gradle.config.SpockConfig -import io.qameta.allure.gradle.config.TestNGConfig -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.reporting.ReportingExtension - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -@CompileStatic -class AllureExtension extends ReportingExtension { - - public static final String NAME = 'allure' - - boolean autoconfigure = false - - boolean aspectjweaver - - File resultsDir - - File reportDir - - String allureJavaVersion = '2.0-BETA21' - - String configuration = 'testCompile' - - String aspectjVersion = '1.9.5' - - protected TestNGConfig testNGConfig - - void useTestNG(Action action) { - testNGConfig = new TestNGConfig() - action.execute(testNGConfig) - } - - protected JUnit4Config junit4Config - - void useJUnit4(Action action) { - junit4Config = new JUnit4Config() - action.execute(junit4Config) - } - - protected JUnit5Config junit5Config - - void useJUnit5(Action action) { - junit5Config = new JUnit5Config() - action.execute(junit5Config) - } - - protected CucumberJVMConfig cucumberJVMConfig - - void useCucumberJVM(Action action) { - cucumberJVMConfig = new CucumberJVMConfig() - action.execute(cucumberJVMConfig) - } - - protected Cucumber2JVMConfig cucumber2JVMConfig - - void useCucumber2JVM(Action action) { - cucumber2JVMConfig = new Cucumber2JVMConfig() - action.execute(cucumber2JVMConfig) - } - - protected SpockConfig spockConfig - - void useSpock(Action action) { - spockConfig = new SpockConfig() - action.execute(spockConfig) - } - - String version - - String downloadLinkFormat - - String downloadLink - - boolean clean = true - - AllureExtension(Project project) { - super(project) - this.resultsDir = new File(project.buildDir, '/allure-results') - this.reportDir = new File(baseDir as File, '/allure-report') - } - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/AllurePlugin.groovy b/src/main/groovy/io/qameta/allure/gradle/AllurePlugin.groovy deleted file mode 100644 index adf7b79..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/AllurePlugin.groovy +++ /dev/null @@ -1,151 +0,0 @@ -package io.qameta.allure.gradle - -import groovy.transform.CompileStatic -import io.qameta.allure.gradle.task.AllureReport -import io.qameta.allure.gradle.task.AllureServe -import io.qameta.allure.gradle.task.DownloadAllure -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.internal.tasks.testing.junit.JUnitTestFramework -import org.gradle.api.internal.tasks.testing.testng.TestNGTestFramework -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.testing.Test -import org.gradle.process.JavaForkOptions - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -@CompileStatic -class AllurePlugin implements Plugin { - - private static final String CONFIGURATION_ASPECTJ_WEAVER = 'aspectjWeaverAgent' - private static final String ALLURE_DIR_PROPERTY = 'allure.results.directory' - private static final String JUNIT4_ASPECT_DEPENDENCY = 'io.qameta.allure:allure-junit4-aspect:' - private static final String JUNIT4 = 'JUnit4' - - // @formatter:off - private static final Map ADAPTER_DEPENDENCIES = - [ - 'TestNG': 'io.qameta.allure:allure-testng:', - 'JUnit4': 'io.qameta.allure:allure-junit4:', - 'JUnit5': 'io.qameta.allure:allure-junit5:', - 'Spock': 'io.qameta.allure:allure-spock:', - 'CucumberJVM': 'io.qameta.allure:allure-cucumber-jvm:', - 'Cucumber2JVM': 'io.qameta.allure:allure-cucumber2-jvm:', - ] - // @formatter:on - - // @formatter:off - private static final Map TEST_FRAMEWORKS = - [ - 'TestNG': TestNGTestFramework, - 'JUnit4': JUnitTestFramework, - ] - // @formatter:on - - private Project project - - @Override - void apply(Project project) { - this.project = project - AllureExtension extension = project.extensions.create(AllureExtension.NAME, AllureExtension, project) - - project.afterEvaluate { - if (extension.autoconfigure) { - autoconfigure(extension) - } - applyAdapters(extension) - applyAspectjweaver(extension) - - configureTestTasks(extension) - - if (extension?.version) { - project.evaluationDependsOnChildren() - project.tasks.create(AllureServe.NAME, AllureServe) - project.tasks.create(AllureReport.NAME, AllureReport) - project.tasks.create(DownloadAllure.NAME, DownloadAllure) - } - } - } - - private void autoconfigure(AllureExtension extension) { - TEST_FRAMEWORKS.each { name, framework -> - boolean apply = project.tasks.withType(Test).any { Test task -> framework.isInstance(task.testFramework) } - if (apply) { - project.logger.debug("Allure autoconfiguration: $name test framework found") - String dependencyString = ADAPTER_DEPENDENCIES[name] + extension.allureJavaVersion - project.dependencies.add(extension.configuration, dependencyString) - if (name == JUNIT4) { - String aspectDependencyString = JUNIT4_ASPECT_DEPENDENCY + extension.allureJavaVersion - project.dependencies.add(extension.configuration, aspectDependencyString) - } - } - } - } - - private applyAdapters(AllureExtension ext) { - if (ext.testNGConfig) { - addAdapterDependency(ext, ext.testNGConfig.name, ext.testNGConfig.version, ext.testNGConfig.spiOff) - } - if (ext.junit4Config) { - addAdapterDependency(ext, ext.junit4Config.name, ext.junit4Config.version, false) - project.dependencies.add(ext.configuration, JUNIT4_ASPECT_DEPENDENCY + ext.junit4Config.version) - } - if (ext.junit5Config) { - addAdapterDependency(ext, ext.junit5Config.name, ext.junit5Config.version, false) - } - if (ext.cucumberJVMConfig) { - addAdapterDependency(ext, ext.cucumberJVMConfig.name, ext.cucumberJVMConfig.version, false) - } - if (ext.cucumber2JVMConfig) { - addAdapterDependency(ext, ext.cucumber2JVMConfig.name, ext.cucumber2JVMConfig.version, false) - } - if (ext.spockConfig) { - addAdapterDependency(ext, ext.spockConfig.name, ext.spockConfig.version, false) - } - } - - private void addAdapterDependency(AllureExtension extension, String name, String version, boolean spiOff) { - String dependencyString = ADAPTER_DEPENDENCIES[name] + version - if (spiOff) { - dependencyString += ':spi-off' - } - project.dependencies.add(extension.configuration, dependencyString) - } - - private void configureTestTasks(AllureExtension ext) { - configureTestTasks { Task task, JavaForkOptions test -> - task.outputs.dir(ext.resultsDir) - test.systemProperty(ALLURE_DIR_PROPERTY, ext.resultsDir) - } - } - - private void applyAspectjweaver(AllureExtension ext) { - if (ext.aspectjweaver || ext.autoconfigure) { - Configuration aspectjConfiguration = project.configurations.maybeCreate(CONFIGURATION_ASPECTJ_WEAVER) - - project.dependencies.add(CONFIGURATION_ASPECTJ_WEAVER, "org.aspectj:aspectjweaver:${ext.aspectjVersion}") - - String javaAgent = "-javaagent:${aspectjConfiguration.singleFile}" - - configureTestTasks { Task task, JavaForkOptions test -> - task.doFirst { - test.jvmArgs = [javaAgent] + test.jvmArgs as Iterable - } - if (project.logger.debugEnabled) { - project.logger.debug "jvmArgs for task $task.name $test.jvmArgs" - } - } - } - } - - private void configureTestTasks(Closure closure) { - [project.tasks.withType(Test), junitPlatformPluginTestTasks()].flatten().each { closure(it, it) } - } - - private Collection junitPlatformPluginTestTasks() { - project.tasks.withType(JavaExec).findAll { JavaExec task -> task.name == 'junitPlatformTest' } - } -} diff --git a/src/main/groovy/io/qameta/allure/gradle/AllureReportContainer.groovy b/src/main/groovy/io/qameta/allure/gradle/AllureReportContainer.groovy deleted file mode 100644 index e6e970d..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/AllureReportContainer.groovy +++ /dev/null @@ -1,19 +0,0 @@ -package io.qameta.allure.gradle - -import org.gradle.api.Task -import org.gradle.api.reporting.ConfigurableReport -import org.gradle.api.reporting.Report -import org.gradle.api.reporting.internal.TaskGeneratedSingleDirectoryReport -import org.gradle.api.reporting.internal.TaskReportContainer - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class AllureReportContainer extends TaskReportContainer { - - AllureReportContainer(Task task) { - super(ConfigurableReport, task) - add(TaskGeneratedSingleDirectoryReport, 'allure-report', task, 'index.html') - } - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/Cucumber2JVMConfig.groovy b/src/main/groovy/io/qameta/allure/gradle/config/Cucumber2JVMConfig.groovy deleted file mode 100644 index 9595e37..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/Cucumber2JVMConfig.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Andrejs Cunskis andrejs.cunskis@gmail.com - */ -class Cucumber2JVMConfig { - - String name = 'Cucumber2JVM' - - String version - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/CucumberJVMConfig.groovy b/src/main/groovy/io/qameta/allure/gradle/config/CucumberJVMConfig.groovy deleted file mode 100644 index f1ca03b..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/CucumberJVMConfig.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class CucumberJVMConfig { - - String name = 'CucumberJVM' - - String version - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/JUnit4Config.groovy b/src/main/groovy/io/qameta/allure/gradle/config/JUnit4Config.groovy deleted file mode 100644 index cf87e7d..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/JUnit4Config.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class JUnit4Config { - - String name = 'JUnit4' - - String version - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/JUnit5Config.groovy b/src/main/groovy/io/qameta/allure/gradle/config/JUnit5Config.groovy deleted file mode 100644 index 0d6eee1..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/JUnit5Config.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class JUnit5Config { - - String name = 'JUnit5' - - String version - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/SpockConfig.groovy b/src/main/groovy/io/qameta/allure/gradle/config/SpockConfig.groovy deleted file mode 100644 index 95f8ea4..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/SpockConfig.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class SpockConfig { - - String name = 'Spock' - - String version - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/config/TestNGConfig.groovy b/src/main/groovy/io/qameta/allure/gradle/config/TestNGConfig.groovy deleted file mode 100644 index 3cb62e8..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/config/TestNGConfig.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package io.qameta.allure.gradle.config - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class TestNGConfig { - - String name = 'TestNG' - - String version - - boolean spiOff -} diff --git a/src/main/groovy/io/qameta/allure/gradle/task/AllureReport.groovy b/src/main/groovy/io/qameta/allure/gradle/task/AllureReport.groovy deleted file mode 100644 index aed46f2..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/task/AllureReport.groovy +++ /dev/null @@ -1,101 +0,0 @@ -package io.qameta.allure.gradle.task - -import io.qameta.allure.gradle.AllureExtension -import io.qameta.allure.gradle.AllureReportContainer -import io.qameta.allure.gradle.util.BuildUtils -import org.gradle.api.Action -import org.gradle.api.DefaultTask -import org.gradle.api.internal.ClosureBackedAction -import org.gradle.api.reporting.Reporting -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction - -import java.nio.file.Files -import java.nio.file.Path - -import static io.qameta.allure.gradle.util.BuildUtils.copyCategoriesInfo -import static io.qameta.allure.gradle.util.BuildUtils.copyExecutorInfo - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class AllureReport extends DefaultTask implements Reporting { - - static final String NAME = 'allureReport' - - static final String GENERATE_COMMAND = 'generate' - - @OutputDirectory - File reportDir - - @Input - boolean clean - - @Input - String version - - @InputFiles - List resultsDirs = [] - - AllureReport() { - dependsOn(DownloadAllure.NAME) - configureDefaults() - } - - @TaskAction - void generateAllureReport() { - Path allureHome = project.rootDir.toPath().resolve('.allure').resolve("allure-${version}") - Path allureExecutable = allureHome.resolve('bin').resolve(BuildUtils.allureExecutable).toAbsolutePath() - - if (Files.notExists(allureExecutable)) { - logger.warn("Cannot find allure commandline in $allureHome") - return - } - - allureExecutable.toFile().setExecutable(true) - project.exec { - commandLine("$allureExecutable") - args(GENERATE_COMMAND) - resultsDirs.each { - copyCategoriesInfo(it, project) - copyExecutorInfo(it, project) - args("$it.absolutePath") - } - args('-o', "$reportDir.absolutePath") - if (clean) { - args('--clean') - } - } - } - - private void configureDefaults() { - AllureExtension allureExtension = project.extensions.findByType(AllureExtension) - if (Objects.nonNull(extensions)) { - resultsDirs.add(allureExtension.resultsDir) - reportDir = allureExtension.reportDir - version = allureExtension.version - clean = allureExtension.clean - } - } - - @Internal - @Override - AllureReportContainer getReports() { - return new AllureReportContainer(this) - } - - @Override - AllureReportContainer reports(Closure closure) { - return reports(new ClosureBackedAction(closure)) - } - - @Override - AllureReportContainer reports(Action configureAction) { - configureAction.execute(reports) - return reports - } - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/task/AllureServe.groovy b/src/main/groovy/io/qameta/allure/gradle/task/AllureServe.groovy deleted file mode 100644 index 25ca195..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/task/AllureServe.groovy +++ /dev/null @@ -1,64 +0,0 @@ -package io.qameta.allure.gradle.task - -import io.qameta.allure.gradle.AllureExtension -import io.qameta.allure.gradle.util.BuildUtils -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.TaskAction - -import java.nio.file.Files -import java.nio.file.Path - -import static io.qameta.allure.gradle.util.BuildUtils.copyCategoriesInfo -import static io.qameta.allure.gradle.util.BuildUtils.copyExecutorInfo - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class AllureServe extends DefaultTask { - - static final String NAME = 'allureServe' - - static final String SERVE_COMMAND = 'serve' - - @Input - String version - - @InputFiles - List resultsDirs = [] - - AllureServe() { - configureDefaults() - } - - @TaskAction - void serveAllureReport() { - Path allureHome = project.rootDir.toPath().resolve('.allure').resolve("allure-${version}") - Path allureExecutable = allureHome.resolve('bin').resolve(BuildUtils.allureExecutable).toAbsolutePath() - - if (Files.notExists(allureExecutable)) { - logger.warn("Cannot find allure commandline in $allureHome") - return - } - - allureExecutable.toFile().setExecutable(true) - project.exec { - commandLine("$allureExecutable") - args(SERVE_COMMAND) - resultsDirs.each { - copyCategoriesInfo(it, project) - copyExecutorInfo(it, project) - args("$it.absolutePath") - } - } - } - - void configureDefaults() { - AllureExtension allureExtension = project.extensions.findByType(AllureExtension) - if (Objects.nonNull(extensions)) { - resultsDirs.add(allureExtension.resultsDir) - version = allureExtension.version - } - } -} diff --git a/src/main/groovy/io/qameta/allure/gradle/task/DownloadAllure.groovy b/src/main/groovy/io/qameta/allure/gradle/task/DownloadAllure.groovy deleted file mode 100644 index c463730..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/task/DownloadAllure.groovy +++ /dev/null @@ -1,53 +0,0 @@ -package io.qameta.allure.gradle.task - -import io.qameta.allure.gradle.AllureExtension -import io.qameta.allure.gradle.util.DownloadUtils -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class DownloadAllure extends DefaultTask { - - static final String NAME = 'downloadAllure' - - @Input - @Optional - String version - - @Input - String src - - @OutputDirectory - File dest - - DownloadAllure() { - configureDefaults() - } - - @TaskAction - downloadAllure() { - File buildDir = project.buildDir - dest.mkdirs() - buildDir.mkdirs() - String archiveDest = buildDir.absolutePath + "/allure-${version}.zip" - project.ant { - get(src: src, dest: archiveDest, skipexisting: 'true') - unzip(src: archiveDest, dest: dest) - } - } - - private void configureDefaults() { - AllureExtension extension = project.extensions.getByType(AllureExtension) - String url = DownloadUtils.getAllureDownloadUrl(extension.version, extension.downloadLinkFormat) - if (Objects.nonNull(extensions)) { - version = extension.version - dest = project.file(new File(project.rootDir, '.allure').absolutePath) - src = extension.downloadLink ?: url - } - } -} diff --git a/src/main/groovy/io/qameta/allure/gradle/util/BuildUtils.groovy b/src/main/groovy/io/qameta/allure/gradle/util/BuildUtils.groovy deleted file mode 100644 index 5cc534c..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/util/BuildUtils.groovy +++ /dev/null @@ -1,57 +0,0 @@ -package io.qameta.allure.gradle.util - -import groovy.json.JsonOutput -import org.gradle.api.Project -import org.gradle.api.tasks.SourceSetContainer - -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - -/** - * @author Egor Borisov ehborisov@gmail.com - */ -class BuildUtils { - - public static final String TEST_SOURCE_SET = 'test' - - public static final String EXECUTOR_FILE_NAME = 'executor.json' - - public static final String CATEGORIES_FILE_NAME = 'categories.json' - - static boolean isWindows() { - return System.getProperty('os.name').toLowerCase().contains('win') - } - - static String getAllureExecutable() { - return isWindows() ? 'allure.bat' : 'allure' - } - - static void copyExecutorInfo(File resultsDir, Project project) { - Map executorInfo = [name: 'Gradle', type: 'gradle', buildName: project.displayName] - - Path resultsPath = Paths.get(resultsDir.absoluteFile.path) - Files.createDirectories(resultsPath) - - Path executorPath = resultsPath.resolve(EXECUTOR_FILE_NAME) - Files.write(executorPath, JsonOutput.toJson(executorInfo).getBytes(StandardCharsets.UTF_8)) - } - - static void copyCategoriesInfo(File resultsDir, Project project) { - SourceSetContainer sourceSets = (SourceSetContainer) project.properties.get('sourceSets') - if (sourceSets != null && sourceSets.names.contains(TEST_SOURCE_SET)) { - List resourcesCategoriesFiles = sourceSets.getByName(TEST_SOURCE_SET).resources - .findAll { file -> (file.name == CATEGORIES_FILE_NAME) } - - Path resultsPath = Paths.get(resultsDir.absoluteFile.path) - Files.createDirectories(resultsPath) - - if (!resourcesCategoriesFiles.isEmpty()) { - Path categoriesPath = resultsPath.resolve(CATEGORIES_FILE_NAME) - Files.write(categoriesPath, resourcesCategoriesFiles.first().text.getBytes(StandardCharsets.UTF_8)) - } - } - } - -} diff --git a/src/main/groovy/io/qameta/allure/gradle/util/DownloadUtils.java b/src/main/groovy/io/qameta/allure/gradle/util/DownloadUtils.java deleted file mode 100644 index 868f15d..0000000 --- a/src/main/groovy/io/qameta/allure/gradle/util/DownloadUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.qameta.allure.gradle.util; - -/** - * @author eroshenkoam (Artem Eroshenko). - */ -public final class DownloadUtils { - - private DownloadUtils() { - } - - private static final String BINTRAY_TEMPLATE = "https://dl.bintray.com/qameta/generic/io/qameta/allure/allure/%s/allure-%s.zip"; - - private static final String CENTRAL_TEMPLATE = "https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/%s/allure-commandline-%s.zip"; - - public static String getAllureDownloadUrl(final String version, final String downloadUrl) { - if (downloadUrl != null) { - return String.format(downloadUrl, version, version); - } - if (versionCompare(version, "2.8.0") < 0) { - return String.format(BINTRAY_TEMPLATE, version, version); - } else { - return String.format(CENTRAL_TEMPLATE, version, version); - } - } - - private static Integer versionCompare(String first, String second) { - String[] firstVersions = first.split("\\."); - String[] secondVersions = second.split("\\."); - int i = 0; - while (i < firstVersions.length && i < secondVersions.length && firstVersions[i].equals(secondVersions[i])) { - i++; - } - if (i < firstVersions.length && i < secondVersions.length) { - int diff = Integer.valueOf(firstVersions[i]).compareTo(Integer.valueOf(secondVersions[i])); - return Integer.signum(diff); - } else { - return Integer.signum(firstVersions.length - secondVersions.length); - } - } - - -} diff --git a/src/main/resources/META-INF/gradle-plugins/io.qameta.allure.properties b/src/main/resources/META-INF/gradle-plugins/io.qameta.allure.properties deleted file mode 100644 index c268ca7..0000000 --- a/src/main/resources/META-INF/gradle-plugins/io.qameta.allure.properties +++ /dev/null @@ -1 +0,0 @@ -implementation-class = io.qameta.allure.gradle.AllurePlugin diff --git a/src/test/java/io/qameta/allure/gradle/SingleReportTest.java b/src/test/java/io/qameta/allure/gradle/SingleReportTest.java deleted file mode 100644 index 368b53a..0000000 --- a/src/test/java/io/qameta/allure/gradle/SingleReportTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.qameta.allure.gradle; - -import io.qameta.allure.gradle.rule.GradleRunnerRule; -import org.gradle.testkit.runner.BuildResult; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; - -@RunWith(Parameterized.class) -public class SingleReportTest { - - @Parameterized.Parameter(0) - public String version; - - @Parameterized.Parameter(1) - public String project; - - @Parameterized.Parameter(2) - public String[] tasks; - - @Rule - public GradleRunnerRule gradleRunner = new GradleRunnerRule() - .version(() -> version) - .project(() -> project) - .tasks(() -> tasks); - - @Parameterized.Parameters(name = "{1} [{0}]") - public static Collection getFrameworks() { - return Arrays.asList( - new Object[]{"3.5", "src/it/test-finalized-by-report", new String[]{"test"}}, - new Object[]{"3.5", "src/it/report-bintray-task", new String[]{"allureReport"}}, - new Object[]{"4.0", "src/it/report-bintray-task", new String[]{"allureReport"}}, - new Object[]{"5.0", "src/it/report-bintray-task", new String[]{"allureReport"}}, - new Object[]{"3.5", "src/it/report-central-task", new String[]{"allureReport"}}, - new Object[]{"4.0", "src/it/report-central-task", new String[]{"allureReport"}}, - new Object[]{"5.0", "src/it/report-central-task", new String[]{"allureReport"}} - ); - } - - @Test - public void shouldGenerateAllureReport() { - BuildResult buildResult = gradleRunner.getBuildResult(); - File resultsDir = new File(gradleRunner.getProjectDir(), "build/allure-results"); - - assertThat(buildResult.getTasks()).as("Download allure task status") - .filteredOn(task -> task.getPath().equals(":downloadAllure")) - .extracting("outcome") - .containsExactly(SUCCESS); - - assertThat(resultsDir.listFiles()).as("Allure executor info") - .filteredOn(file -> file.getName().endsWith("executor.json")) - .hasSize(1); - - File projectDir = gradleRunner.getProjectDir(); - File reportDir = new File(projectDir, "build/reports/allure-report"); - assertThat(reportDir).as("Allure report directory") - .exists(); - - File testCasesDir = new File(reportDir, "data/test-cases"); - assertThat(testCasesDir.listFiles()).as("Allure test cases directory") - .isNotEmpty(); - - } -} diff --git a/src/test/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java b/src/test/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java deleted file mode 100644 index fd25299..0000000 --- a/src/test/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.qameta.allure.gradle.rule; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.gradle.testkit.runner.BuildResult; -import org.gradle.testkit.runner.GradleRunner; -import org.junit.rules.ExternalResource; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; - - -/** - * eroshenkoam - * 20.06.17 - */ -public class GradleRunnerRule extends ExternalResource { - - private Supplier projectSupplier; - - private Supplier tasksSupplier; - - private Supplier versionSupplier; - - private BuildResult buildResult; - - private File projectDir; - - public BuildResult getBuildResult() { - return this.buildResult; - } - - public File getProjectDir() { - return this.projectDir; - } - - public GradleRunnerRule version(String version) { - return version(() -> version); - } - - public GradleRunnerRule version(Supplier version) { - this.versionSupplier = version; - return this; - } - - public GradleRunnerRule project(Supplier supplier) { - this.projectSupplier = supplier; - return this; - } - - public GradleRunnerRule project(String project) { - return project(() -> project); - } - - public GradleRunnerRule tasks(Supplier supplier) { - this.tasksSupplier = supplier; - return this; - } - - public GradleRunnerRule tasks(String... tasks) { - return tasks(() -> tasks); - } - - - protected void before() throws Throwable { - projectDir = copyProject(projectSupplier.get()); - new File(projectDir, "settings.gradle").createNewFile(); - buildResult = GradleRunner.create() - .withProjectDir(projectDir) - .withArguments(tasksSupplier.get()) - .withGradleVersion(versionSupplier.get()) - .withTestKitDir(new File(projectDir.getParentFile().getAbsolutePath(), ".gradle")) - .withPluginClasspath(readPluginClasspath()) - .forwardOutput() - .build(); - } - - private static File copyProject(String project) { - try { - File to = new File("build/gradle-testkit", randomAlphabetic(8)); - File from = new File(project); - FileUtils.copyDirectory(from, to); - return to; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private static List readPluginClasspath() throws IOException { - try (InputStream stream = GradleRunnerRule.class.getClassLoader().getResourceAsStream("plugin-classpath.txt")) { - return IOUtils.readLines(stream, StandardCharsets.UTF_8).stream() - .map(File::new).collect(Collectors.toList()); - } - } - -} diff --git a/testkit-junit4/build.gradle.kts b/testkit-junit4/build.gradle.kts new file mode 100644 index 0000000..23e5d38 --- /dev/null +++ b/testkit-junit4/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("allure-gradle.java") + `java-library` +} + +group = "io.qameta.allure.gradle.testkit" + +repositories { + mavenCentral() +} + +dependencies { + api("junit:junit:_") + + implementation(gradleTestKit()) + implementation("commons-io:commons-io:_") + implementation("org.apache.commons:commons-text:_") +} + diff --git a/testkit-junit4/src/main/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java b/testkit-junit4/src/main/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java new file mode 100644 index 0000000..7520db0 --- /dev/null +++ b/testkit-junit4/src/main/java/io/qameta/allure/gradle/rule/GradleRunnerRule.java @@ -0,0 +1,183 @@ +package io.qameta.allure.gradle.rule; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.gradle.api.JavaVersion; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.util.GradleVersion; +import org.junit.Assume; +import org.junit.rules.ExternalResource; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; + + +/** + * JUnit4 rule for executing Gradle tests in a temporary folder. + */ +public class GradleRunnerRule extends ExternalResource { + + private Supplier projectSupplier; + + private Supplier tasksSupplier; + + private Supplier versionSupplier; + + private BuildResult buildResult; + + private File projectDir; + + public BuildResult getBuildResult() { + return this.buildResult; + } + + public File getProjectDir() { + return this.projectDir; + } + + public GradleRunnerRule version(String version) { + return version(() -> version); + } + + public GradleRunnerRule version(Supplier version) { + this.versionSupplier = version; + return this; + } + + public GradleRunnerRule project(Supplier supplier) { + this.projectSupplier = supplier; + return this; + } + + public GradleRunnerRule project(String project) { + return project(() -> project); + } + + public GradleRunnerRule tasks(Supplier supplier) { + this.tasksSupplier = supplier; + return this; + } + + public GradleRunnerRule tasks(String... tasks) { + return tasks(() -> tasks); + } + + static class JavaGradle { + final JavaVersion javaVersion; + final GradleVersion gradleVersion; + + JavaGradle(JavaVersion javaVersion, String gradleVersion) { + this.javaVersion = javaVersion; + this.gradleVersion = GradleVersion.version(gradleVersion); + } + } + + protected void before() throws Throwable { + projectDir = copyProject(projectSupplier.get()); + new File(projectDir, "settings.gradle").createNewFile(); + String gradleVersion = versionSupplier.get(); + GradleVersion testGradle = GradleVersion.version(gradleVersion); + + // tasks.named requires Gradle 5.0+ + // Configuration avoidance tasks.register requires Gradle 4.9+ + if (testGradle.compareTo(GradleVersion.version("5.0")) < 0) { + Assume.assumeTrue("Gradle " + testGradle + " is not supported, please upgrade to 5.0+", false); + } + + Optional gradleRequirement = Stream.of( + new JavaGradle(JavaVersion.VERSION_16, "7.0"), + new JavaGradle(JavaVersion.VERSION_15, "6.7"), + new JavaGradle(JavaVersion.VERSION_14, "6.3"), + new JavaGradle(JavaVersion.VERSION_11, "5.0"), + new JavaGradle(JavaVersion.VERSION_1_8, "2.0")) + .filter(v -> v.javaVersion.compareTo(JavaVersion.current()) <= 0) + .findFirst(); + if (!gradleRequirement.isPresent()) { + throw new IllegalStateException("Running with Java " + JavaVersion.current().getMajorVersion() + " is not supported yet"); + } + + checkGradleCompatibility(gradleRequirement.get(), testGradle); + + List args = new ArrayList<>(); + args.add("--stacktrace"); + args.add("--info"); + if (testGradle.compareTo(GradleVersion.version("7.0")) >= 0) { + // Disable file watching since it prevents file removal on Windows + // See https://github.com/gradle/gradle/pull/16977 + // FS watch is disabled to avoid test flakiness + args.add("--no-watch-fs"); + } + args.addAll(Arrays.asList(tasksSupplier.get())); + + buildResult = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments(args) + .withGradleVersion(gradleVersion) + .withTestKitDir(new File(projectDir.getParentFile().getAbsolutePath(), ".gradle")) + .withPluginClasspath() + .forwardOutput() + .build(); + } + + @Override + protected void after() { + try { + FileUtils.forceDelete(projectDir); + } catch (IOException e) { + throw new RuntimeException("Unable to remove temporary project directory " + projectDir, e); + } + } + + private void checkGradleCompatibility(JavaGradle javaGradle, GradleVersion testGradle) { + JavaVersion javaVersion = javaGradle.javaVersion; + GradleVersion minimalGradle = javaGradle.gradleVersion; + JavaVersion currentJava = JavaVersion.current(); + boolean configurationSupported = + // E.g. if the current Java is 12, then "Java 16 is supported in 7.0" does not help us, and we assume true + currentJava.compareTo(javaVersion) < 0 + // If the current Java is 12, and we know that "Java 11 was supported since Gradle 5.0", + // then we require current Gradle to be 5.0+ + || testGradle.compareTo(minimalGradle) >= 0; + if (configurationSupported) { + return; + } + String skipMessage = "Java " + javaVersion.getMajorVersion() + " requires Gradle " + minimalGradle + "+, current Java is " + currentJava + + ", test Gradle version is " + testGradle + ", so will skip the test"; + System.out.println(skipMessage); + Assume.assumeTrue(skipMessage, configurationSupported); + } + + private static File copyProject(String project) { + String projectName = StringUtils.substringAfterLast(project.replace('\\', '/'), '/'); + if (!projectName.isEmpty()) { + projectName += "-"; + } + File to = new File("build/gradle-testkit", projectName + randomAlphabetic(8)); + File from = new File(project); + try { + if (!from.isDirectory() || !from.exists()) { + throw new IllegalArgumentException("Directory " + project + " is not found " + + "(full path is " + from.getAbsolutePath() + ")"); + } + FileUtils.copyDirectory(from, to); + return to; + } catch (IOException e) { + throw new RuntimeException("Unable to copy " + project + + " (full path is " + from.getAbsolutePath() + ") to " + to, e); + } + } + +} diff --git a/versions.properties b/versions.properties new file mode 100644 index 0000000..dd1b2fb --- /dev/null +++ b/versions.properties @@ -0,0 +1,12 @@ +plugin.ru.vyarus.quality=4.5.0 +plugin.com.gradle.plugin-publish=0.14.0 +plugin.com.github.vlsi.gradle-extensions=1.74 +plugin.io.github.gradle-nexus.publish-plugin=1.1.0 + +version.junit.junit=4.13.2 + +version.commons-io..commons-io=2.8.0 + +version.org.apache.commons..commons-text=1.9 + +version.org.assertj..assertj-core=3.19.0