diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1ef0730 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 330628f..d83c96d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Detekt" uses: burrunan/gradle-cache-action@v1 @@ -22,8 +22,16 @@ jobs: save-local-build-cache: false arguments: detekt + - name: "Upload sarif report" + uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: build/reports/detekt/detekt.sarif + category: detekt + - name: "Upload detekt report" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.0.0 + if: success() || failure() with: name: detektReport path: build/reports/detekt @@ -33,7 +41,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Fossa analyze" uses: fossas/fossa-action@v1 @@ -51,7 +59,7 @@ jobs: name: "Build artifact" steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Compile" uses: burrunan/gradle-cache-action@v1 @@ -64,7 +72,7 @@ jobs: needs: compile steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Validate plugin" uses: burrunan/gradle-cache-action@v1 @@ -77,7 +85,7 @@ jobs: needs: compile steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Unit test coverage cache" uses: actions/cache@v2 @@ -102,7 +110,7 @@ jobs: files: build/test-results/**/*.xml - name: "Upload unit test report" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.0.0 if: always() with: name: unitTestReport @@ -113,7 +121,7 @@ jobs: needs: compile steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Functional test coverage cache" uses: actions/cache@v2 @@ -136,7 +144,7 @@ jobs: files: build/test-results/functionalTest/*.xml - name: "Upload functional test report" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.0.0 if: always() with: name: functionalTestReport @@ -149,7 +157,7 @@ jobs: - functionalTest steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Load unit test coverage cache" uses: actions/cache@v2 @@ -171,7 +179,7 @@ jobs: arguments: jacocoTestReport - name: "Publish Report" - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3.0.0 publishSnapshot: runs-on: ubuntu-latest @@ -181,7 +189,7 @@ jobs: - licenceCheck steps: - name: "Check out sources" - uses: actions/checkout@v2 + uses: actions/checkout@v3.0.2 - name: "Load compile cache" uses: actions/cache@v2 @@ -189,11 +197,13 @@ jobs: path: ${{ github.workspace }}/build/** key: ${{ runner.os }}-compile-${{ github.run_id }} - - name: "Publish snapshot" + - name: "Publish snapshot to github" uses: burrunan/gradle-cache-action@v1 env: USERNAME: ${{ github.actor }} TOKEN: ${{ github.token }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MVN_CNTRL_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MVN_CNTRL_SECRET_KEY_PASS }} with: remote-build-cache-proxy-enabled: false save-local-build-cache: false diff --git a/.github/workflows/craft_new_release.yml b/.github/workflows/craft_new_release.yml index 856e3a7..329058c 100644 --- a/.github/workflows/craft_new_release.yml +++ b/.github/workflows/craft_new_release.yml @@ -18,7 +18,7 @@ jobs: name: "Craft a new release" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.0.2 - name: "Get next version" id: nextVersion @@ -53,7 +53,7 @@ jobs: git push origin release/${{ steps.nextVersion.outputs.version }} - name: Create pull request - uses: thomaseizinger/create-pull-request@1.0.0 + uses: thomaseizinger/create-pull-request@1.2.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/publish_new_release.yml b/.github/workflows/publish_new_release.yml index c18c5a8..6719c46 100644 --- a/.github/workflows/publish_new_release.yml +++ b/.github/workflows/publish_new_release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.merged == true #only merged pull requests must trigger this job steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.0.2 - name: Extract version from branch name (for release branches) if: startsWith(github.event.pull_request.head.ref, 'release/') @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-latest needs: release steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3.0.2 with: ref: main @@ -64,6 +64,8 @@ jobs: env: TOKEN: ${{ secrets.GITHUB_TOKEN }} USERNAME: ${{ github.actor }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MVN_CNTRL_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MVN_CNTRL_SECRET_KEY_PASS }} with: remote-build-cache-proxy-enabled: false save-local-build-cache: false @@ -74,6 +76,8 @@ jobs: env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MVN_CNTRL_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MVN_CNTRL_SECRET_KEY_PASS }} with: remote-build-cache-proxy-enabled: false save-local-build-cache: false diff --git a/.github/workflows/vulnerabilityScan.yml b/.github/workflows/vulnerabilityScan.yml new file mode 100644 index 0000000..8ba94db --- /dev/null +++ b/.github/workflows/vulnerabilityScan.yml @@ -0,0 +1,43 @@ +name: "ODC-Scan for vulnerabilities" + +on: + schedule: + # on a weekly basis for new identified issues + - cron: '0 16 * * 5' # 16 o'clock UTC every friday + push: + paths: + # if build script changes which may introduce new issues + - build.gradle.kts + +jobs: + check: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - uses: actions/checkout@v3.0.2 + + - name: "Run owasp check" + uses: burrunan/gradle-cache-action@v1 + with: + remote-build-cache-proxy-enabled: false + arguments: dependencyCheckAnalyze + + - name: "Upload sarif report" + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: build/reports/dependency-check-report.sarif + category: 'owasp dependency check' + + - name: 'pack reports' + uses: edgarrc/action-7z@v1 + with: + args: 7z a -t7z dependency-check-report ./build/reports/dependency-c*.* + + - name: "Upload report" + uses: actions/upload-artifact@v3.0.0 + if: always() + with: + name: owaspCheckReport + path: dependency-check-report.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 929d980..cdf004d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] ### Added +- added support for properties `-r` and `-X` for remote test execution (#50) + +### Fixed +- Fixed issue with UP-TO-DATE check which let tasks fail after SetupTask was executed once (#48) + +## [2.3.0] +### Added - Added additional cli-arguments for proxy-settings (#40) ### Fixed diff --git a/README.adoc b/README.adoc index 7aea7d6..876b75f 100644 --- a/README.adoc +++ b/README.adoc @@ -9,9 +9,12 @@ endif::[] ifndef::env-github[] :icons: font endif::[] -:jm_tm: https://jmeter.apache.org/[JMeter(TM),window=_blank] -:jm_cli: https://jmeter.apache.org/usermanual/get-started.html#override -:gh_rp: https://github.com/qualersoft/jmeter-gradle-plugin +:jm_base: https://jmeter.apache.org +:jm_tm: {jm_base}/[JMeter(TM),window=_blank] +:jm_doc_base: {jm_base}/usermanual +:jm_remoteing: {jm_doc_base}/remote-test.html +:jm_cli: {jm_doc_base}/get-started.html#override +:gh_rp: https://github.com/qualersoft/jmeter-gradle-plugin :toc: preamble [cols="1,~", frame=none, grid=none] @@ -86,6 +89,9 @@ jmeter { reportDir // <.> maxHeap // <.> jvmArgs //<.> + + enableRemoteExecution // <.> + exitRemoteServers // <.> } ---- [%collapsible] @@ -114,6 +120,10 @@ Defaults to /test-results/jmeter Defaults to /reports/jmeter <.> [Optional] Specifies the maximum heap size the JVM process will start with. <.> [Optional] additional JVM arguments that will be passed to the jvm directly. +<.> [Optional] tells {jm_tm} to run the tests on the configured remote-servers (see {jm_remoteing}[remoting]) + +Defaults to `false`. +<.> [Optional] Flag to exit remote servers at the end of the test. Only effective iff `enableRemoteExecution` is `true`. + +Defaults to `false`. ==== These configurations will be applied to all tasks (where appropriate) and can be overridden or extended on a per task base. diff --git a/build.gradle.kts b/build.gradle.kts index 9d3892c..10f0550 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,23 +1,28 @@ import de.qualersoft.parseSemVer +import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.owasp.dependencycheck.gradle.extension.AnalyzerExtension +import org.owasp.dependencycheck.gradle.extension.RetireJSExtension +import org.owasp.dependencycheck.reporting.ReportGenerator.Format plugins { // implementation - `java-gradle-plugin` - kotlin("jvm") version "1.6.10" + kotlin("jvm") version "1.7.10" // quality jacoco id("pl.droidsonroids.jacoco.testkit") version "1.0.9" - id("io.gitlab.arturbosch.detekt") version "1.18.0" + id("io.gitlab.arturbosch.detekt") version "1.21.0" + id("org.owasp.dependencycheck") version "7.1.1" // documentation - id("org.jetbrains.dokka") version "1.5.0" + id("org.jetbrains.dokka") version "1.7.10" id("org.asciidoctor.jvm.convert") version "3.3.2" // publishing - `maven-publish` - id("com.gradle.plugin-publish") version "0.15.0" - id("org.jetbrains.changelog") version "1.3.0" + signing + id("com.gradle.plugin-publish") version "1.0.0" + id("org.jetbrains.changelog") version "1.3.1" } group = "de.qualersoft" @@ -30,17 +35,28 @@ dependencies { // Align versions of all Kotlin components implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - testImplementation(platform("org.junit:junit-bom:5.7.2")) + testImplementation(platform("org.junit:junit-bom:5.8.2")) testImplementation(group = "org.junit.jupiter", name = "junit-jupiter") - testImplementation(group = "io.kotest", name = "kotest-runner-junit5", version = "4.6.4") + testImplementation(group = "io.kotest", name = "kotest-assertions-core", version = "5.3.2") testRuntimeOnly(kotlin("script-runtime")) + + // quality + detektPlugins(group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version = "1.21.0") { + because("We also want to check formatting issues.") + } } // Add a source set for the functional test suite val functionalTestSourceSet: SourceSet = sourceSets.create("functionalTest") configurations["functionalTestImplementation"].extendsFrom(configurations["testImplementation"]) +pluginBundle { + website = "https://github.com/qualersoft/jmeter-gradle-plugin" + vcsUrl = "https://github.com/qualersoft/jmeter-gradle-plugin" + tags = listOf("jmeter", "test", "performance") +} + gradlePlugin { // Define the plugin plugins.create("jmeter") { @@ -53,27 +69,29 @@ gradlePlugin { testSourceSets(sourceSets.test.get(), functionalTestSourceSet) } -pluginBundle { - website = "https://github.com/qualersoft/jmeter-gradle-plugin" - vcsUrl = "https://github.com/qualersoft/jmeter-gradle-plugin" - tags = listOf("jmeter", "test", "performance") -} - jacoco { toolVersion = "0.8.7" } detekt { allRules = false - buildUponDefaultConfig = true - config = files("$projectDir/detekt.yml") - source = files("src/main/kotlin") + source = files("src") + config = files("detekt.yml") + basePath = project.projectDir.path +} - reports { - html.enabled = true - xml.enabled = true - txt.enabled = false - } +dependencyCheck { + suppressionFile = file("config/dependencyCheck/suppressions.xml").path + formats = listOf( + Format.HTML, + Format.SARIF + ) + analyzers(closureOf { + assemblyEnabled = false // requires 'dotnet' executable which is not present everywhere + retirejs(closureOf { + enabled = false // because there seams to be an issue with RetireJS + }) + }) } if (project.version.toString().endsWith("-SNAPSHOT", true)) { @@ -83,8 +101,6 @@ if (project.version.toString().endsWith("-SNAPSHOT", true)) { val javaVersion = JavaVersion.VERSION_1_8 java { targetCompatibility = javaVersion - withSourcesJar() - withJavadocJar() } // Configure gradle-changelog-plugin plugin. @@ -96,7 +112,7 @@ changelog { tasks { - withType().configureEach { + withType().configureEach { kotlinOptions { jvmTarget = javaVersion.toString() } @@ -106,9 +122,16 @@ tasks { enableStricterValidation.set(true) } - this.detekt { + withType().configureEach { // Target version of the generated JVM bytecode. It is used for type resolution. - this.jvmTarget = javaVersion.toString() + jvmTarget = javaVersion.toString() + reports { + html.required.set(true) + xml.required.set(true) + txt.required.set(false) + md.required.set(false) + sarif.required.set(true) + } } // Setup functional test sets @@ -173,6 +196,12 @@ publishing { } } +signing { + val signingKey: String? by project + val signingPassword: String? by project + useInMemoryPgpKeys(signingKey, signingPassword) +} + val KINDS = listOf("major", "minor", "patch", "snapshot") tasks.register("nextVersion") { doLast { diff --git a/config/dependencyCheck/suppressions.xml b/config/dependencyCheck/suppressions.xml new file mode 100644 index 0000000..ebcaec0 --- /dev/null +++ b/config/dependencyCheck/suppressions.xml @@ -0,0 +1,17 @@ + + + + + pkg:maven/com.google.code.gson/gson@2.8.8 + CVE-2022-25647 + + + + pkg:maven/com.google.protobuf/protobuf-java@2.6.1 + CVE-2021-22569 + + diff --git a/detekt.yml b/detekt.yml index a9c9f49..2dc4a85 100644 --- a/detekt.yml +++ b/detekt.yml @@ -33,18 +33,21 @@ processors: console-reports: active: true exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' -# - 'FindingsReport' - - 'FileBasedFindingsReport' + - 'ProjectStatisticsReport' +# - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' +# - 'LiteFindingsReport' output-reports: active: true exclude: - # - 'TxtOutputReport' - # - 'XmlOutputReport' - # - 'HtmlOutputReport' + - 'TxtOutputReport' +# - 'XmlOutputReport' +# - 'HtmlOutputReport' + - 'MdOutputReport' +# - 'SarifOutputReport' comments: active: true @@ -61,6 +64,14 @@ comments: EndOfSentenceFormat: active: false endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: false + excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: false + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: false UndocumentedPublicClass: active: false excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -110,23 +121,32 @@ complexity: LongMethod: active: true threshold: 60 - ignoreAnnotated: [] LongParameterList: active: true functionThreshold: 6 constructorThreshold: 7 ignoreDefaultParameters: false ignoreDataClasses: true - ignoreAnnotated: [] + ignoreAnnotatedParameter: [] MethodOverloading: active: false threshold: 6 NamedArguments: active: false threshold: 3 + ignoreArgumentsMatchingNames: false NestedBlockDepth: active: true threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' ReplaceSafeCallChainWithRun: active: false StringLiteralDuplication: @@ -152,12 +172,20 @@ coroutines: active: true GlobalCoroutineUsage: active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' RedundantSuspendModifier: - active: false + active: true SleepInsteadOfDelay: + active: true + SuspendFunWithCoroutineScopeReceiver: active: false SuspendFunWithFlowReturnType: - active: false + active: true empty-blocks: active: true @@ -204,7 +232,7 @@ exceptions: - 'hashCode' - 'toString' InstanceOfCheckForException: - active: false + active: true excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] NotImplementedDeclaration: active: false @@ -269,7 +297,6 @@ exceptions: formatting: active: true android: false - autoCorrect: true AnnotationOnSeparateLine: active: false autoCorrect: true @@ -350,6 +377,7 @@ formatting: autoCorrect: true NoWildcardImports: active: true + excludes: ['**/*.gradle.kts'] PackageName: active: true autoCorrect: true @@ -407,6 +435,7 @@ naming: active: false excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] allowedPattern: '^(is|has|are)' + ignoreOverridden: true ClassNaming: active: true excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -440,8 +469,6 @@ naming: functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' excludeClassPattern: '$^' ignoreOverridden: true - ignoreAnnotated: - - 'Composable' FunctionParameterNaming: active: true excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -449,9 +476,12 @@ naming: excludeClassPattern: '$^' ignoreOverridden: true InvalidPackageDeclaration: - active: false - excludes: ['**/*.kts'] + active: true rootPackage: '' + requireRootInDeclaration: false + LambdaParameterNaming: + active: false + parameterPattern: '[a-z][A-Za-z0-9]*|_' MatchingDeclarationName: active: true mustBeFirst: true @@ -459,7 +489,7 @@ naming: active: true ignoreOverridden: true NoNameShadowing: - active: false + active: true NonBooleanPropertyPrefixedWithIs: active: false excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -499,6 +529,9 @@ performance: active: true ArrayPrimitive: active: true + CouldBeSequence: + active: false + threshold: 3 ForEachOnRange: active: true excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] @@ -511,7 +544,7 @@ performance: potential-bugs: active: true AvoidReferentialEquality: - active: false + active: true forbiddenTypePatterns: - 'kotlin.String' CastToNullableType: @@ -522,8 +555,19 @@ potential-bugs: active: true DoubleMutabilityForCollection: active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' DuplicateCaseInWhenExpression: active: true + ElseCaseInsteadOfExhaustiveWhen: + active: false EqualsAlwaysReturnsTrueOrFalse: active: true EqualsWithHashCodeExist: @@ -533,15 +577,16 @@ potential-bugs: ExplicitGarbageCollectionCall: active: true HasPlatformType: - active: false + active: true IgnoredReturnValue: - active: false + active: true restrictToAnnotatedMethods: true returnValueAnnotations: - '*.CheckResult' - '*.CheckReturnValue' ignoreReturnValueAnnotations: - '*.CanIgnoreReturnValue' + ignoreFunctionCall: [] ImplicitDefaultLocale: active: true ImplicitUnitReturnType: @@ -556,13 +601,17 @@ potential-bugs: LateinitUsage: active: false excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] - excludeAnnotatedProperties: [] ignoreOnClassesPattern: '' MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: active: false + excludes: ['**/*.kts'] MissingWhenCase: active: true allowElseExpression: true + NullCheckOnMutableProperty: + active: true NullableToStringCall: active: true RedundantElseInWhen: @@ -574,7 +623,7 @@ potential-bugs: UnnecessarySafeCall: active: true UnreachableCatchBlock: - active: false + active: true UnreachableCode: active: true UnsafeCallOnNullableType: @@ -582,14 +631,19 @@ potential-bugs: UnsafeCast: active: true UnusedUnaryOperator: - active: false + active: true UselessPostfixExpression: - active: false + active: true WrongEqualsTypeParameter: active: true style: active: true + CanBeNonNullable: + active: true + CascadingCallWrapping: + active: true + includeElvis: true ClassOrdering: active: false CollapsibleIfStatements: @@ -600,7 +654,7 @@ style: DataClassShouldBeImmutable: active: false DestructuringDeclarationWithTooManyEntries: - active: false + active: true maxDestructuringEntries: 3 EqualsNullCall: active: true @@ -621,6 +675,7 @@ style: - 'STOPSHIP:' - 'TODO:' allowedPatterns: '\wBUG' + customMessage: '' ForbiddenImport: active: false imports: [] @@ -636,8 +691,11 @@ style: ignorePackages: - '*.internal' - '*.internal.*' - ForbiddenVoid: + ForbiddenSuppress: active: false + rules: [] + ForbiddenVoid: + active: true ignoreOverridden: false ignoreUsageInGenerics: false FunctionOnlyReturningConstant: @@ -645,8 +703,6 @@ style: ignoreOverridableFunction: true ignoreActualFunction: true excludedFunctions: '' - excludeAnnotatedFunction: - - 'dagger.Provides' LibraryCodeMustSpecifyReturnType: active: true excludes: ['**'] @@ -678,6 +734,9 @@ style: active: true MandatoryBracesLoops: active: true + MaxChainedCallsOnSameLine: + active: true + maxChainedCalls: 5 MaxLineLength: active: true maxLineLength: 120 @@ -696,8 +755,10 @@ style: active: true NoTabs: active: true + NullableBooleanCheck: + active: true ObjectLiteralToLambda: - active: false + active: true OptionalAbstractKeyword: active: true OptionalUnit: @@ -711,7 +772,7 @@ style: RedundantExplicitType: active: false RedundantHigherOrderMapUsage: - active: false + active: true RedundantVisibilityModifierRule: active: false ReturnCount: @@ -735,19 +796,22 @@ style: active: false UnderscoresInNumericLiterals: active: false - acceptableDecimalLength: 5 + acceptableLength: 5 + allowNonStandardGrouping: false UnnecessaryAbstractClass: active: true - excludeAnnotatedClasses: - - 'dagger.Module' UnnecessaryAnnotationUseSiteTarget: active: false UnnecessaryApply: active: true + UnnecessaryBackticks: + active: true UnnecessaryFilter: active: true UnnecessaryInheritance: active: true + UnnecessaryInnerClass: + active: false UnnecessaryLet: active: false UnnecessaryParentheses: @@ -761,15 +825,16 @@ style: UnusedPrivateMember: active: true allowedNames: '(_|ignored|expected|serialVersionUID)' + UseAnyOrNoneInsteadOfFind: + active: true UseArrayLiteralsInAnnotations: - active: false + active: true UseCheckNotNull: active: true UseCheckOrError: - active: false + active: true UseDataClass: active: false - excludeAnnotatedClasses: [] allowVars: false UseEmptyCounterpart: active: false @@ -778,22 +843,22 @@ style: UseIfInsteadOfWhen: active: false UseIsNullOrEmpty: - active: false + active: true UseOrEmpty: - active: false + active: true UseRequire: - active: false + active: true UseRequireNotNull: - active: false + active: true UselessCallOnNotNull: active: true UtilityClassWithPublicConstructor: active: true VarCouldBeVal: active: true + ignoreLateinitVar: false WildcardImport: active: true excludes: ['**/test/**', '**/functionalTest/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] excludeImports: - 'java.util.*' - - 'kotlinx.android.synthetic.*' diff --git a/gradle.properties b/gradle.properties index 1b029e7..39b1846 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -#Sat Jun 18 16:02:18 UTC 2022 -version=2.3.0 +#Sun Jul 24 14:15:17 UTC 2022 +version=2.4.0 diff --git a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPluginFunctionalTestBase.kt b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPluginFunctionalTestBase.kt index 03c3ea8..88013f1 100644 --- a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPluginFunctionalTestBase.kt +++ b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPluginFunctionalTestBase.kt @@ -34,10 +34,6 @@ open class JMeterPluginFunctionalTestBase { return createRunner(debug) } - protected fun runShouldSucceed(result: BuildResult) { - result.output shouldContain "BUILD SUCCESSFUL" - } - protected fun runShouldFail(result: BuildResult, reason: String = "") { result.output shouldContain "FAILURE: $reason" } @@ -61,18 +57,6 @@ open class JMeterPluginFunctionalTestBase { resource.copyTo(destFile) } - /** - * Copies a result-file (*.jtl) from `resource` to the default location. - * @param source The path to the resource file. - */ - fun copyResultToDefaultLocation(source: String) { - val destDir = testProjectDir.newFolder("./build/test-results/jmeter") - destDir.mkdirs() - val resource = File(javaClass.classLoader.getResource(source)!!.file) - val destFile = destDir.resolve(resource.name) - resource.copyTo(destFile) - } - fun copyZipResourceTo(resource: String, targetDir: File) { val tmp = testProjectDir.newFolder() val tmpZip = tmp.resolve(resource) diff --git a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterReportFunctionalTest.kt b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterReportFunctionalTest.kt index 9cdd23e..acf6a5e 100644 --- a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterReportFunctionalTest.kt +++ b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterReportFunctionalTest.kt @@ -17,13 +17,10 @@ class JMeterReportFunctionalTest : JMeterPluginFunctionalTestBase() { val runner = setupTest("default_build").withArguments("runTest", "reportTest") copyJmxToDefaultLocation() - val result = runner.build() + runner.build() val expectedReportFile = runner.projectDir.resolve("build/reports/jmeter/Test/index.html") - assertAll( - { runShouldSucceed(result) }, - { expectedReportFile should exist() } - ) + expectedReportFile should exist() } @Test @@ -33,11 +30,10 @@ class JMeterReportFunctionalTest : JMeterPluginFunctionalTestBase() { val reportDir = runner.projectDir.resolve("custom-template") copyZipResourceTo("report-template.zip", reportDir) - val result = runner.build() + runner.build() val expectedReportFile = runner.projectDir.resolve("build/reports/jmeter/Test/index.html") assertAll( - { runShouldSucceed(result) }, { expectedReportFile should exist() }, { expectedReportFile.readText() should contain("My own") } ) @@ -50,11 +46,10 @@ class JMeterReportFunctionalTest : JMeterPluginFunctionalTestBase() { val reportDir = runner.projectDir.resolve("custom-template") copyZipResourceTo("report-template.zip", reportDir) - val result = runner.build() + runner.build() val expectedReportFile = runner.projectDir.resolve("build/reports/jmeter/Test/index.html") assertAll( - { runShouldSucceed(result) }, { expectedReportFile should exist() }, { expectedReportFile.readText() should contain("<title>My own") } ) diff --git a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterRunFunctionalTest.kt b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterRunFunctionalTest.kt index 559ff25..1dcdf71 100644 --- a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterRunFunctionalTest.kt +++ b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterRunFunctionalTest.kt @@ -1,10 +1,13 @@ package de.qualersoft.jmeter.gradleplugin +import io.kotest.assertions.withClue import io.kotest.matchers.file.exist import io.kotest.matchers.should +import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNot import io.kotest.matchers.string.contain import io.kotest.matchers.string.shouldContain +import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll @@ -34,11 +37,10 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { val runner = setupTest("default_build").withArguments("runTest") copyJmxToDefaultLocation() - val result = runner.build() + runner.build() val resultFolder = runner.projectDir.resolve("build/test-results/jmeter") assertAll( - { runShouldSucceed(result) }, { resultFolder should exist() }, { resultFolder.resolve("Test.jtl") should exist() } ) @@ -51,10 +53,7 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { val result = runner.build() - assertAll( - { runShouldSucceed(result) }, - { result.output shouldNot contain(REGEX_ARGS_LOG_MSG) } - ) + result.output shouldNot contain(REGEX_ARGS_LOG_MSG) } @Test @@ -64,10 +63,7 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { val result = runner.build() - assertAll( - { runShouldSucceed(result) }, - { result.output should contain(REGEX_ARGS_LOG_MSG) } - ) + result.output should contain(REGEX_ARGS_LOG_MSG) } @Test @@ -75,11 +71,10 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { val runner = setupTest("noJmxFileGiven_build").withArguments("runTest", "--test=Test.jmx") copyJmxToDefaultLocation() - val result = runner.build() + runner.build() val resultFolder = runner.projectDir.resolve("jmeter-results") assertAll( - { runShouldSucceed(result) }, { resultFolder should exist() }, { resultFolder.resolve("Test.jtl") should exist() } ) @@ -90,12 +85,11 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { val runner = setupTest("useExtension_build").withArguments("runTest") copyJmxToDefaultLocation() - val result = runner.build() + runner.build() val resultFolder = runner.projectDir.resolve("jmeter-results") val defaultResultFolder = runner.projectDir.resolve("build/test-results/jmeter") assertAll( - { runShouldSucceed(result) }, { resultFolder should exist() }, { resultFolder.resolve("Test.jtl") should exist() }, { defaultResultFolder shouldNot exist() } @@ -268,6 +262,20 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { ) } + @Test + fun `execute jmeter twice without clean should succeed and setup should run only once`() { + val runner = setupTest("default_build").withArguments("runTest") + copyJmxToDefaultLocation() + val res1 = runner.build() + val res2 = runner.build() + assertAll( + { withClue("setupJMeter run 1") { res1.task(":setupJMeter")?.outcome shouldBe TaskOutcome.SUCCESS } }, + { withClue("runTest run 1") { res1.task(":runTest")?.outcome shouldBe TaskOutcome.SUCCESS } }, + { withClue("setupJMeter run 2") { res2.task(":setupJMeter")?.outcome shouldBe TaskOutcome.UP_TO_DATE } }, + { withClue("runTest run 2") { res2.task(":runTest")?.outcome shouldBe TaskOutcome.SUCCESS } } + ) + } + @Nested inner class ProxySettings { @@ -338,9 +346,8 @@ class JMeterRunFunctionalTest : JMeterPluginFunctionalTestBase() { } private fun defaultRunner(vararg arguments: String) = setupTest("default_build") - .withArguments("runTest", *arguments).also { - copyJmxToDefaultLocation() - } + .withArguments("runTest", *arguments) + .also { copyJmxToDefaultLocation() } } companion object { diff --git a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterSetupFunctionalTest.kt b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterSetupFunctionalTest.kt index 0f32d3d..2f53922 100644 --- a/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterSetupFunctionalTest.kt +++ b/src/functionalTest/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterSetupFunctionalTest.kt @@ -1,10 +1,13 @@ package de.qualersoft.jmeter.gradleplugin +import io.kotest.assertions.withClue import io.kotest.matchers.collections.haveSize import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.should +import io.kotest.matchers.shouldBe import io.kotest.matchers.string.contain import io.kotest.matchers.string.shouldContain +import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import java.io.File @@ -93,49 +96,47 @@ class JMeterSetupFunctionalTest : JMeterPluginFunctionalTestBase() { @Test fun `run same setup tasks twice must cache second run`() { - val runner = setupTest("twoSetupTasks").withArguments("setupJMeter") + val runner = setupTest("twoSetupTasks").withArguments(SETUP_TASK_NAME) var result = runner.build() - val output1 = result.output + val task1 = result.task(":$SETUP_TASK_NAME") - result = runner.withArguments("setupJMeter").build() - val output2 = result.output + result = runner.withArguments(SETUP_TASK_NAME).build() + val task2 = result.task(":$SETUP_TASK_NAME") assertAll( - { output1 should contain("(?m)^> Task :setupJMeter$".toRegex()) }, - { output1 should contain("(?m)^1 actionable task: 1 executed$".toRegex()) }, - { output2 should contain("(?m)^> Task :setupJMeter UP-TO-DATE$".toRegex()) }, - { output2 should contain("(?m)^1 actionable task: 1 up-to-date$".toRegex()) } + { withClue("Outcome first run") { task1?.outcome shouldBe TaskOutcome.SUCCESS } }, + { withClue("Outcome second run") { task2?.outcome shouldBe TaskOutcome.UP_TO_DATE } } ) } @Test fun `run same setup tasks twice with modification of tool must not cache second call`() { - val runner = setupTest("withToolVersionFromCli").withArguments("setupJMeter") + val runner = setupTest("withToolVersionFromCli").withArguments(SETUP_TASK_NAME) var result = runner.build() - val output1 = result.output + val task1 = result.task(":$SETUP_TASK_NAME") - result = runner.withArguments("setupJMeter", "-PtoolVersion=5.4.2").build() - val output2 = result.output + result = runner.withArguments(SETUP_TASK_NAME, "-PtoolVersion=5.4.2").build() + val task2 = result.task(":$SETUP_TASK_NAME") // atm we do not clean val jars = testProjectDir.root.resolve("build/jmeter/bin").listFiles { _, name -> name.startsWith("ApacheJMeter") }!! assertAll( - { output1 should contain("(?m)^> Task :setupJMeter$".toRegex()) }, - { output1 should contain("(?m)^1 actionable task: 1 executed$".toRegex()) }, - { output2 should contain("(?m)^> Task :setupJMeter$".toRegex()) }, - { output2 should contain("(?m)^1 actionable task: 1 executed$".toRegex()) }, + { withClue("Outcome first run") { task1?.outcome shouldBe TaskOutcome.SUCCESS } }, + { withClue("Outcome second run") { task2?.outcome shouldBe TaskOutcome.SUCCESS } }, { jars shouldHaveSize 2 } ) } /** - * Defaults defined in [JMeterConfig] + * Defaults defined in [JMeterConfig][de.qualersoft.jmeter.gradleplugin.JMeterConfig] */ companion object { const val DEFAULT_GROUP = "org.apache.jmeter" const val DEFAULT_NAME = "ApacheJMeter" const val DEFAULT_VERSION = "5.4.1" + + const val SETUP_TASK_NAME = "setupJMeter" } } diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterExtension.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterExtension.kt index 2210537..c95b941 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterExtension.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterExtension.kt @@ -6,6 +6,7 @@ import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Nested /** @@ -118,6 +119,23 @@ open class JMeterExtension(private val project: Project) { ) // </editor-fold> + // <editor-fold desc="Remoting properties"> + /** + * Flag to execute test on configured remote servers. + * + * Defaults to `false`. + */ + val enableRemoteExecution: Property<Boolean> = objects.property<Boolean>().value(false) + + /** + * Flag to exit remote servers at the end of the test. + * Only effective iff [enableRemoteExecution] is `true`. + * + * Defaults to `false`. + */ + val exitRemoteServers: Property<Boolean> = objects.property<Boolean>().value(false) + // </editor-fold> + /** * Root directory used by tasks to resolve its jmxFile. * diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPlugin.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPlugin.kt index 9b4d19d..cef0960 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPlugin.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/JMeterPlugin.kt @@ -101,5 +101,5 @@ class JMeterPlugin : Plugin<Project> { /** * Main */ -internal fun Project.jmeter(): JMeterExtension = extensions.getByName(EXTENSION_NAME) as? JMeterExtension - ?: throw IllegalStateException("$EXTENSION_NAME is not of the correct type") +internal fun Project.jmeter(): JMeterExtension = checkNotNull(extensions.getByName(EXTENSION_NAME) as? JMeterExtension +) { "$EXTENSION_NAME is not of the correct type" } diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTask.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTask.kt index 572244e..76ffa30 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTask.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTask.kt @@ -166,8 +166,10 @@ abstract class JMeterBaseTask : JavaExec() { private val setupTask: TaskProvider<JMeterSetupTask> = project.tasks.named(JMETER_SETUP_TASK_NAME, JMeterSetupTask::class.java) - private val jmToolJar: RegularFileProperty = objectFactory.fileProperty() - .value(setupTask.map { it.jmJar.get() }) + @InputFile + @PathSensitive(PathSensitivity.ABSOLUTE) + protected val jmToolJar: RegularFileProperty = objectFactory.fileProperty() + .value(setupTask.get().jmJar) @Internal protected val maskOutput = mutableListOf<String>() @@ -175,7 +177,6 @@ abstract class JMeterBaseTask : JavaExec() { init { group = "jmeter" mainClass.value(jmExt.tool.mainClass) - super.dependsOn(setupTask) } private fun resolveJmxFile() = jmxFile.map { diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterExecBaseTask.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterExecBaseTask.kt index 94c833e..1bf64af 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterExecBaseTask.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterExecBaseTask.kt @@ -87,6 +87,6 @@ abstract class JMeterExecBaseTask : JMeterBaseTask() { } companion object { - private const val REPORT_TEMPLATE_DIR_KEY="jmeter.reportgenerator.exporter.html.property.template_dir" + private const val REPORT_TEMPLATE_DIR_KEY = "jmeter.reportgenerator.exporter.html.property.template_dir" } } diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTask.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTask.kt index f579dd6..2d95357 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTask.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTask.kt @@ -4,7 +4,9 @@ import de.qualersoft.jmeter.gradleplugin.JMeterExtension import de.qualersoft.jmeter.gradleplugin.listProperty import de.qualersoft.jmeter.gradleplugin.property import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Optional @@ -22,6 +24,10 @@ import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "Would love to execute jmeter tests more than once;)") open class JMeterRunTask : JMeterExecBaseTask() { + init { + outputs.upToDateWhen { false } + } + /** * Path to a JMeter property file which will be sent to all remote server. * @@ -77,7 +83,7 @@ open class JMeterRunTask : JMeterExecBaseTask() { */ @Input @Optional - val proxyScheme = objectFactory.property<String>() + val proxyScheme: Property<String> = objectFactory.property<String>() .value(jmExt.proxyScheme) @Option( @@ -95,7 +101,7 @@ open class JMeterRunTask : JMeterExecBaseTask() { */ @Input @Optional - val proxyHost = objectFactory.property<String>() + val proxyHost: Property<String> = objectFactory.property<String>() .value(jmExt.proxyHost) @Option( @@ -111,7 +117,7 @@ open class JMeterRunTask : JMeterExecBaseTask() { */ @Input @Optional - val proxyPort = objectFactory.property<Int>() + val proxyPort: Property<Int> = objectFactory.property<Int>() .value(jmExt.proxyPort) @Option(option = "PP", description = "proxy server port") @@ -126,7 +132,7 @@ open class JMeterRunTask : JMeterExecBaseTask() { */ @Input @Optional - val nonProxyHosts = objectFactory.listProperty<String>() + val nonProxyHosts: ListProperty<String> = objectFactory.listProperty<String>() .value(jmExt.nonProxyHosts) @Option( @@ -156,6 +162,16 @@ open class JMeterRunTask : JMeterExecBaseTask() { fun setPassword(pwd: String) { password.value(pwd) } + + @Input + @Optional + val enableRemoteExecution = objectFactory.property<Boolean>() + .value(jmExt.enableRemoteExecution) + + @Input + @Optional + val exitRemoteServers = objectFactory.property<Boolean>() + .value(jmExt.exitRemoteServers) // </editor-fold> override fun createRunArguments() = mutableListOf<String>().apply { @@ -220,5 +236,20 @@ open class JMeterRunTask : JMeterExecBaseTask() { } addDelete(this) + + addRemoteArgs(this) + } + + private fun addRemoteArgs(args: MutableList<String>) { + if (enableRemoteExecution.get()) { + args.add("-r") + if (exitRemoteServers.get()) { + args.add("-X") + } + } else if (exitRemoteServers.get()) { + logger.warn( + "The Flag `exitRemoteServer` is enabled, but `enableRemoteExecution` isn't! Check your configuration." + ) + } } } diff --git a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterSetupTask.kt b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterSetupTask.kt index dc08f43..26bb871 100644 --- a/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterSetupTask.kt +++ b/src/main/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterSetupTask.kt @@ -11,9 +11,9 @@ import org.gradle.api.artifacts.ResolvedArtifact import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction import java.io.File import java.util.jar.JarFile @@ -32,8 +32,12 @@ open class JMeterSetupTask : DefaultTask() { private val jmExtDir = jmLibDir.resolve("ext") private val jmJUnitDir = jmLibDir.resolve("junit") - @get:Internal - internal val jmJar: RegularFileProperty = project.objects.fileProperty() + private val sourceJmJar by lazy { getJMeterLib() } + + @get:OutputFile + internal val jmJar: RegularFileProperty = project.objects.fileProperty().value { + jmBinDir.resolve("${jmTool.name}-${jmTool.version}.jar") + } init { group = "jmeter" @@ -44,7 +48,7 @@ open class JMeterSetupTask : DefaultTask() { prepareDirectories() // copy jmeter-runner to bin dir - jmJar.set(getJMeterLib().copyToDir(jmBinDir)) + sourceJmJar.copyTo(jmJar.asFile.get(), true) val resourceJar = getJMeterResourceLib() CopyResource.extractJarToDir(JarFile(resourceJar), jmToolDir) @@ -59,7 +63,8 @@ open class JMeterSetupTask : DefaultTask() { private fun getJMeterLib(): File { val artifacts: Set<ResolvedArtifact> = project.configurations .getByName(JMETER_RUNNER) - .resolvedConfiguration.resolvedArtifacts + .resolvedConfiguration + .resolvedArtifacts return findArtifactMatch(artifacts, jmTool.group, jmTool.name) } @@ -70,7 +75,8 @@ open class JMeterSetupTask : DefaultTask() { private fun getJMeterResourceLib(): File { val artifacts: Set<ResolvedArtifact> = project.configurations .getByName(JMETER_RUNNER) - .resolvedConfiguration.resolvedArtifacts + .resolvedConfiguration + .resolvedArtifacts val toolConfNot = jmTool.createToolConfigDependencyNotion() val toolConfName = toolConfNot["name"]!! @@ -91,26 +97,22 @@ open class JMeterSetupTask : DefaultTask() { private fun resolveAndCopyExtensionLibs() { val resolvedExtensions = mutableListOf<ResolvedDependency>() project.configurations.getByName(JMETER_PLUGIN_DEPENDENCY) - .resolvedConfiguration.firstLevelModuleDependencies.flatMap { + .resolvedConfiguration + .firstLevelModuleDependencies + .flatMap { resolvedExtensions.add(it) it.moduleArtifacts - }.map { - it.file - }.forEach { - it.copyToDir(jmExtDir) } - resolvedExtensions.flatMap { - it.children - }.filterNot { + .map { it.file } + .forEach { it.copyToDir(jmExtDir) } + + resolvedExtensions + .flatMap { it.children } // only take dependencies that were not already copied earlier - resolvedExtensions.contains(it) - }.flatMap { - it.allModuleArtifacts - }.map { - it.file - }.forEach { - it.copyToDir(jmLibDir) - } + .filterNot { resolvedExtensions.contains(it) } + .flatMap { it.allModuleArtifacts } + .map { it.file } + .forEach { it.copyToDir(jmLibDir) } } /** @@ -118,11 +120,10 @@ open class JMeterSetupTask : DefaultTask() { */ private fun resolveAndCopyToolLibs() { project.configurations.getByName(JMETER_LIB_DEPENDENCY) - .resolvedConfiguration.resolvedArtifacts.map { - it.file - }.forEach { - it.copyToDir(jmLibDir) - } + .resolvedConfiguration + .resolvedArtifacts + .map { it.file } + .forEach { it.copyToDir(jmLibDir) } } /** diff --git a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/PluginTestBase.kt b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/PluginTestBase.kt index 7a2328f..a2bcf21 100644 --- a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/PluginTestBase.kt +++ b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/PluginTestBase.kt @@ -1,6 +1,6 @@ package de.qualersoft.jmeter.gradleplugin -import io.kotest.assertions.show.show +import io.kotest.assertions.print.print import io.kotest.matchers.Matcher import io.kotest.matchers.MatcherResult @@ -11,23 +11,23 @@ object PluginTestBase { internal fun <T : CharSequence, C : Collection<T>> matchingEntry(regex: Regex) = object : Matcher<C> { override fun test(value: C) = MatcherResult( value.any { it.matches(regex) }, - { "Collection should contain element matching ${regex.show().value}; listing some elements ${value.take(5)}" }, - { "Collection should not contain element matching ${regex.show().value}" } + { "Collection should contain element matching ${regex.print().value}; listing some elements ${value.take(5)}" }, + { "Collection should not contain element matching ${regex.print().value}" } ) } internal fun <T : CharSequence, C : Collection<T>> entryStartsWith(t: T) = object : Matcher<C> { override fun test(value: C) = MatcherResult( value.any { it.startsWith(t) }, - { "Collection should contain element starting with ${t.show().value}; listing some elements ${value.take(5)}" }, - { "Collection should not contain element starting with ${t.show().value}" } + { "Collection should contain element starting with ${t.print().value}; listing some elements ${value.take(5)}" }, + { "Collection should not contain element starting with ${t.print().value}" } ) } internal fun <T : CharSequence, C : Collection<T>> entryEndsWith(t: T) = object : Matcher<C> { override fun test(value: C) = MatcherResult( value.any { it.endsWith(t) }, - { "Collection should contain element ending with ${t.show().value}; listing some elements ${value.take(5)}" }, - { "Collection should not contain element ending with ${t.show().value}" } + { "Collection should contain element ending with ${t.print().value}; listing some elements ${value.take(5)}" }, + { "Collection should not contain element ending with ${t.print().value}" } ) } diff --git a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTaskTest.kt b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTaskTest.kt index 88c6017..fdcd7df 100644 --- a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTaskTest.kt +++ b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterBaseTaskTest.kt @@ -23,7 +23,7 @@ import java.io.File class JMeterBaseTaskTest : JMeterTaskTestBase() { - //<editor-fold desc="Sys-prop-file"> + // <editor-fold desc="Sys-prop-file"> @Test fun systemPropertyFileDefaultsToEmpty() { val task = createTask<JMeterBaseTask> {}.get() @@ -48,9 +48,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { val result = task.createRunArguments() result shouldHave entryEndsWith("sysPropsForArgs.properties") } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="Sys prop key value"> + // <editor-fold desc="Sys prop key value"> @Test fun systemPropertyDefaultsToEmpty() { val task = createTask<JMeterBaseTask> {}.get() @@ -107,9 +107,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { { result should contain("-DbKey2=bValue2") } ) } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="Main property file"> + // <editor-fold desc="Main property file"> @Test fun mainPropertyFileDefaultsToUnset() { val task = createTask<JMeterBaseTask> { }.get() @@ -135,9 +135,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { val result = task.createRunArguments() result shouldHave entryEndsWith("otherMain.properties") } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="Additional prop files"> + // <editor-fold desc="Additional prop files"> @Test fun additionalPropertyFilesDefaultsToEmpty() { val task = createTask<JMeterBaseTask> { }.get() @@ -193,9 +193,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { { result shouldHave entryEndsWith("Extension.file") } ) } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="jmeter Properties"> + // <editor-fold desc="jmeter Properties"> @Test fun jmeterPropertiesDefaultsToEmpty() { val task = createTask<JMeterBaseTask> { }.get() @@ -239,9 +239,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { val result = task.createRunArguments() result should contain("-JargKey=argVal") } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="log config"> + // <editor-fold desc="log config"> @Test fun noLogConfigByDefault() { val task = createTask<JMeterBaseTask> { }.get() @@ -264,9 +264,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { { args shouldHave matchingEntry(".*myCustomLogConf.xml".toRegex()) } ) } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="logOutputFile"> + // <editor-fold desc="logOutputFile"> @Test fun logOutputHasDefault() { val task = createTask<JMeterBaseTask> { }.get() @@ -316,9 +316,9 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { val result = task.createRunArguments() withClue("logOutput flag") { result shouldNot contain("-j") } } - //</editor-fold> + // </editor-fold> - //<editor-fold desc="JMX-file"> + // <editor-fold desc="JMX-file"> @Test fun noDefaultJmxFile() { val task = createTask<JMeterBaseTask> { }.get() @@ -354,7 +354,7 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { { task.sourceFile.get().asFile.absolutePath shouldNot match(".*src[/\\\\]test[/\\\\]jmeter".toRegex()) } ) } - //</editor-fold> + // </editor-fold> @Test fun taskWithNoConfigShouldInheritDefaultConfig() { @@ -367,4 +367,4 @@ class JMeterBaseTaskTest : JMeterTaskTestBase() { } ) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTaskTest.kt b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTaskTest.kt index 861aecb..6c7c387 100644 --- a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTaskTest.kt +++ b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterRunTaskTest.kt @@ -1,5 +1,6 @@ package de.qualersoft.jmeter.gradleplugin.task +import de.qualersoft.jmeter.gradleplugin.JMeterExtension import de.qualersoft.jmeter.gradleplugin.entryEndsWith import de.qualersoft.jmeter.gradleplugin.matchingEntry import io.kotest.assertions.withClue @@ -7,6 +8,7 @@ import io.kotest.matchers.collections.contain import io.kotest.matchers.collections.containExactly import io.kotest.matchers.collections.containExactlyInAnyOrder import io.kotest.matchers.collections.haveSize +import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldContainInOrder import io.kotest.matchers.collections.shouldNotContain import io.kotest.matchers.should @@ -14,6 +16,8 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldHave import io.kotest.matchers.shouldNot import io.kotest.matchers.shouldNotHave +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import java.io.File @@ -96,212 +100,297 @@ class JMeterRunTaskTest : JMeterTaskTestBase() { args shouldNot contain("-f") } - @Test - fun withAllProxySettingsFromExtension() { - val scheme = "https" - val host = "127.0.0.1" - val port = 8080 - val nonHosts = listOf("localhost", "localhorst") - val task = createTaskWithConfig<JMeterRunTask>({ - proxyScheme.set(scheme) - proxyHost.set(host) - proxyPort.set(port) - nonProxyHosts.addAll(nonHosts) - }, {}).get() - assertAll( - { task.proxyScheme.get() shouldBe scheme }, - { task.proxyHost.get() shouldBe host }, - { task.proxyPort.get() shouldBe port }, - { task.nonProxyHosts.get() should containExactlyInAnyOrder(nonHosts) } - ) - } - - @Test - fun withProxySettingsFromExtensionOverridden() { - val extScheme = "smtp" - val extHost = "10.3.5.42" - val extPort = 666 - val extNonHost = "example.com" - - val scheme = "https" - val host = "127.0.0.1" - val port = 8080 - val nonHosts = listOf("localhost", "localhorst") - val task = createTaskWithConfig<JMeterRunTask>({ - proxyScheme.set(extScheme) - proxyHost.set(extHost) - proxyPort.set(extPort) - nonProxyHosts.add(extNonHost) - }, { - proxyScheme.set(scheme) - proxyHost.set(host) - proxyPort.set(port) - nonProxyHosts.addAll(nonHosts) - }).get() - - val ext = getExtension() - - assertAll( - { withClue("ext Scheme") { ext.proxyScheme.get() shouldBe extScheme } }, - { withClue("ext Host") { ext.proxyHost.get() shouldBe extHost } }, - { withClue("ext Port") { ext.proxyPort.get() shouldBe extPort } }, - { withClue("ext nonProxyHost size") { ext.nonProxyHosts.get() should haveSize(1) } }, - { withClue("ext nonProxyHosts") { ext.nonProxyHosts.get() should containExactly(extNonHost) } }, - { withClue("task Scheme") { task.proxyScheme.get() shouldBe scheme } }, - { withClue("task Host") { task.proxyHost.get() shouldBe host } }, - { withClue("task Port") { task.proxyPort.get() shouldBe port } }, - { - withClue("task nonProxyHosts") { - task.nonProxyHosts.get() should containExactlyInAnyOrder(nonHosts + extNonHost) + @Nested + @DisplayName("Proxy settings tests") + inner class ProxyTests { + + @Test + fun withAllProxySettingsFromExtension() { + val scheme = "https" + val host = "127.0.0.1" + val port = 8080 + val nonHosts = listOf("localhost", "localhorst") + val task = createTaskWithConfig<JMeterRunTask>({ + proxyScheme.set(scheme) + proxyHost.set(host) + proxyPort.set(port) + nonProxyHosts.addAll(nonHosts) + }, {}).get() + assertAll( + { task.proxyScheme.get() shouldBe scheme }, + { task.proxyHost.get() shouldBe host }, + { task.proxyPort.get() shouldBe port }, + { task.nonProxyHosts.get() should containExactlyInAnyOrder(nonHosts) } + ) + } + + @Test + fun withProxySettingsFromExtensionOverridden() { + val extScheme = "smtp" + val extHost = "10.3.5.42" + val extPort = 666 + val extNonHost = "example.com" + + val scheme = "https" + val host = "127.0.0.1" + val port = 8080 + val nonHosts = listOf("localhost", "localhorst") + val task = createTaskWithConfig<JMeterRunTask>({ + proxyScheme.set(extScheme) + proxyHost.set(extHost) + proxyPort.set(extPort) + nonProxyHosts.add(extNonHost) + }, { + proxyScheme.set(scheme) + proxyHost.set(host) + proxyPort.set(port) + nonProxyHosts.addAll(nonHosts) + }).get() + + val ext = getExtension() + + assertAll( + { withClue("ext Scheme") { ext.proxyScheme.get() shouldBe extScheme } }, + { withClue("ext Host") { ext.proxyHost.get() shouldBe extHost } }, + { withClue("ext Port") { ext.proxyPort.get() shouldBe extPort } }, + { withClue("ext nonProxyHost size") { ext.nonProxyHosts.get() should haveSize(1) } }, + { withClue("ext nonProxyHosts") { ext.nonProxyHosts.get() should containExactly(extNonHost) } }, + { withClue("task Scheme") { task.proxyScheme.get() shouldBe scheme } }, + { withClue("task Host") { task.proxyHost.get() shouldBe host } }, + { withClue("task Port") { task.proxyPort.get() shouldBe port } }, + { + withClue("task nonProxyHosts") { + task.nonProxyHosts.get() should containExactlyInAnyOrder(nonHosts + extNonHost) + } } - } - ) - } - - @Test - fun withSchemeOnly() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - proxyScheme.set("smtp1234") - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("Scheme") { args shouldContainInOrder listOf("-E", "smtp1234") } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - @Test - fun withHostOnly() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - proxyHost.set("localhost") - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("Host") { args shouldContainInOrder listOf("-H", "localhost") } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - @Test - fun withPortOnly() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - proxyPort.set(42) - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("Port") { args shouldContainInOrder listOf("-P", "42") } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - @Test - fun withNonProxy() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - nonProxyHosts.add("localHorst") - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("NonProxy") { args shouldContainInOrder listOf("-N", "localHorst") } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - @Test - fun withMultipleNonProxy() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - nonProxyHosts.addAll("localHorst", "127.0.0.42") - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("NonProxy") { args shouldContainInOrder listOf("-N", "localHorst|127.0.0.42") } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - /** - * Corner case - * `Null` could be introduced through build script logic - */ - @Test - fun withNonProxyNulled() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - nonProxyHosts.set(null as List<String>?) - jmxFile.set("Report.jmx") - }).get() - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) - } - - @Test - fun withUsername() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - username.set("proxyUser") - jmxFile.set("Report.jmx") - }).get() - - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("User") { args shouldContainInOrder listOf("-u", "proxyUser") } }, - { withClue("no Password") { args shouldNotContain "-a" } } - ) + ) + } + + @Test + fun withSchemeOnly() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + proxyScheme.set("smtp1234") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("Scheme") { args shouldContainInOrder listOf("-E", "smtp1234") } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withHostOnly() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + proxyHost.set("localhost") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("Host") { args shouldContainInOrder listOf("-H", "localhost") } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withPortOnly() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + proxyPort.set(42) + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("Port") { args shouldContainInOrder listOf("-P", "42") } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withNonProxy() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + nonProxyHosts.add("localHorst") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("NonProxy") { args shouldContainInOrder listOf("-N", "localHorst") } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withMultipleNonProxy() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + nonProxyHosts.addAll("localHorst", "127.0.0.42") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("NonProxy") { args shouldContainInOrder listOf("-N", "localHorst|127.0.0.42") } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + /** + * Corner case + * `Null` could be introduced through build script logic + */ + @Test + fun withNonProxyNulled() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + nonProxyHosts.set(null as List<String>?) + jmxFile.set("Report.jmx") + }).get() + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withUsername() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + username.set("proxyUser") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("User") { args shouldContainInOrder listOf("-u", "proxyUser") } }, + { withClue("no Password") { args shouldNotContain "-a" } } + ) + } + + @Test + fun withPassword() { + val task = createTaskWithConfig<JMeterRunTask>({}, { + password.set("Secret") + jmxFile.set("Report.jmx") + }).get() + + val args = task.createRunArguments() + assertAll("Only Scheme arg", + { withClue("no Scheme") { args shouldNotContain "-E" } }, + { withClue("no Host") { args shouldNotContain "-H" } }, + { withClue("no Port") { args shouldNotContain "-P" } }, + { withClue("no NonProxy") { args shouldNotContain "-N" } }, + { withClue("no User") { args shouldNotContain "-u" } }, + { withClue("Password") { args shouldContainInOrder listOf("-a", "Secret") } } + ) + } } - @Test - fun withPassword() { - val task = createTaskWithConfig<JMeterRunTask>({}, { - password.set("Secret") - jmxFile.set("Report.jmx") - }).get() + @Nested + @DisplayName("Remote settings tests") + inner class RemoteTests { + + @Test + fun defaultNoRemoteArgsAreUsed() { + val task = createTaskWithMandatoryArgs().get() + + val args = task.createRunArguments() + checkForFlags(args, "No `-r` and no `-X` args", remoteFlag = false, exitFlag = false) + } + + @Test + fun withRemoteExecutionFlagFromExtension() { + val task = createTaskWithMandatoryArgs(extConfig = { + enableRemoteExecution.set(true) + }).get() + + val args = task.createRunArguments() + checkForFlags(args, "With `-r` no `-X` args", remoteFlag = true, exitFlag = false) + } + + @Test + fun withRemoteExecutionFlagOnTask() { + val task = createTaskWithMandatoryArgs(taskConfig = { + enableRemoteExecution.set(true) + }).get() + + val args = task.createRunArguments() + checkForFlags(args, "With `-r` no `-X` args", remoteFlag = true, exitFlag = false) + } + + @Test + fun withRemoteExecutionAndExitFlagFromExtension() { + val task = createTaskWithMandatoryArgs(extConfig = { + enableRemoteExecution.set(true) + exitRemoteServers.set(true) + }).get() + + val args = task.createRunArguments() + checkForFlags(args, "With `-r` with `-X` args", remoteFlag = true, exitFlag = true) + } + + @Test + fun withRemoteExecutionAndExitFlagOnTask() { + val task = createTaskWithMandatoryArgs(taskConfig = { + enableRemoteExecution.set(true) + exitRemoteServers.set(true) + }).get() + + val args = task.createRunArguments() + checkForFlags(args, "With `-r` with `-X` args", remoteFlag = true, exitFlag = true) + } + + @Test + fun exitFlagShouldBeIgnoredIfRemoteIsDisabled() { + val task = createTaskWithMandatoryArgs(taskConfig = { + exitRemoteServers.set(true) + }).get() + + val args = task.createRunArguments() + checkForFlags(args, "No `-r` no `-X` args", remoteFlag = false, exitFlag = false) + } + + private fun createTaskWithMandatoryArgs( + extConfig: JMeterExtension.() -> Unit = {}, + taskConfig: JMeterRunTask.() -> Unit = {} + ) = createTaskWithConfig(extConfig, taskConfig).also { + it.configure { tsk -> + tsk.jmxFile.set("test.jmx") + } + } - val args = task.createRunArguments() - assertAll("Only Scheme arg", - { withClue("no Scheme") { args shouldNotContain "-E" } }, - { withClue("no Host") { args shouldNotContain "-H" } }, - { withClue("no Port") { args shouldNotContain "-P" } }, - { withClue("no NonProxy") { args shouldNotContain "-N" } }, - { withClue("no User") { args shouldNotContain "-u" } }, - { withClue("Password") { args shouldContainInOrder listOf("-a", "Secret") } } + private fun checkForFlags(args: List<String>, title: String, remoteFlag: Boolean, exitFlag: Boolean) = assertAll( + title, + { withClue("Remote-Flag") { if (remoteFlag) args shouldContain "-r" else args shouldNotContain "-r" } }, + { withClue("Exit-Flag") { if (exitFlag) args shouldContain "-X" else args shouldNotContain "-X" } } ) } } diff --git a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterTaskTestBase.kt b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterTaskTestBase.kt index 72534f4..4f1ea62 100644 --- a/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterTaskTestBase.kt +++ b/src/test/kotlin/de/qualersoft/jmeter/gradleplugin/task/JMeterTaskTestBase.kt @@ -12,8 +12,8 @@ open class JMeterTaskTestBase { lateinit var project: Project protected inline fun <reified T : Task> createTaskWithConfig( - extConfig: JMeterExtension.() -> Unit, - noinline taskConfig: T.() -> Unit + extConfig: JMeterExtension.() -> Unit = {}, + noinline taskConfig: T.() -> Unit = {} ): TaskProvider<T> { return ProjectBuilder.builder().build().also { project = it