├── .circleci └── config.yml ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── documentation-issue.md │ ├── feature-request.md │ ├── question.md │ └── technical-debt.md └── dependabot.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appveyor.yml ├── build.gradle ├── docs ├── SCREENSHOTS.md └── images │ ├── mocha-error.png │ ├── mocha-light-error.png │ ├── mocha-light.gif │ ├── mocha-light.png │ ├── mocha-parallel-error.png │ ├── mocha-parallel-streams.png │ ├── mocha-parallel.gif │ ├── mocha-streams.png │ ├── mocha-theme.gif │ ├── mocha.gif │ ├── mocha.png │ ├── plain-error.png │ ├── plain-light.png │ ├── plain-parallel.gif │ ├── plain.gif │ ├── plain.png │ ├── standard-error.png │ ├── standard-light-error.png │ ├── standard-light.gif │ ├── standard-light.png │ ├── standard-parallel-error.png │ ├── standard-parallel-streams.png │ ├── standard-parallel.gif │ ├── standard-streams.png │ ├── standard-theme.gif │ ├── standard.gif │ └── standard.png ├── gradle.properties ├── gradle ├── coverage.gradle ├── publishing.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── settings.gradle ├── src ├── main │ └── groovy │ │ └── com │ │ └── adarshr │ │ └── gradle │ │ └── testlogger │ │ ├── TestDescriptorWrapper.groovy │ │ ├── TestLoggerExtension.groovy │ │ ├── TestLoggerExtensionProperties.groovy │ │ ├── TestLoggerPlugin.groovy │ │ ├── TestResultWrapper.groovy │ │ ├── logger │ │ ├── ConsoleLogger.groovy │ │ ├── OutputCollector.groovy │ │ ├── ParallelTestLogger.groovy │ │ ├── SequentialTestLogger.groovy │ │ ├── TestLogger.groovy │ │ ├── TestLoggerAdapter.groovy │ │ └── TestLoggerWrapper.groovy │ │ ├── renderer │ │ ├── AnsiTextRenderer.groovy │ │ ├── CharHandler.groovy │ │ ├── CharHandlers.groovy │ │ ├── RenderingContext.groovy │ │ └── TextRenderer.groovy │ │ ├── theme │ │ ├── AbstractTheme.groovy │ │ ├── MochaParallelTheme.groovy │ │ ├── MochaTheme.groovy │ │ ├── PlainParallelTheme.groovy │ │ ├── PlainTheme.groovy │ │ ├── StandardParallelTheme.groovy │ │ ├── StandardTheme.groovy │ │ ├── Theme.groovy │ │ ├── ThemeFactory.groovy │ │ └── ThemeType.groovy │ │ └── util │ │ ├── RendererUtils.groovy │ │ └── TimeUtils.groovy ├── test-functional │ ├── groovy │ │ └── com │ │ │ └── adarshr │ │ │ └── gradle │ │ │ └── testlogger │ │ │ └── functional │ │ │ ├── AbstractFunctionalSpec.groovy │ │ │ ├── ParallelExecutionSpec.groovy │ │ │ ├── TestLoggerOutput.groovy │ │ │ ├── TestLoggerPluginSpec.groovy │ │ │ └── ThemeSwitchingSpec.groovy │ └── resources │ │ ├── many-spock-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── groovy │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FiveSpec.groovy │ │ │ ├── FourSpec.groovy │ │ │ ├── OneSpec.groovy │ │ │ ├── ThreeSpec.groovy │ │ │ └── TwoSpec.groovy │ │ ├── sample-junit4-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstTest.java │ │ │ └── SecondTest.java │ │ ├── sample-junit5-jupiter-deep-nested-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ └── DeepNestedTest.java │ │ ├── sample-junit5-jupiter-nested-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ └── NestedTest.java │ │ ├── sample-junit5-jupiter-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstTest.java │ │ │ ├── ParamTest.java │ │ │ └── SecondTest.java │ │ ├── sample-junit5-vintage-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstTest.java │ │ │ └── SecondTest.java │ │ ├── sample-kotest-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── kotlin │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ └── WordSpecTest.kt │ │ ├── sample-spek-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── kotlin │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── Calculator.kt │ │ │ └── CalculatorSpec.kt │ │ ├── sample-spock-tests-system-exit │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── groovy │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstSpec.groovy │ │ │ └── SecondSpec.groovy │ │ ├── sample-spock-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── groovy │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstSpec.groovy │ │ │ ├── SecondSpec.groovy │ │ │ └── ThirdSpec.groovy │ │ ├── sample-testng-tests │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ ├── FirstTest.java │ │ │ └── SecondTest.java │ │ ├── single-spock-test │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── groovy │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ └── SingleSpec.groovy │ │ ├── slow-spock-test │ │ ├── build.gradle │ │ └── src │ │ │ └── test │ │ │ └── groovy │ │ │ └── com │ │ │ └── adarshr │ │ │ └── test │ │ │ └── SlowSpec.groovy │ │ └── test-marker.gradle └── test │ └── groovy │ └── com │ └── adarshr │ └── gradle │ └── testlogger │ ├── TestDescriptorWrapperSpec.groovy │ ├── TestLoggerExtensionSpec.groovy │ ├── TestResultWrapperSpec.groovy │ ├── logger │ ├── ConsoleLoggerSpec.groovy │ ├── OutputCollectorSpec.groovy │ └── TestLoggerWrapperSpec.groovy │ ├── renderer │ └── AnsiTextRendererSpec.groovy │ ├── theme │ ├── AbstractThemeSpec.groovy │ ├── BaseThemeSpec.groovy │ ├── MochaParallelThemeSpec.groovy │ ├── MochaThemeSpec.groovy │ ├── PlainParallelThemeSpec.groovy │ ├── PlainThemeSpec.groovy │ ├── StandardParallelThemeSpec.groovy │ ├── StandardThemeSpec.groovy │ ├── ThemeFactorySpec.groovy │ └── ThemeTypeSpec.groovy │ └── util │ ├── RendererUtilsSpec.groovy │ └── TimeUtilsSpec.groovy └── uploadTestResults.ps1 /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: cimg/openjdk:17.0 7 | working_directory: ~/repo 8 | environment: 9 | JVM_OPTS: -Xmx4096m 10 | TERM: dumb 11 | steps: 12 | - checkout 13 | - restore_cache: 14 | keys: 15 | - v1-dependencies-{{ checksum "build.gradle" }} 16 | - v1-dependencies- 17 | - run: 18 | name: Download dependencies 19 | command: GRADLE_OPTS="-Xms256m -Xmx1024m" ./gradlew dependencies --no-daemon 20 | - save_cache: 21 | paths: 22 | - ~/.m2 23 | key: v1-dependencies-{{ checksum "build.gradle" }} 24 | - run: 25 | name: Run tests 26 | command: GRADLE_OPTS="-Xms256m -Xmx2048m" ./gradlew build --no-daemon 27 | - run: 28 | name: Code coverage 29 | command: GRADLE_OPTS="-Xms256m -Xmx1024m" ./gradlew jacocoTestReport coveralls --no-daemon 30 | - run: 31 | name: Save reports 32 | command: | 33 | mkdir -p ~/results/unit 34 | mkdir -p ~/reports/unit 35 | find . -type f -regex "./build/test-results/test/.*xml" -exec cp {} ~/results/unit \; 36 | cp -r build/reports/tests/test/* ~/reports/unit 37 | 38 | mkdir -p ~/results/functional 39 | mkdir -p ~/reports/functional 40 | find . -type f -regex "./build/test-results/functionalTest/.*xml" -exec cp {} ~/results/functional \; 41 | cp -r build/reports/tests/functionalTest/* ~/reports/functional 42 | when: always 43 | - store_test_results: 44 | path: ~/results 45 | - store_artifacts: 46 | path: ~/reports 47 | 48 | publish: 49 | docker: 50 | - image: cimg/openjdk:17.0 51 | working_directory: ~/repo 52 | environment: 53 | JVM_OPTS: -Xmx3200m 54 | TERM: dumb 55 | steps: 56 | - checkout 57 | - restore_cache: 58 | keys: 59 | - v1-dependencies-{{ checksum "build.gradle" }} 60 | - v1-dependencies- 61 | - run: 62 | name: Download dependencies 63 | command: GRADLE_OPTS="-Xms256m -Xmx1024m" ./gradlew dependencies --no-daemon 64 | - save_cache: 65 | paths: 66 | - ~/.m2 67 | key: v1-dependencies-{{ checksum "build.gradle" }} 68 | - run: 69 | name: Publish plugin 70 | command: 71 | GRADLE_OPTS="-Xms256m -Xmx1024m" ./gradlew clean publishPlugins --no-daemon 72 | -Pgradle.publish.key=${GRADLE_PUBLISH_KEY} 73 | -Pgradle.publish.secret=${GRADLE_PUBLISH_SECRET} 74 | 75 | workflows: 76 | version: 2 77 | build-and-publish: 78 | jobs: 79 | - build 80 | - publish: 81 | requires: 82 | - build 83 | filters: 84 | branches: 85 | only: master 86 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: radarsh 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | A clear and concise description of what problem you are facing. 13 | 14 | **Versions** 15 | 16 | - Test logger version: 17 | - Gradle version: 18 | - Java version: 19 | 20 | **Type of test being run** 21 | 22 | Whether it is JUnit, Spock, TestNg, or anything else. Also, try to include a sample test here. 23 | 24 | **Test logger configuration** 25 | 26 | Paste your test logger configuration here. 27 | 28 | **Screenshots** 29 | 30 | If possible, add screenshots to help explain your problem. 31 | 32 | **Additional information** 33 | 34 | Add any other information about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation issue 3 | about: Clarify something about the documentation 4 | title: '' 5 | labels: documentation 6 | assignees: radarsh 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | Explain what needs clarifying and where. 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature 6 | assignees: radarsh 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | A clear and concise description of what the feature should do. 13 | 14 | **Additional information** 15 | 16 | Supplement with examples, samples from other projects, etc here. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question 4 | title: '' 5 | labels: question 6 | assignees: radarsh 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | Ask your question here. 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/technical-debt.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Technical debt 3 | about: Create a technical debt issue 4 | title: '' 5 | labels: technical debt 6 | assignees: radarsh 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | Describe the technical debt here. 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | assignees: 9 | - radarsh 10 | ignore: 11 | - dependency-name: net.bytebuddy:byte-buddy 12 | versions: 13 | - 1.10.19 14 | - 1.10.20 15 | - 1.10.21 16 | - dependency-name: org.fusesource.jansi:jansi 17 | versions: 18 | - 2.2.0 19 | - 2.3.1 20 | - dependency-name: com.github.kt3k.coveralls 21 | versions: 22 | - 2.10.2 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | build/ 3 | gradle-app.setting 4 | !gradle-wrapper.jar 5 | .gradletasknamecache 6 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 7 | # gradle/wrapper/gradle-wrapper.properties 8 | .idea/ 9 | *.iml 10 | out/ 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at adarsh.ramamurthy@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for considering to contribute to gradle-test-logger-plugin. Here are the house rules: 4 | 5 | ## Branching model 6 | 7 | The project uses Git flow. It's best to install the [Git flow extensions](https://github.com/nvie/gitflow) before you get started. 8 | 9 | ## Commit messages 10 | 11 | Try to come up with short but meaningful commit messages in an imperative tone. [How to write a Git commit message](https://chris.beams.io/posts/git-commit/) is a great starting point. 12 | 13 | ## Testing 14 | 15 | The project uses Groovy based [Spock Framework](http://spockframework.org/spock/docs/1.3/all_in_one.html) for both unit and functional tests. Spock is an extremely expressive tool for writing tests for code written in any JVM language. 16 | 17 | ## Formatting 18 | 19 | The project uses [`.editorconfig`](https://editorconfig.org/) for setting some basic rules. The only additional thing we ask you to do is to make sure that statements after each label in Spock tests are indented using 4 spaces as follows: 20 | 21 | ```groovy 22 | def "suite text"() { 23 | given: 24 | testDescriptorMock.classDisplayName >> 'ClassName' 25 | when: 26 | def actual = theme.suiteText(testDescriptorMock, testResultMock) 27 | then: 28 | actual == "ClassName${lineSeparator()}" 29 | } 30 | ``` 31 | 32 | ## Merging 33 | 34 | Make sure that your rebase against develop first. Merging develop into a feature branch is discouraged. If you have several commits in your branch, squash them all into one commit as you rebase. 35 | 36 | If you have totally separate units of work going into the same branch, then it's OK to have these as separate commits. For example, if you have two commits "Implement feature x" and "Upgrade jansi version" on the same feature branch, it's OK to not squash them. 37 | 38 | ## Releasing 39 | 40 | 1. `git flow release start ` 41 | 2. Edit gradle.properties to have the new version 42 | 3. Edit README.md to have the new version 43 | 4. Generate a new changelog. See [Generating a changelog](#generating-a-changelog) for more info. 44 | 5. Commit all files using `git commit -am 'Release version ` 45 | 6. `git flow release finish ` 46 | 7. Tag the new release `v` 47 | 8. Push to develop, master and tags using `git push origin develop && git push origin master && git push origin --tags` 48 | 49 | ### Generating a changelog 50 | 51 | The project uses [GitHub changelog generator](https://github.com/github-changelog-generator/github-changelog-generator) to make the process of generating changelogs simpler. After installing it, run 52 | 53 | ```sh 54 | github_changelog_generator --token --user radarsh --project gradle-test-logger-plugin --enhancement-labels feature --unreleased-label --future-release v 55 | ``` 56 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | skip_tags: true 2 | test_script: 3 | - gradlew build --no-daemon 4 | after_test: 5 | - ps: ./uploadTestResults.ps1 6 | build: off 7 | artifacts: 8 | - path: build/reports/tests/test/**/*.* 9 | - path: build/reports/tests/functionalTest/**/*.* 10 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath 'org.fusesource.jansi:jansi:2.4.0' 4 | classpath localGroovy() 5 | } 6 | } 7 | 8 | plugins { 9 | id 'groovy' 10 | id 'java-gradle-plugin' 11 | id 'maven-publish' 12 | id 'idea' 13 | id 'jacoco' 14 | id 'com.gradle.plugin-publish' version '1.2.1' 15 | id 'com.github.kt3k.coveralls' version '2.12.2' 16 | } 17 | 18 | def thisPlugin = 19 | new GroovyScriptEngine(file('src/main/groovy').absolutePath, this.class.classLoader) 20 | .loadScriptByName('com/adarshr/gradle/testlogger/TestLoggerPlugin.groovy') 21 | 22 | apply from: 'gradle/publishing.gradle' 23 | apply from: 'gradle/coverage.gradle' 24 | apply plugin: thisPlugin 25 | 26 | sourceSets { 27 | functionalTest { 28 | groovy { 29 | srcDir 'src/test-functional/groovy' 30 | compileClasspath += sourceSets.main.output 31 | runtimeClasspath += sourceSets.main.output 32 | } 33 | } 34 | } 35 | 36 | dependencies { 37 | compileOnly gradleApi() 38 | compileOnly localGroovy() 39 | implementation 'org.fusesource.jansi:jansi:2.4.0' 40 | 41 | testImplementation gradleTestKit() 42 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 43 | testImplementation 'net.bytebuddy:byte-buddy:1.14.9' 44 | testImplementation 'org.objenesis:objenesis:3.3' 45 | testImplementation 'commons-io:commons-io:2.14.0' 46 | 47 | functionalTestImplementation gradleTestKit() 48 | functionalTestImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 49 | functionalTestImplementation 'commons-io:commons-io:2.14.0' 50 | } 51 | 52 | java { 53 | toolchain.languageVersion = JavaLanguageVersion.of(8) 54 | } 55 | 56 | gradlePlugin { 57 | plugins { 58 | testLoggerPlugin { 59 | id = 'com.adarshr.test-logger' 60 | implementationClass = 'com.adarshr.gradle.testlogger.TestLoggerPlugin' 61 | } 62 | } 63 | } 64 | 65 | idea { 66 | module { 67 | testSourceDirs += file('src/test-functional/groovy') 68 | } 69 | } 70 | 71 | tasks.named("test") { 72 | useJUnitPlatform() 73 | 74 | testClassesDirs += sourceSets.functionalTest.output.classesDirs 75 | classpath += sourceSets.functionalTest.runtimeClasspath 76 | systemProperty 'file.encoding', 'UTF-8' 77 | 78 | testlogger { 79 | theme 'mocha' 80 | } 81 | 82 | exclude 'com/adarshr/gradle/testlogger/functional/**' 83 | } 84 | 85 | tasks.register("functionalTest", Test) { 86 | dependsOn 'pluginUnderTestMetadata', 'testClasses' 87 | 88 | useJUnitPlatform() 89 | 90 | inputs.dir "$projectDir/src/test-functional/resources" 91 | maxParallelForks = 2 92 | testClassesDirs = sourceSets.functionalTest.output.classesDirs 93 | classpath = sourceSets.test.runtimeClasspath + sourceSets.functionalTest.runtimeClasspath 94 | systemProperty 'file.encoding', 'UTF-8' 95 | minHeapSize '128m' 96 | maxHeapSize '512m' 97 | 98 | testlogger { 99 | theme 'mocha-parallel' 100 | } 101 | 102 | include 'com/adarshr/gradle/testlogger/functional/**' 103 | } 104 | 105 | tasks.named("check") { 106 | dependsOn("functionalTest") 107 | } 108 | -------------------------------------------------------------------------------- /docs/SCREENSHOTS.md: -------------------------------------------------------------------------------- 1 | # Screenshots 2 | 3 | ## Standard theme 4 | 5 | ### Typical operation 6 | ![Standard theme](images/standard.gif) 7 | ### Typical operation on a light background 8 | ![Standard theme](images/standard-light.gif) 9 | ### Successful build 10 | ![Standard theme](images/standard.png) 11 | ### Successful build on a light background 12 | ![Standard theme](images/standard-light.png) 13 | ### Failed build 14 | ![Standard theme](images/standard-error.png) 15 | ### Failed build on a light background 16 | ![Standard theme](images/standard-light-error.png) 17 | ### Displaying standard streams 18 | ![Standard theme](images/standard-streams.png) 19 | 20 | ## Mocha theme 21 | 22 | ### Typical operation 23 | ![Mocha theme](images/mocha.gif) 24 | ### Typical operation on a light background 25 | ![Mocha theme](images/mocha-light.gif) 26 | ### Successful build 27 | ![Mocha theme](images/mocha.png) 28 | ### Successful build on a light background 29 | ![Mocha theme](images/mocha-light.png) 30 | ### Failed build 31 | ![Mocha theme](images/mocha-error.png) 32 | ### Failed build on a light background 33 | ![Mocha theme](images/mocha-light-error.png) 34 | ### Displaying standard streams 35 | ![Mocha theme](images/mocha-streams.png) 36 | 37 | ## Plain theme 38 | 39 | ### Typical operation 40 | ![Plain theme](images/plain.gif) 41 | ### Successful build 42 | ![Plain theme](images/plain.png) 43 | ### Successful build on a light background 44 | ![Plain theme](images/plain-light.png) 45 | ### Failed build 46 | ![Plain theme](images/plain-error.png) 47 | 48 | ## Standard parallel theme 49 | 50 | ### Typical operation 51 | ![Standard parallel theme](images/standard-parallel.gif) 52 | ### Failed build 53 | ![Standard parallel theme](images/standard-parallel-error.png) 54 | ### Displaying standard 55 | ![Standard parallel theme](images/standard-parallel-streams.png) 56 | 57 | ## Mocha parallel theme 58 | 59 | ### Typical operation 60 | ![Mocha parallel theme](images/mocha-parallel.gif) 61 | ### Failed build 62 | ![Mocha parallel theme](images/mocha-parallel-error.png) 63 | ### Displaying standard 64 | ![Mocha parallel theme](images/mocha-parallel-streams.png) 65 | 66 | ## Plain parallel theme 67 | 68 | ### Typical operation 69 | ![Plain parallel theme](images/plain-parallel.gif) 70 | -------------------------------------------------------------------------------- /docs/images/mocha-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-error.png -------------------------------------------------------------------------------- /docs/images/mocha-light-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-light-error.png -------------------------------------------------------------------------------- /docs/images/mocha-light.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-light.gif -------------------------------------------------------------------------------- /docs/images/mocha-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-light.png -------------------------------------------------------------------------------- /docs/images/mocha-parallel-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-parallel-error.png -------------------------------------------------------------------------------- /docs/images/mocha-parallel-streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-parallel-streams.png -------------------------------------------------------------------------------- /docs/images/mocha-parallel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-parallel.gif -------------------------------------------------------------------------------- /docs/images/mocha-streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-streams.png -------------------------------------------------------------------------------- /docs/images/mocha-theme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha-theme.gif -------------------------------------------------------------------------------- /docs/images/mocha.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha.gif -------------------------------------------------------------------------------- /docs/images/mocha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/mocha.png -------------------------------------------------------------------------------- /docs/images/plain-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/plain-error.png -------------------------------------------------------------------------------- /docs/images/plain-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/plain-light.png -------------------------------------------------------------------------------- /docs/images/plain-parallel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/plain-parallel.gif -------------------------------------------------------------------------------- /docs/images/plain.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/plain.gif -------------------------------------------------------------------------------- /docs/images/plain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/plain.png -------------------------------------------------------------------------------- /docs/images/standard-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-error.png -------------------------------------------------------------------------------- /docs/images/standard-light-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-light-error.png -------------------------------------------------------------------------------- /docs/images/standard-light.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-light.gif -------------------------------------------------------------------------------- /docs/images/standard-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-light.png -------------------------------------------------------------------------------- /docs/images/standard-parallel-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-parallel-error.png -------------------------------------------------------------------------------- /docs/images/standard-parallel-streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-parallel-streams.png -------------------------------------------------------------------------------- /docs/images/standard-parallel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-parallel.gif -------------------------------------------------------------------------------- /docs/images/standard-streams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-streams.png -------------------------------------------------------------------------------- /docs/images/standard-theme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard-theme.gif -------------------------------------------------------------------------------- /docs/images/standard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard.gif -------------------------------------------------------------------------------- /docs/images/standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/docs/images/standard.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=4.0.0 2 | group=com.adarshr 3 | 4 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 5 | org.gradle.caching=true 6 | org.gradle.parallel=true 7 | -------------------------------------------------------------------------------- /gradle/coverage.gradle: -------------------------------------------------------------------------------- 1 | jacoco { 2 | toolVersion '0.8.10' 3 | } 4 | 5 | jacocoTestReport { 6 | executionData layout.buildDirectory.file("jacoco/test.exec").get().asFile, layout.buildDirectory.file("jacoco/functionalTest.exec").get().asFile 7 | 8 | reports { 9 | xml.required = true 10 | html.required = true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /gradle/publishing.gradle: -------------------------------------------------------------------------------- 1 | gradlePlugin { 2 | website = 'https://github.com/radarsh/gradle-test-logger-plugin' 3 | vcsUrl = 'https://github.com/radarsh/gradle-test-logger-plugin' 4 | 5 | plugins { 6 | testLoggerPlugin { 7 | id = 'com.adarshr.test-logger' 8 | displayName = 'Gradle Test Logger Plugin' 9 | description = 'A Gradle plugin for printing beautiful logs on the console while running tests' 10 | implementationClass = 'com.adarshr.gradle.testlogger.TestLoggerPlugin' 11 | description = 'A Gradle plugin for printing beautiful logs on the console while running tests' 12 | tags = ['test', 'logging', 'console', 'terminal', 'groovy'] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsh/gradle-test-logger-plugin/1835aec91cf53b2dd68a89493ee9890c7c9cdf7e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ], 6 | "packageRules": [ 7 | { 8 | "groupName": "Spek2", 9 | "matchPackagePrefixes": [ 10 | "org.spekframework.spek2" 11 | ] 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | 7 | plugins { 8 | id 'com.gradle.enterprise' version '3.15.1' 9 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' 10 | } 11 | 12 | dependencyResolutionManagement { 13 | repositories { 14 | mavenCentral() 15 | } 16 | } 17 | 18 | def isCiBuild = providers.environmentVariable("CI").isPresent() 19 | 20 | gradleEnterprise { 21 | buildScan { 22 | termsOfServiceUrl = 'https://gradle.com/terms-of-service' 23 | termsOfServiceAgree = 'yes' 24 | publishAlwaysIf(isCiBuild) 25 | } 26 | } 27 | 28 | rootProject.name = 'gradle-test-logger-plugin' 29 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/TestDescriptorWrapper.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger 2 | 3 | import groovy.transform.CompileDynamic 4 | import groovy.transform.CompileStatic 5 | import org.gradle.api.tasks.testing.TestDescriptor 6 | 7 | import static com.adarshr.gradle.testlogger.util.RendererUtils.escape 8 | 9 | @CompileStatic 10 | class TestDescriptorWrapper { 11 | 12 | @Delegate 13 | private final TestDescriptor testDescriptor 14 | private final TestLoggerExtension testLoggerExtension 15 | 16 | final List ancestors 17 | final int depth 18 | final String trail 19 | 20 | TestDescriptorWrapper(TestDescriptor testDescriptor, TestLoggerExtension testLoggerExtension, List ancestors) { 21 | this.testDescriptor = testDescriptor 22 | this.testLoggerExtension = testLoggerExtension 23 | this.depth = ancestors.size() 24 | this.ancestors = ancestors 25 | this.trail = ancestors.collect { it.displayName }.join(' > ') 26 | } 27 | 28 | @CompileDynamic 29 | String getId() { 30 | testDescriptor.properties.id 31 | } 32 | 33 | String getDisplayName() { 34 | if (!depth && className && className.endsWith(testDescriptor.displayName)) { 35 | if (testLoggerExtension.showSimpleNames) { 36 | return escape(simpleClassName) 37 | } 38 | 39 | return escape(testDescriptor.className) 40 | } 41 | 42 | escape(testDescriptor.displayName) 43 | } 44 | 45 | private String getSimpleClassName() { 46 | def clazzName = testDescriptor.className 47 | 48 | clazzName = clazzName.substring(clazzName.lastIndexOf('.') + 1) 49 | clazzName = clazzName.substring(clazzName.lastIndexOf('$') + 1) 50 | 51 | clazzName 52 | } 53 | 54 | String getTrail() { 55 | trail 56 | } 57 | 58 | int getDepth() { 59 | depth 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/TestLoggerExtensionProperties.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger 2 | 3 | import com.adarshr.gradle.testlogger.theme.ThemeType 4 | import org.gradle.api.logging.LogLevel 5 | 6 | abstract class TestLoggerExtensionProperties { 7 | 8 | /** 9 | * Test logger theme. Defaults to {@link ThemeType#STANDARD}. 10 | */ 11 | ThemeType theme 12 | 13 | /** 14 | * Log level used to print the messages. Defaults to {@link LogLevel#LIFECYCLE} 15 | */ 16 | LogLevel logLevel 17 | 18 | /** 19 | * Whether exception information is to be shown. Defaults to true. 20 | */ 21 | Boolean showExceptions 22 | 23 | /** 24 | * Whether causes of exceptions are to be shown. Defaults to true. 25 | */ 26 | Boolean showCauses 27 | 28 | /** 29 | * Whether exception stack traces are to be shown. Defaults to true. 30 | */ 31 | Boolean showStackTraces 32 | 33 | /** 34 | * Whether full (unfiltered) stack traces are to be shown. Defaults to false. 35 | */ 36 | Boolean showFullStackTraces 37 | 38 | /** 39 | * Threshold in milliseconds to highlight slow tests. Defaults to 2000 milliseconds. 40 | */ 41 | Long slowThreshold 42 | 43 | /** 44 | * Whether a summary of tests is shown in the end. Defaults to true. 45 | */ 46 | Boolean showSummary 47 | 48 | /** 49 | * Whether standard streams are to be shown. Defaults to false. 50 | */ 51 | Boolean showStandardStreams 52 | 53 | /** 54 | * Whether standard streams for passed tests are to be shown. Defaults to true. 55 | */ 56 | Boolean showPassedStandardStreams 57 | 58 | /** 59 | * Whether standard streams for skipped tests are to be shown. Defaults to true. 60 | */ 61 | Boolean showSkippedStandardStreams 62 | 63 | /** 64 | * Whether standard streams for failed tests are to be shown. Defaults to true. 65 | */ 66 | Boolean showFailedStandardStreams 67 | 68 | /** 69 | * Whether passed tests should be shown. Defaults to true. 70 | */ 71 | Boolean showPassed 72 | 73 | /** 74 | * Whether skipped tests should be shown. Defaults to true. 75 | */ 76 | Boolean showSkipped 77 | 78 | /** 79 | * Whether failed tests should be shown. Defaults to true. 80 | */ 81 | Boolean showFailed 82 | 83 | /** 84 | * Whether simple class names should be used for displaying test suites. Defaults to false. 85 | */ 86 | Boolean showSimpleNames 87 | 88 | /** 89 | * Whether only slow tests should be shown. Defaults to false. 90 | */ 91 | Boolean showOnlySlow 92 | } 93 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/TestLoggerPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger 2 | 3 | import com.adarshr.gradle.testlogger.logger.TestLoggerWrapper 4 | import groovy.transform.CompileStatic 5 | import org.gradle.api.GradleException 6 | import org.gradle.api.Plugin 7 | import org.gradle.api.Project 8 | import org.gradle.api.tasks.testing.Test 9 | import org.gradle.util.GradleVersion 10 | 11 | @CompileStatic 12 | class TestLoggerPlugin implements Plugin { 13 | 14 | private static final String EXTENSION_NAME = 'testlogger' 15 | private static final String MIN_GRADLE = "7.6" 16 | 17 | @Override 18 | void apply(Project project) { 19 | if (GradleVersion.current() <= GradleVersion.version(MIN_GRADLE)) { 20 | throw new GradleException("Gradle Test Logger Plugin requires Gradle version ${MIN_GRADLE} or above.") 21 | } 22 | 23 | project.extensions.create(EXTENSION_NAME, TestLoggerExtension, project) 24 | 25 | project.tasks.withType(Test).configureEach { Test test -> 26 | def testExtension = test.extensions.create(EXTENSION_NAME, TestLoggerExtension, project, test) 27 | 28 | testExtension.originalTestLoggingEvents = test.testLogging.events 29 | test.testLogging.lifecycle.events = [] 30 | 31 | def testLogger = new TestLoggerWrapper(project.gradle.startParameter, test, testExtension) 32 | 33 | test.addTestListener(testLogger) 34 | test.addTestOutputListener(testLogger) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/TestResultWrapper.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger 2 | 3 | import com.adarshr.gradle.testlogger.util.TimeUtils 4 | import groovy.transform.CompileStatic 5 | import org.gradle.api.tasks.testing.TestResult 6 | 7 | @CompileStatic 8 | class TestResultWrapper { 9 | 10 | @Delegate 11 | private final TestResult testResult 12 | private final TestLoggerExtension testLoggerExtension 13 | 14 | TestResultWrapper(TestResult testResult, TestLoggerExtension testLoggerExtension) { 15 | this.testResult = testResult 16 | this.testLoggerExtension = testLoggerExtension 17 | } 18 | 19 | boolean isLoggable() { 20 | boolean showPassed = testLoggerExtension.showPassed && testResult.successfulTestCount 21 | boolean showSkipped = testLoggerExtension.showSkipped && (testResult.resultType == TestResult.ResultType.SKIPPED || testResult.skippedTestCount) 22 | boolean showFailed = testLoggerExtension.showFailed && testResult.failedTestCount 23 | if (showPassed || showSkipped || showFailed) { 24 | boolean showSlow = testLoggerExtension.showOnlySlow && (!isTooSlow() || !isMediumSlow()) 25 | if (showSlow) { 26 | return false 27 | } 28 | return true 29 | } 30 | return false 31 | } 32 | 33 | boolean isStandardStreamLoggable() { 34 | loggable && testLoggerExtension.showStandardStreams && 35 | (testLoggerExtension.showPassedStandardStreams && testResult.successfulTestCount || 36 | testLoggerExtension.showSkippedStandardStreams && testResult.skippedTestCount || 37 | testLoggerExtension.showFailedStandardStreams && testResult.failedTestCount) 38 | } 39 | 40 | boolean isTooSlow() { 41 | (testResult.endTime - testResult.startTime) >= testLoggerExtension.slowThreshold 42 | } 43 | 44 | boolean isMediumSlow() { 45 | (testResult.endTime - testResult.startTime) >= testLoggerExtension.slowThreshold / 2 46 | } 47 | 48 | String getDuration() { 49 | TimeUtils.humanDuration(testResult.endTime - testResult.startTime) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/ConsoleLogger.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.renderer.AnsiTextRenderer 4 | import com.adarshr.gradle.testlogger.renderer.TextRenderer 5 | import groovy.transform.CompileStatic 6 | import org.gradle.api.logging.LogLevel 7 | import org.gradle.api.logging.Logger 8 | 9 | import static com.adarshr.gradle.testlogger.util.RendererUtils.unescape 10 | 11 | @CompileStatic 12 | class ConsoleLogger { 13 | 14 | private final Logger logger 15 | private final LogLevel level 16 | private final TextRenderer renderer 17 | 18 | ConsoleLogger(Logger logger, LogLevel level, TextRenderer renderer = new AnsiTextRenderer()) { 19 | this.logger = logger 20 | this.level = level 21 | this.renderer = renderer 22 | } 23 | 24 | void log(String text) { 25 | if (text) { 26 | logger.log(level, unescape(renderer.render(text))) 27 | } 28 | } 29 | 30 | void logNewLine() { 31 | logger.log(level, '') 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/OutputCollector.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import groovy.transform.CompileStatic 5 | 6 | @CompileStatic 7 | class OutputCollector { 8 | 9 | private static final Closure mappingFunction = { 10 | new StringBuilder() 11 | } 12 | 13 | private final Map collector = [:] 14 | 15 | void collect(TestDescriptorWrapper descriptor, String output) { 16 | collector.computeIfAbsent(descriptor.id, mappingFunction) << output 17 | } 18 | 19 | String pop(TestDescriptorWrapper descriptor) { 20 | def output = collector.computeIfAbsent(descriptor.id, mappingFunction).toString() 21 | collector.remove(descriptor.id).length = 0 22 | output 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/ParallelTestLogger.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | import org.gradle.api.tasks.testing.TestDescriptor 8 | 9 | @CompileStatic 10 | @InheritConstructors 11 | class ParallelTestLogger extends TestLoggerAdapter { 12 | 13 | @Override 14 | void beforeSuite(TestDescriptorWrapper suite) { 15 | if (!suite.parent) { 16 | logger.logNewLine() 17 | } 18 | } 19 | 20 | @Override 21 | void afterSuite(TestDescriptorWrapper suite, TestResultWrapper result) { 22 | logger.log theme.suiteStandardStreamText(suite, outputCollector.pop(suite), result) 23 | 24 | if (!suite.parent) { 25 | logger.logNewLine() 26 | logger.log theme.summaryText(suite, result) 27 | } 28 | } 29 | 30 | @Override 31 | void afterTest(TestDescriptorWrapper descriptor, TestResultWrapper result) { 32 | def testText = theme.testText(descriptor, result) 33 | 34 | if (testText) { 35 | logger.log testText 36 | 37 | descriptor.ancestors.each { ancestor -> 38 | logger.log theme.suiteStandardStreamText(descriptor, outputCollector.pop(ancestor), result) 39 | } 40 | 41 | logger.log theme.testStandardStreamText(descriptor, outputCollector.pop(descriptor), result) 42 | } 43 | } 44 | 45 | @Override 46 | protected TestDescriptorWrapper wrap(TestDescriptor descriptor) { 47 | List rawAncestors = [] 48 | List wrappedAncestors = [] 49 | 50 | def current = descriptor.parent 51 | 52 | while (current && current.name && !isGradleSuite(current.name)) { 53 | rawAncestors << current 54 | current = current.parent 55 | } 56 | 57 | rawAncestors.reverse().withIndex().collect { TestDescriptor anc, int i -> 58 | wrappedAncestors << new TestDescriptorWrapper(anc, testLoggerExtension, wrappedAncestors.subList(0, i)) 59 | } 60 | 61 | new TestDescriptorWrapper(descriptor, testLoggerExtension, wrappedAncestors) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/SequentialTestLogger.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | @InheritConstructors 9 | @CompileStatic 10 | class SequentialTestLogger extends TestLoggerAdapter { 11 | 12 | private final Map suites = [:] 13 | 14 | private String lastLogged = 'suite' 15 | private int lastDepth = 0 16 | 17 | @Override 18 | void beforeSuite(TestDescriptorWrapper suite) { 19 | suites << [(suite.id): false] 20 | } 21 | 22 | @Override 23 | void afterSuite(TestDescriptorWrapper suite, TestResultWrapper result) { 24 | logger.log theme.suiteStandardStreamText(suite, outputCollector.pop(suite), result) 25 | 26 | if (!suite.parent) { 27 | logger.logNewLine() 28 | logger.log theme.summaryText(suite, result) 29 | } 30 | 31 | suites.remove(suite.id) 32 | } 33 | 34 | @Override 35 | void afterTest(TestDescriptorWrapper descriptor, TestResultWrapper result) { 36 | descriptor.ancestors 37 | .findAll { ancestor -> !suites[ancestor.id] } 38 | .each { ancestor -> 39 | def suiteText = theme.suiteText(ancestor, result) 40 | 41 | if (suiteText) { 42 | if (lastLogged == 'test') { 43 | logger.logNewLine() 44 | } 45 | 46 | logger.log theme.suiteStandardStreamText(ancestor, outputCollector.pop(ancestor), result) 47 | logger.log suiteText 48 | lastLogged = 'suite' 49 | 50 | suites[ancestor.id] = true 51 | } 52 | } 53 | 54 | def testText = theme.testText(descriptor, result) 55 | 56 | if (testText) { 57 | if (lastLogged == 'test' && lastDepth > descriptor.depth) { 58 | logger.logNewLine() 59 | } 60 | 61 | lastLogged = 'test' 62 | lastDepth = descriptor.depth 63 | } 64 | 65 | logger.log testText 66 | logger.log theme.testStandardStreamText(descriptor, outputCollector.pop(descriptor), result) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/TestLogger.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import org.gradle.api.tasks.testing.TestListener 4 | import org.gradle.api.tasks.testing.TestOutputListener 5 | 6 | 7 | interface TestLogger extends TestListener, TestOutputListener { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/TestLoggerAdapter.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestLoggerExtension 5 | import com.adarshr.gradle.testlogger.TestResultWrapper 6 | import com.adarshr.gradle.testlogger.theme.Theme 7 | import groovy.transform.CompileDynamic 8 | import groovy.transform.CompileStatic 9 | import org.gradle.api.logging.Logger 10 | import org.gradle.api.tasks.testing.TestDescriptor 11 | import org.gradle.api.tasks.testing.TestOutputEvent 12 | import org.gradle.api.tasks.testing.TestResult 13 | 14 | import java.util.concurrent.ConcurrentHashMap 15 | 16 | @CompileStatic 17 | class TestLoggerAdapter implements TestLogger { 18 | 19 | protected final Theme theme 20 | protected final ConsoleLogger logger 21 | protected final OutputCollector outputCollector 22 | protected final TestLoggerExtension testLoggerExtension 23 | private final Deque ancestors 24 | private final Map descriptorCache = new ConcurrentHashMap<>() 25 | 26 | TestLoggerAdapter(Logger logger, TestLoggerExtension testLoggerExtension, Theme theme) { 27 | this.logger = new ConsoleLogger(logger, testLoggerExtension.logLevel) 28 | this.testLoggerExtension = testLoggerExtension 29 | this.theme = theme 30 | this.outputCollector = new OutputCollector() 31 | ancestors = new ArrayDeque<>() 32 | } 33 | 34 | @Override 35 | final void beforeSuite(TestDescriptor descriptor) { 36 | def wrappedDescriptor = checkAndWrap(descriptor) 37 | 38 | ancestors.push(wrappedDescriptor) 39 | 40 | if (isGradleSuite(wrappedDescriptor.name)) { 41 | ancestors.clear() 42 | } 43 | 44 | if (!descriptor.parent) { 45 | logger.logNewLine() 46 | } 47 | 48 | beforeSuite(wrappedDescriptor) 49 | } 50 | 51 | protected void beforeSuite(TestDescriptorWrapper descriptor) { 52 | } 53 | 54 | @Override 55 | final void afterSuite(TestDescriptor descriptor, TestResult result) { 56 | if (!ancestors.empty) { 57 | ancestors.pop() 58 | } 59 | if (!descriptor.parent) { 60 | ancestors.clear() 61 | } 62 | 63 | afterSuite(checkAndWrap(descriptor), wrap(result)) 64 | 65 | descriptorCache.remove(id(descriptor)) 66 | } 67 | 68 | protected void afterSuite(TestDescriptorWrapper descriptor, TestResultWrapper result) { 69 | } 70 | 71 | @Override 72 | final void beforeTest(TestDescriptor descriptor) { 73 | beforeTest(checkAndWrap(descriptor)) 74 | } 75 | 76 | protected void beforeTest(TestDescriptorWrapper descriptor) { 77 | } 78 | 79 | @Override 80 | final void afterTest(TestDescriptor descriptor, TestResult result) { 81 | afterTest(checkAndWrap(descriptor), wrap(result)) 82 | 83 | descriptorCache.remove(id(descriptor)) 84 | } 85 | 86 | protected void afterTest(TestDescriptorWrapper descriptor, TestResultWrapper result) { 87 | } 88 | 89 | @Override 90 | void onOutput(TestDescriptor descriptor, TestOutputEvent outputEvent) { 91 | if (testLoggerExtension.showStandardStreams) { 92 | outputCollector.collect(checkAndWrap(descriptor), outputEvent.message) 93 | } 94 | } 95 | 96 | private TestDescriptorWrapper checkAndWrap(TestDescriptor descriptor) { 97 | descriptorCache.computeIfAbsent(id(descriptor)) { wrap(descriptor) } 98 | } 99 | 100 | @CompileDynamic 101 | private static String id(TestDescriptor descriptor) { 102 | descriptor.properties.id 103 | } 104 | 105 | protected TestDescriptorWrapper wrap(TestDescriptor descriptor) { 106 | new TestDescriptorWrapper(descriptor, testLoggerExtension, ancestors.toList().reverse()) 107 | } 108 | 109 | protected TestResultWrapper wrap(TestResult result) { 110 | new TestResultWrapper(result, testLoggerExtension) 111 | } 112 | 113 | protected boolean isGradleSuite(String name) { 114 | name.startsWith('Gradle Test Executor') || 115 | name.startsWith('Gradle suite') || 116 | name.startsWith('Gradle test') 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/logger/TestLoggerWrapper.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestLoggerExtension 4 | import com.adarshr.gradle.testlogger.theme.Theme 5 | import com.adarshr.gradle.testlogger.theme.ThemeFactory 6 | import groovy.transform.CompileStatic 7 | import org.gradle.StartParameter 8 | import org.gradle.api.tasks.testing.Test 9 | 10 | @CompileStatic 11 | class TestLoggerWrapper implements TestLogger { 12 | 13 | private final StartParameter startParameter 14 | private final Test test 15 | private final TestLoggerExtension testLoggerExtension 16 | 17 | private TestLogger testLoggerDelegate 18 | 19 | TestLoggerWrapper(StartParameter startParameter, Test test, TestLoggerExtension testLoggerExtension) { 20 | this.startParameter = startParameter 21 | this.test = test 22 | this.testLoggerExtension = testLoggerExtension 23 | } 24 | 25 | @Delegate 26 | TestLogger getTestLoggerDelegate() { 27 | if (testLoggerDelegate) { 28 | return testLoggerDelegate 29 | } 30 | 31 | Theme theme = ThemeFactory.getTheme(startParameter, test, testLoggerExtension) 32 | 33 | if (theme.type.parallel) { 34 | testLoggerDelegate = new ParallelTestLogger(test.logger, testLoggerExtension, theme) 35 | } else { 36 | testLoggerDelegate = new SequentialTestLogger(test.logger, testLoggerExtension, theme) 37 | } 38 | 39 | return testLoggerDelegate 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/renderer/AnsiTextRenderer.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | import static com.adarshr.gradle.testlogger.renderer.CharHandlers.HANDLERS 6 | 7 | @CompileStatic 8 | class AnsiTextRenderer implements TextRenderer { 9 | 10 | @Override 11 | String render(String input) { 12 | RenderingContext context = new RenderingContext() 13 | 14 | input.chars.each { char ch -> 15 | if (HANDLERS.containsKey(ch)) { 16 | HANDLERS[ch].handle(ch, context) 17 | return 18 | } 19 | 20 | if (context.inTag) { 21 | context.tag << ch 22 | return 23 | } 24 | 25 | context << ch 26 | } 27 | 28 | context 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/renderer/CharHandler.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | interface CharHandler { 4 | 5 | void handle(char ch, RenderingContext context) 6 | } 7 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/renderer/CharHandlers.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | class CharHandlers { 7 | 8 | private static final CharHandler backslash = { char ch, RenderingContext context -> 9 | context.escaped = true 10 | } as CharHandler 11 | 12 | private static final CharHandler leftBracket = { char ch, RenderingContext context -> 13 | if (context.escaped) { 14 | context << ch 15 | context.escaped = false 16 | return 17 | } 18 | 19 | context.beginTag() 20 | } as CharHandler 21 | 22 | private static final CharHandler rightBracket = { char ch, RenderingContext context -> 23 | if (context.escaped) { 24 | context << ch 25 | context.escaped = false 26 | return 27 | } 28 | 29 | context.endTag() 30 | } as CharHandler 31 | 32 | public static final Map HANDLERS = [ 33 | ('[' as char): leftBracket, 34 | (']' as char): rightBracket, 35 | ('\\' as char): backslash, 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/renderer/RenderingContext.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | import groovy.transform.CompileStatic 4 | import org.fusesource.jansi.Ansi 5 | 6 | import static org.fusesource.jansi.Ansi.Erase.* 7 | import static org.fusesource.jansi.Ansi.ansi 8 | 9 | @CompileStatic 10 | class RenderingContext implements Appendable { 11 | 12 | //@formatter:off 13 | private static final Map> TAG_MAPPING = [ 14 | 'bold' : { Ansi ansi -> ansi.bold() }, 15 | 'bold-off' : { Ansi ansi -> ansi.boldOff() }, 16 | 'default' : { Ansi ansi -> ansi.fgDefault() }, 17 | 'grey' : { Ansi ansi -> ansi.fgBrightBlack()}, 18 | 'red' : { Ansi ansi -> ansi.fgRed() }, 19 | 'bright-red' : { Ansi ansi -> ansi.fgBrightRed() }, 20 | 'green' : { Ansi ansi -> ansi.fgGreen() }, 21 | 'bright-green' : { Ansi ansi -> ansi.fgBrightGreen() }, 22 | 'yellow' : { Ansi ansi -> ansi.fgYellow() }, 23 | 'bright-yellow' : { Ansi ansi -> ansi.fgBrightYellow() }, 24 | 'cyan' : { Ansi ansi -> ansi.fgCyan() }, 25 | 'bright-cyan' : { Ansi ansi -> ansi.fgBrightCyan() }, 26 | 'blue' : { Ansi ansi -> ansi.fgBlue() }, 27 | 'bright-blue' : { Ansi ansi -> ansi.fgBrightBlue() }, 28 | 'magenta' : { Ansi ansi -> ansi.fgMagenta() }, 29 | 'bright-magenta' : { Ansi ansi -> ansi.fgBrightMagenta() }, 30 | 'cursor-up-line' : { Ansi ansi -> ansi.cursorUpLine() }, 31 | 'erase-all' : { Ansi ansi -> ansi.eraseLine(ALL) }, 32 | 'erase-ahead' : { Ansi ansi -> ansi.eraseLine(FORWARD) }, 33 | 'erase-back' : { Ansi ansi -> ansi.eraseLine(BACKWARD) }, 34 | '/' : { Ansi ansi -> ansi.reset() } 35 | ] 36 | // @formatter:on 37 | 38 | private final Ansi ansi 39 | StringBuilder tag 40 | boolean inTag 41 | boolean escaped 42 | 43 | RenderingContext() { 44 | ansi = ansi() 45 | tag = new StringBuilder() 46 | } 47 | 48 | void beginTag() { 49 | inTag = true 50 | } 51 | 52 | void endTag() { 53 | def tags = tag.toString().split(',') 54 | tags.each { String tag -> 55 | if (TAG_MAPPING.containsKey(tag)) { 56 | def mapping = TAG_MAPPING[tag] 57 | 58 | mapping.call(ansi) 59 | } else { 60 | ansi.a("[${tag}]") 61 | } 62 | } 63 | 64 | this.tag = new StringBuilder() 65 | inTag = false 66 | } 67 | 68 | @Override 69 | RenderingContext append(CharSequence csq) { 70 | ansi.a(csq) 71 | this 72 | } 73 | 74 | @Override 75 | RenderingContext append(CharSequence csq, int start, int end) { 76 | ansi.a(csq, start, end) 77 | this 78 | } 79 | 80 | @Override 81 | RenderingContext append(char c) { 82 | ansi.a(c) 83 | this 84 | } 85 | 86 | @Override 87 | String toString() { 88 | ansi.toString() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/renderer/TextRenderer.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | interface TextRenderer { 4 | 5 | String render(String input) 6 | } 7 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/AbstractTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestLoggerExtension 5 | import com.adarshr.gradle.testlogger.TestResultWrapper 6 | import groovy.transform.CompileStatic 7 | 8 | import static com.adarshr.gradle.testlogger.util.RendererUtils.escape 9 | import static java.lang.System.lineSeparator 10 | 11 | @CompileStatic 12 | abstract class AbstractTheme implements Theme { 13 | 14 | protected final TestLoggerExtension extension 15 | 16 | protected AbstractTheme(TestLoggerExtension extension) { 17 | this.extension = extension 18 | } 19 | 20 | @Override 21 | final String suiteText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 22 | result.loggable ? suiteTextInternal(descriptor) : '' 23 | } 24 | 25 | protected abstract String suiteTextInternal(TestDescriptorWrapper descriptor) 26 | 27 | @Override 28 | final String testText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 29 | result.loggable ? testTextInternal(descriptor, result) : '' 30 | } 31 | 32 | protected abstract String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) 33 | 34 | @Override 35 | String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 36 | exceptionText(descriptor, result, 2) 37 | } 38 | 39 | @Override 40 | final String suiteStandardStreamText(TestDescriptorWrapper descriptor, String lines, TestResultWrapper result) { 41 | result.loggable ? suiteStandardStreamTextInternal(descriptor, lines) : '' 42 | } 43 | 44 | protected abstract suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) 45 | 46 | @Override 47 | final String testStandardStreamText(TestDescriptorWrapper descriptor, String lines, TestResultWrapper result) { 48 | result.standardStreamLoggable ? testStandardStreamTextInternal(descriptor, lines) : '' 49 | } 50 | 51 | protected abstract testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) 52 | 53 | protected String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 54 | def line = new StringBuilder() 55 | 56 | if (!extension.showExceptions || result.exception == null) { 57 | return line 58 | } 59 | 60 | line << "${lineSeparator()}${lineSeparator()}" 61 | 62 | new StackTracePrinter(descriptor, ' ' * indent, line, extension) 63 | .printStackTrace(result.exception) 64 | .toString() 65 | } 66 | 67 | @CompileStatic 68 | private static class StackTracePrinter { 69 | final TestDescriptorWrapper descriptor 70 | final String indentation 71 | final StringBuilder line 72 | final TestLoggerExtension extension 73 | 74 | StackTracePrinter(TestDescriptorWrapper descriptor, String indentation, StringBuilder line, TestLoggerExtension extension) { 75 | this.descriptor = descriptor 76 | this.indentation = indentation 77 | this.line = line 78 | this.extension = extension 79 | } 80 | 81 | StackTracePrinter printStackTrace(Throwable exception, List parentStackTrace = [], boolean cause = false) { 82 | if (cause) { 83 | line << "${indentation}Caused by: " 84 | } 85 | 86 | line << message(exception, cause) 87 | line << lineSeparator() 88 | 89 | if (!extension.showStackTraces) { 90 | return this 91 | } 92 | 93 | def filteredTrace = filter(exception.stackTrace) 94 | 95 | line << stackTrace(filteredTrace, countCommonFrames(parentStackTrace, filteredTrace)) 96 | line << lineSeparator() 97 | 98 | if (extension.showCauses && exception.cause) { 99 | printStackTrace(exception.cause, filteredTrace, true) 100 | } 101 | 102 | this 103 | } 104 | 105 | String message(Throwable exception, boolean cause) { 106 | exception.toString() 107 | .trim() 108 | .readLines() 109 | .withIndex() 110 | .collect { String message, index -> 111 | "${index != 0 || !cause ? indentation : ''}${escape(message)}" 112 | }.join(lineSeparator()) 113 | } 114 | 115 | String stackTrace(List stackTrace, int commonFrames) { 116 | def trace = new StringBuilder(stackTrace 117 | .subList(0, stackTrace.size() - commonFrames) 118 | .collect { 119 | "${indentation} at ${escape(it.toString())}" 120 | }.join(lineSeparator())) 121 | 122 | if (commonFrames) { 123 | trace << "${lineSeparator()}${indentation} ... ${commonFrames} more" 124 | } 125 | 126 | trace.toString() 127 | } 128 | 129 | int countCommonFrames(List parentStackTrace, List stackTrace) { 130 | int count = 0 131 | 132 | if (parentStackTrace.empty) { 133 | return count; 134 | } 135 | 136 | int i = stackTrace.size() - 1, j = parentStackTrace.size() - 1 137 | 138 | while (i >= 1 && j >= 0 && stackTrace[i] == parentStackTrace[j]) { 139 | ++count; i--; j-- 140 | } 141 | 142 | count 143 | } 144 | 145 | List filter(StackTraceElement[] stackTrace) { 146 | if (extension.showFullStackTraces) { 147 | return stackTrace.toList() 148 | } 149 | 150 | stackTrace.find { 151 | it.className == descriptor.className 152 | }.collect { 153 | it as StackTraceElement 154 | } 155 | } 156 | 157 | @Override 158 | String toString() { 159 | line.toString() 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/MochaParallelTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | import static com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA_PARALLEL 9 | 10 | @CompileStatic 11 | @InheritConstructors 12 | class MochaParallelTheme extends MochaTheme { 13 | 14 | ThemeType type = MOCHA_PARALLEL 15 | 16 | @Override 17 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 18 | '' 19 | } 20 | 21 | @Override 22 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 23 | super.testTextInternal(" [erase-ahead,default]${descriptor.trail} ", descriptor, result) 24 | } 25 | 26 | @Override 27 | String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 28 | super.exceptionText(descriptor, result, 4) 29 | } 30 | 31 | @Override 32 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 33 | super.summaryText(descriptor, result, 2) 34 | } 35 | 36 | @Override 37 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 38 | super.standardStreamTextInternal(lines, 4) 39 | } 40 | 41 | @Override 42 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 43 | super.standardStreamTextInternal(lines, 4) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/MochaTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | import org.gradle.api.tasks.testing.TestResult.ResultType 8 | 9 | import static com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA 10 | import static com.adarshr.gradle.testlogger.util.RendererUtils.escape 11 | import static java.lang.System.lineSeparator 12 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 13 | 14 | @CompileStatic 15 | @InheritConstructors 16 | class MochaTheme extends AbstractTheme { 17 | 18 | ThemeType type = MOCHA 19 | 20 | @Override 21 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 22 | " ${' ' * descriptor.depth}[erase-ahead,default]${descriptor.displayName}[/]${lineSeparator()}" 23 | } 24 | 25 | @Override 26 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 27 | testTextInternal(" ${' ' * descriptor.depth}[erase-ahead]", descriptor, result) 28 | } 29 | 30 | protected String testTextInternal(String start, TestDescriptorWrapper descriptor, TestResultWrapper result) { 31 | def line = new StringBuilder(start) 32 | 33 | switch (result.resultType) { 34 | case SUCCESS: 35 | line << "[green]${getSymbol(result.resultType)}[grey] ${descriptor.displayName}" 36 | showDurationIfSlow(result, line) 37 | break 38 | case FAILURE: 39 | line << "[red]${getSymbol(result.resultType)} ${descriptor.displayName}" 40 | showDurationIfSlow(result, line) 41 | line << exceptionText(descriptor, result) 42 | break 43 | case SKIPPED: 44 | line << "[cyan]${getSymbol(result.resultType)} ${descriptor.displayName}" 45 | break 46 | } 47 | 48 | line << '[/]' 49 | } 50 | 51 | private static void showDurationIfSlow(TestResultWrapper result, StringBuilder line) { 52 | if (result.tooSlow) { 53 | line << "[red] (${result.duration})" 54 | } else if (result.mediumSlow) { 55 | line << "[yellow] (${result.duration})" 56 | } 57 | } 58 | 59 | private static String getSymbol(ResultType resultType) { 60 | switch (resultType) { 61 | case SUCCESS: return windows ? '√' : '✔' 62 | case FAILURE: return windows ? 'X' : '✘' 63 | case SKIPPED: return '-' 64 | } 65 | } 66 | 67 | private static boolean getWindows() { 68 | System.getProperty('os.name').startsWith('Windows') 69 | } 70 | 71 | @Override 72 | String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 73 | exceptionText(descriptor, result, 6 * descriptor.depth) 74 | } 75 | 76 | @Override 77 | protected String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 78 | def exceptionText = super.exceptionText(descriptor, result, indent) 79 | 80 | exceptionText ? "[red]${exceptionText}" : '' 81 | } 82 | 83 | @Override 84 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 85 | return summaryText(descriptor, result, 2) 86 | } 87 | 88 | protected String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 89 | if (!extension.showSummary) { 90 | return '' 91 | } 92 | 93 | def indentation = ' ' * indent 94 | def line = new StringBuilder() 95 | 96 | line << "${indentation}[erase-ahead,green]${result.successfulTestCount} passing [grey](${result.duration})" 97 | 98 | if (result.skippedTestCount) { 99 | line << "${lineSeparator()}${indentation}[erase-ahead,cyan]${result.skippedTestCount} pending" 100 | } 101 | if (result.failedTestCount) { 102 | line << "${lineSeparator()}${indentation}[erase-ahead,red]${result.failedTestCount} failing" 103 | } 104 | 105 | line << "[/]${lineSeparator()}" 106 | } 107 | 108 | @Override 109 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 110 | standardStreamTextInternal(lines, descriptor.depth * 2 + 4) 111 | } 112 | 113 | @Override 114 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 115 | standardStreamTextInternal(lines, descriptor.depth * 2 + 6) 116 | } 117 | 118 | protected String standardStreamTextInternal(String lines, int indent) { 119 | if (!extension.showStandardStreams || !lines) { 120 | return '' 121 | } 122 | 123 | lines = escape(lines) 124 | 125 | def indentation = ' ' * indent 126 | def line = new StringBuilder("[grey]${lineSeparator()}") 127 | 128 | line << lines.split($/${lineSeparator()}/$).collect { 129 | "${indentation}${it}" 130 | }.join(lineSeparator()) 131 | 132 | line << "[/]${lineSeparator()}" 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/PlainParallelTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | import static com.adarshr.gradle.testlogger.theme.ThemeType.PLAIN_PARALLEL 9 | 10 | @CompileStatic 11 | @InheritConstructors 12 | class PlainParallelTheme extends PlainTheme { 13 | 14 | ThemeType type = PLAIN_PARALLEL 15 | 16 | @Override 17 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 18 | '' 19 | } 20 | 21 | @Override 22 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 23 | super.testTextInternal("${descriptor.trail} ${descriptor.displayName} ${RESULT_TYPE_MAPPING[result.resultType]}", descriptor, result) 24 | } 25 | 26 | @Override 27 | protected String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 28 | return super.exceptionText(descriptor, result, 2) 29 | } 30 | 31 | @Override 32 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 33 | super.standardStreamTextInternal(lines, 2) 34 | } 35 | 36 | @Override 37 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 38 | super.standardStreamTextInternal(lines, 2) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/PlainTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | import static com.adarshr.gradle.testlogger.theme.ThemeType.PLAIN 9 | import static com.adarshr.gradle.testlogger.util.RendererUtils.escape 10 | import static java.lang.System.lineSeparator 11 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 12 | 13 | @CompileStatic 14 | @InheritConstructors 15 | class PlainTheme extends AbstractTheme { 16 | 17 | ThemeType type = PLAIN 18 | 19 | protected static final Map RESULT_TYPE_MAPPING = [ 20 | (SUCCESS): 'PASSED', 21 | (FAILURE): 'FAILED', 22 | (SKIPPED): 'SKIPPED' 23 | ] 24 | 25 | @Override 26 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 27 | "${' ' * descriptor.depth}${descriptor.displayName}${lineSeparator()}" 28 | } 29 | 30 | @Override 31 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 32 | testTextInternal("${' ' * descriptor.depth}Test ${descriptor.displayName} ${RESULT_TYPE_MAPPING[result.resultType]}", descriptor, result) 33 | } 34 | 35 | protected String testTextInternal(String start, TestDescriptorWrapper descriptor, TestResultWrapper result) { 36 | def line = new StringBuilder(start) 37 | 38 | if (result.tooSlow) { 39 | line << " (${result.duration})" 40 | } 41 | 42 | if (result.resultType == FAILURE) { 43 | line << exceptionText(descriptor, result) 44 | } 45 | 46 | line 47 | } 48 | 49 | @Override 50 | protected String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 51 | super.exceptionText(descriptor, result, getType().parallel ? indent : indent * descriptor.depth) 52 | } 53 | 54 | @Override 55 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 56 | if (!extension.showSummary) { 57 | return '' 58 | } 59 | 60 | def line = new StringBuilder() 61 | 62 | line << "${result.resultType}: " 63 | line << "Executed ${result.testCount} tests in ${result.duration}" 64 | 65 | def breakdown = getBreakdown(result) 66 | 67 | if (breakdown) { 68 | line << ' (' << breakdown.join(', ') << ')' 69 | } 70 | 71 | line << lineSeparator() 72 | } 73 | 74 | private static List getBreakdown(TestResultWrapper result) { 75 | def breakdown = [] 76 | 77 | if (result.failedTestCount) { 78 | breakdown << "${result.failedTestCount} failed" 79 | } 80 | 81 | if (result.skippedTestCount) { 82 | breakdown << "${result.skippedTestCount} skipped" 83 | } 84 | 85 | breakdown 86 | } 87 | 88 | @Override 89 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 90 | standardStreamTextInternal(lines, descriptor.depth * 2 + 2) 91 | } 92 | 93 | @Override 94 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 95 | standardStreamTextInternal(lines, descriptor.depth * 2 + 2) 96 | } 97 | 98 | protected String standardStreamTextInternal(String lines, int indent) { 99 | if (!extension.showStandardStreams || !lines) { 100 | return '' 101 | } 102 | 103 | lines = escape(lines) 104 | 105 | def indentation = ' ' * indent 106 | def line = new StringBuilder(lineSeparator()) 107 | 108 | line << lines.split($/${lineSeparator()}/$).collect { 109 | "${indentation}${it}" 110 | }.join(lineSeparator()) 111 | 112 | line << lineSeparator() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/StandardParallelTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | import static com.adarshr.gradle.testlogger.theme.ThemeType.STANDARD_PARALLEL 9 | 10 | @CompileStatic 11 | @InheritConstructors 12 | class StandardParallelTheme extends StandardTheme { 13 | 14 | ThemeType type = STANDARD_PARALLEL 15 | 16 | @Override 17 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 18 | '' 19 | } 20 | 21 | @Override 22 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 23 | super.testTextInternal("[erase-ahead,bold]${descriptor.trail}[bold-off] ${descriptor.displayName}", descriptor, result) 24 | } 25 | 26 | @Override 27 | String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 28 | super.exceptionText(descriptor, result, 2) 29 | } 30 | 31 | @Override 32 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 33 | super.standardStreamTextInternal(lines, 2) 34 | } 35 | 36 | @Override 37 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 38 | super.standardStreamTextInternal(lines, 2) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/StandardTheme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | import groovy.transform.CompileStatic 6 | import groovy.transform.InheritConstructors 7 | 8 | import static com.adarshr.gradle.testlogger.theme.ThemeType.STANDARD 9 | import static com.adarshr.gradle.testlogger.util.RendererUtils.escape 10 | import static java.lang.System.lineSeparator 11 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 12 | 13 | @CompileStatic 14 | @InheritConstructors 15 | class StandardTheme extends AbstractTheme { 16 | 17 | ThemeType type = STANDARD 18 | 19 | @Override 20 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 21 | "[erase-ahead,bold]${' ' * descriptor.depth}${descriptor.displayName}[/]${lineSeparator()}" 22 | } 23 | 24 | @Override 25 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 26 | testTextInternal("[erase-ahead,bold]${' ' * descriptor.depth}Test [bold-off]${descriptor.displayName}", descriptor, result) 27 | } 28 | 29 | protected String testTextInternal(String start, TestDescriptorWrapper descriptor, TestResultWrapper result) { 30 | def line = new StringBuilder(start) 31 | 32 | switch (result.resultType) { 33 | case SUCCESS: 34 | line << '[green] PASSED' 35 | showDurationIfSlow(result, line) 36 | break 37 | case FAILURE: 38 | line << '[red] FAILED' 39 | showDurationIfSlow(result, line) 40 | line << exceptionText(descriptor, result) 41 | break 42 | case SKIPPED: 43 | line << '[yellow] SKIPPED' 44 | break 45 | } 46 | 47 | line << '[/]' 48 | } 49 | 50 | private void showDurationIfSlow(TestResultWrapper result, StringBuilder line) { 51 | if (result.tooSlow) { 52 | line << "[red] (${result.duration})" 53 | } else if (result.mediumSlow) { 54 | line << "[yellow] (${result.duration})" 55 | } 56 | } 57 | 58 | @Override 59 | protected String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result, int indent) { 60 | def exceptionText = super.exceptionText(descriptor, result, getType().parallel ? indent : indent * descriptor.depth) 61 | 62 | exceptionText ? "[red]${exceptionText}" : '' 63 | } 64 | 65 | @Override 66 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 67 | if (!extension.showSummary) { 68 | return '' 69 | } 70 | 71 | def colour = result.resultType == FAILURE ? 'red' : 'green' 72 | def line = new StringBuilder() 73 | 74 | line << "[erase-ahead,bold,${colour}]${result.resultType}: " 75 | line << "[default]Executed ${result.testCount} tests in ${result.duration}" 76 | 77 | def breakdown = getBreakdown(result) 78 | 79 | if (breakdown) { 80 | line << ' (' << breakdown.join(', ') << ')' 81 | } 82 | 83 | line << "[/]${lineSeparator()}" 84 | } 85 | 86 | private static List getBreakdown(TestResultWrapper result) { 87 | def breakdown = [] 88 | 89 | if (result.failedTestCount) { 90 | breakdown << "${result.failedTestCount} failed" 91 | } 92 | 93 | if (result.skippedTestCount) { 94 | breakdown << "${result.skippedTestCount} skipped" 95 | } 96 | 97 | breakdown 98 | } 99 | 100 | @Override 101 | protected String suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 102 | standardStreamTextInternal(lines, descriptor.depth * 2 + 2) 103 | } 104 | 105 | @Override 106 | protected String testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 107 | standardStreamTextInternal(lines, descriptor.depth * 2 + 2) 108 | } 109 | 110 | protected String standardStreamTextInternal(String lines, int indent) { 111 | if (!extension.showStandardStreams || !lines) { 112 | return '' 113 | } 114 | 115 | lines = escape(lines) 116 | 117 | def indentation = ' ' * indent 118 | def line = new StringBuilder("[default]${lineSeparator()}") 119 | 120 | line << lines.split($/${lineSeparator()}/$).collect { 121 | "${indentation}${it}" 122 | }.join(lineSeparator()) 123 | 124 | line << "[/]${lineSeparator()}" 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/Theme.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestResultWrapper 5 | 6 | interface Theme { 7 | 8 | ThemeType getType() 9 | 10 | String suiteText(TestDescriptorWrapper descriptor, TestResultWrapper result) 11 | 12 | String testText(TestDescriptorWrapper descriptor, TestResultWrapper result) 13 | 14 | String exceptionText(TestDescriptorWrapper descriptor, TestResultWrapper result) 15 | 16 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) 17 | 18 | String suiteStandardStreamText(TestDescriptorWrapper descriptor, String lines, TestResultWrapper result) 19 | 20 | String testStandardStreamText(TestDescriptorWrapper descriptor, String lines, TestResultWrapper result) 21 | } 22 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/ThemeFactory.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestLoggerExtension 4 | import groovy.transform.CompileStatic 5 | import org.gradle.StartParameter 6 | import org.gradle.api.tasks.testing.Test 7 | import org.gradle.api.tasks.testing.testng.TestNGOptions 8 | 9 | import static com.adarshr.gradle.testlogger.theme.ThemeType.PLAIN 10 | import static com.adarshr.gradle.testlogger.theme.ThemeType.fromName 11 | import static org.gradle.api.logging.configuration.ConsoleOutput.Plain 12 | 13 | @CompileStatic 14 | class ThemeFactory { 15 | 16 | static Theme getTheme(StartParameter startParameter, Test test, TestLoggerExtension extension) { 17 | resolveThemeType(startParameter, test, extension).themeClass.newInstance(extension) 18 | } 19 | 20 | private static ThemeType resolveThemeType(StartParameter startParameter, Test test, TestLoggerExtension extension) { 21 | ThemeType themeType = extension.theme 22 | 23 | if (startParameter.consoleOutput == Plain) { 24 | themeType = PLAIN 25 | } 26 | 27 | if (isParallelMode(test) && !themeType.parallel) { 28 | themeType = fromName(themeType.parallelFallback) 29 | } 30 | 31 | if (themeType != extension.theme) { 32 | test.logger.info("Test logger theme for task ${test.name} overridden " + 33 | "from ${extension.theme.name} to ${themeType.name}") 34 | } 35 | 36 | themeType 37 | } 38 | 39 | private static boolean isParallelMode(Test test) { 40 | boolean testNgParallelMode = test.options instanceof TestNGOptions && (test.options as TestNGOptions).parallel 41 | 42 | test.maxParallelForks > 1 || testNgParallelMode 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/theme/ThemeType.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import groovy.transform.CompileStatic 4 | import org.gradle.api.GradleException 5 | 6 | @CompileStatic 7 | enum ThemeType { 8 | //@formatter:off 9 | // name parallel themeClass parallelFallback 10 | PLAIN( 'plain', false, PlainTheme, 'plain-parallel'), 11 | PLAIN_PARALLEL( 'plain-parallel', true, PlainParallelTheme, null), 12 | STANDARD( 'standard', false, StandardTheme, 'standard-parallel'), 13 | STANDARD_PARALLEL( 'standard-parallel', true, StandardParallelTheme, null), 14 | MOCHA( 'mocha', false, MochaTheme, 'mocha-parallel'), 15 | MOCHA_PARALLEL( 'mocha-parallel', true, MochaParallelTheme, null) 16 | //@formatter:on 17 | 18 | final String name 19 | final boolean parallel 20 | final Class themeClass 21 | final String parallelFallback 22 | 23 | ThemeType(String name, boolean parallel, Class themeClass, String parallelFallback = null) { 24 | this.name = name 25 | this.parallel = parallel 26 | this.themeClass = themeClass 27 | this.parallelFallback = parallelFallback 28 | 29 | assert parallel || parallelFallback: 'A non-parallel theme must have a parallel fallback' 30 | } 31 | 32 | static ThemeType fromName(String name) { 33 | def themeType = values().find { 34 | it.name == name 35 | } 36 | 37 | if (!themeType) { 38 | throw new GradleException("Unknown theme '${name}'. Must be one of ${allThemeNames}") 39 | } 40 | 41 | themeType 42 | } 43 | 44 | static String getAllThemeNames() { 45 | values().collect { ThemeType themeType -> 46 | "'${themeType.name}'" 47 | }.join(', ') 48 | } 49 | 50 | static String getParallelThemeNames() { 51 | values().findAll { 52 | it.parallel 53 | }.collect { 54 | "'${it.name}'" 55 | }.join(', ') 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/util/RendererUtils.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.util 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | class RendererUtils { 7 | 8 | static String escape(String text) { 9 | text?.replace('[', '\\[')?.replace(']', '\\]') 10 | } 11 | 12 | static String unescape(String text) { 13 | text?.replace('\\[', '[')?.replace('\\]', ']') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/groovy/com/adarshr/gradle/testlogger/util/TimeUtils.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.util 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | @CompileStatic 6 | final class TimeUtils { 7 | 8 | private static final int HOUR = 3600 * 1000 9 | private static final int MINUTE = 60 * 1000 10 | private static final int SECOND = 1000 11 | 12 | static String humanDuration(long millis) { 13 | def duration = [:] 14 | 15 | if (millis >= HOUR) { 16 | duration.h = millis / HOUR as int 17 | duration.m = (millis / MINUTE as int) % 60 18 | } else if (millis >= MINUTE) { 19 | duration.m = millis / MINUTE as int 20 | duration.s = (millis / SECOND as int) % 60 21 | } else if (millis >= SECOND) { 22 | duration.s = (millis / SECOND * 10 as int) / 10 23 | } else if (millis > 0) { 24 | duration.ms = millis 25 | } else { 26 | duration.ms = '0' 27 | } 28 | 29 | duration.entrySet().findAll { 30 | it.value 31 | }.collect { 32 | "${it.value}${it.key}" 33 | }.join(' ') 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test-functional/groovy/com/adarshr/gradle/testlogger/functional/AbstractFunctionalSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.functional 2 | 3 | import com.adarshr.gradle.testlogger.renderer.AnsiTextRenderer 4 | import org.gradle.testkit.runner.BuildResult 5 | import org.gradle.testkit.runner.GradleRunner 6 | import org.gradle.testkit.runner.UnexpectedBuildFailure 7 | import org.gradle.util.GradleVersion 8 | import spock.lang.Specification 9 | import spock.lang.TempDir 10 | import spock.util.environment.OperatingSystem 11 | 12 | import java.nio.file.Files 13 | import java.nio.file.Path 14 | 15 | import static java.lang.System.lineSeparator 16 | import static org.apache.commons.io.FileUtils.copyDirectoryToDirectory 17 | 18 | @SuppressWarnings('GrMethodMayBeStatic') 19 | abstract class AbstractFunctionalSpec extends Specification { 20 | 21 | private static final String TEST_ROOT = 'src/test-functional/resources' 22 | 23 | @TempDir 24 | Path temporaryFolder 25 | 26 | private static final def START_MARKER = '__START__' 27 | private static final def END_MARKER = '__END__' 28 | private static final def SUMMARY_MARKER = '__SUMMARY__' 29 | private static final def SUITE_MARKER = '__SUITE=' 30 | private static final def TEST_MARKER = '__TEST=' 31 | private static final def SUITE_MARKER_REGEX = $/${SUITE_MARKER}(.*)__/$ 32 | private static final def TEST_MARKER_REGEX = $/${TEST_MARKER}(.*)__/$ 33 | 34 | private static final List> FILTER_PATTERNS = [ 35 | [pattern: $/(?ms)${lineSeparator()}Unexpected exception thrown.*?${lineSeparator() * 2}/$, replacement: ''], 36 | [pattern: $/(?ms)> Task .*?${lineSeparator()}/$, replacement: ''], 37 | [pattern: $/(?m)${lineSeparator()}.*EngineDiscoveryOrchestrator.*${lineSeparator()}/$, replacement: ''], 38 | [pattern: $/(?m).*tests were Method or class mismatch.*${lineSeparator() * 2}/$, replacement: ''], 39 | [pattern: $/app///$, replacement: ''], 40 | [pattern: $/(?m).*EngineDiscoveryOrchestrator.*/$, replacement: ''], 41 | [pattern: $/(?m).*tests were Method or class mismatch.*/$, replacement: ''], 42 | ] 43 | 44 | private AnsiTextRenderer ansi = new AnsiTextRenderer() 45 | 46 | protected TestLoggerOutput getLoggerOutput(String text) { 47 | text = filterLines(text) 48 | 49 | def allLines = text.readLines() 50 | def lines = allLines 51 | .subList(allLines.indexOf(START_MARKER) + 1, allLines.indexOf(SUMMARY_MARKER)) 52 | .findAll { !it.startsWith(TEST_MARKER) } 53 | def summary = allLines.subList(allLines.indexOf(SUMMARY_MARKER) + 1, allLines.indexOf(END_MARKER)) 54 | def map = new LinkedHashMap>() 55 | 56 | lines.each { line -> 57 | if (line.startsWith(SUITE_MARKER)) { 58 | map << [(line.replaceFirst(SUITE_MARKER_REGEX, '$1')): []] 59 | } else { 60 | map.values().last() << line 61 | } 62 | } 63 | 64 | new TestLoggerOutput(lines: map.sort().values().flatten(), summary: summary) 65 | } 66 | 67 | protected TestLoggerOutput getParallelLoggerOutput(String text) { 68 | text = filterLines(text) 69 | 70 | def allLines = text.readLines() 71 | def lines = allLines 72 | .subList(allLines.indexOf(START_MARKER) + 1, allLines.indexOf(SUMMARY_MARKER)) 73 | .findAll { !it.startsWith(SUITE_MARKER) } 74 | def summary = allLines.subList(allLines.indexOf(SUMMARY_MARKER) + 1, allLines.indexOf(END_MARKER)) 75 | def map = new LinkedHashMap>() 76 | 77 | lines.each { line -> 78 | if (line.startsWith(TEST_MARKER)) { 79 | map << [(line.replaceFirst(TEST_MARKER_REGEX, '$1')): []] 80 | } else { 81 | map.values().last() << line 82 | } 83 | } 84 | 85 | new TestLoggerOutput(lines: map.sort().values().flatten(), summary: summary) 86 | } 87 | 88 | protected TestLoggerOutput getNestedLoggerOutput(String text) { 89 | text = filterLines(text) 90 | 91 | def allLines = text.readLines() 92 | def lines = allLines 93 | .subList(allLines.indexOf(START_MARKER) + 1, allLines.indexOf(SUMMARY_MARKER)) 94 | .findAll { !it.startsWith(SUITE_MARKER) && !it.startsWith(TEST_MARKER) } 95 | def summary = allLines.subList(allLines.indexOf(SUMMARY_MARKER) + 1, allLines.indexOf(END_MARKER)) 96 | 97 | new TestLoggerOutput(lines: lines, summary: summary) 98 | } 99 | 100 | private String filterLines(String text) { 101 | FILTER_PATTERNS.each { it -> 102 | text = text.replaceAll(it.pattern, it.replacement) 103 | } 104 | 105 | text 106 | } 107 | 108 | protected String render(String ansiText) { 109 | ansi.render(ansiText) 110 | } 111 | 112 | protected BuildResult run(String project, String args) { 113 | run(project, '', args) 114 | } 115 | 116 | protected BuildResult run(String project, String buildFragment, String args) { 117 | copyDirectoryToDirectory(new File("${TEST_ROOT}/${project}"), temporaryFolder.toFile()) 118 | 119 | def buildFile = temporaryFolder.resolve("${project}/build.gradle").toFile() 120 | def testMarkerFile = Files.createFile(temporaryFolder.resolve("${project}/test-marker.gradle")).toFile() 121 | 122 | testMarkerFile << new File(TEST_ROOT, 'test-marker.gradle').text 123 | buildFile << buildFragment 124 | 125 | runProject(temporaryFolder.resolve(project).toFile(), args) 126 | } 127 | 128 | private BuildResult runProject(File projectDir, String args) { 129 | try { 130 | GradleRunner.create() 131 | .withGradleVersion(GradleVersion.current().version) 132 | .withProjectDir(projectDir) 133 | .withPluginClasspath() 134 | .withDebug(true) 135 | .withArguments(args.split(' ')) 136 | .forwardOutput() 137 | .build() 138 | } catch (UnexpectedBuildFailure e) { 139 | e.buildResult 140 | } 141 | } 142 | 143 | protected static String getPassedSymbol() { 144 | OperatingSystem.current.windows ? '√' : '✔' 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test-functional/groovy/com/adarshr/gradle/testlogger/functional/TestLoggerOutput.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.functional 2 | 3 | class TestLoggerOutput { 4 | 5 | List lines = [] 6 | List summary = [] 7 | } 8 | -------------------------------------------------------------------------------- /src/test-functional/groovy/com/adarshr/gradle/testlogger/functional/ThemeSwitchingSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.functional 2 | 3 | 4 | import spock.util.environment.OperatingSystem 5 | 6 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS 7 | 8 | class ThemeSwitchingSpec extends AbstractFunctionalSpec { 9 | 10 | def "log spock tests when plain theme is set"() { 11 | when: 12 | def result = run( 13 | 'single-spock-test', 14 | ''' 15 | testlogger { 16 | theme 'plain' 17 | slowThreshold 5000 18 | } 19 | ''', 20 | 'clean test' 21 | ) 22 | then: 23 | def lines = getLoggerOutput(result.output).lines 24 | and: 25 | lines.size() == 4 26 | lines[0] == render('') 27 | lines[1] == render('com.adarshr.test.SingleSpec') 28 | lines[2] == render('') 29 | lines[3] == render(' Test this is a single test PASSED') 30 | 31 | and: 32 | result.task(":test").outcome == SUCCESS 33 | } 34 | 35 | def "fallback to plain theme when --console plain is specified"() { 36 | when: 37 | def result = run( 38 | 'single-spock-test', 39 | ''' 40 | testlogger { 41 | theme 'standard' 42 | slowThreshold 5000 43 | } 44 | ''', 45 | 'clean test --console plain' 46 | ) 47 | then: 48 | def lines = getLoggerOutput(result.output).lines 49 | and: 50 | lines.size() == 4 51 | lines[0] == render('') 52 | lines[1] == render('com.adarshr.test.SingleSpec') 53 | lines[2] == render('') 54 | lines[3] == render(' Test this is a single test PASSED') 55 | and: 56 | result.task(":test").outcome == SUCCESS 57 | } 58 | 59 | def "log spock tests when standard theme is set"() { 60 | when: 61 | def result = run( 62 | 'single-spock-test', 63 | ''' 64 | testlogger { 65 | theme 'standard' 66 | slowThreshold 5000 67 | } 68 | ''', 69 | 'clean test' 70 | ) 71 | then: 72 | def lines = getLoggerOutput(result.output).lines 73 | and: 74 | lines.size() == 4 75 | lines[0] == render('') 76 | lines[1] == render('[erase-ahead,bold]com.adarshr.test.SingleSpec[/]') 77 | lines[2] == render('') 78 | lines[3] == render('[erase-ahead,bold] Test [bold-off]this is a single test[green] PASSED[/]') 79 | and: 80 | result.task(":test").outcome == SUCCESS 81 | } 82 | 83 | def "log spock tests when mocha theme is set"() { 84 | when: 85 | def result = run( 86 | 'single-spock-test', 87 | ''' 88 | testlogger { 89 | theme 'mocha' 90 | slowThreshold 5000 91 | } 92 | ''', 93 | 'clean test' 94 | ) 95 | then: 96 | def lines = getLoggerOutput(result.output).lines 97 | and: 98 | lines.size() == 4 99 | lines[0] == render('') 100 | lines[1] == render(' [erase-ahead,default]com.adarshr.test.SingleSpec[/]') 101 | lines[2] == render('') 102 | lines[3] == render(" [erase-ahead][green]${symbol}[grey] this is a single test[/]") 103 | and: 104 | result.task(":test").outcome == SUCCESS 105 | } 106 | 107 | def "theme can be overridden using system property"() { 108 | when: 109 | def result = run( 110 | 'single-spock-test', 111 | ''' 112 | testlogger { 113 | theme 'mocha' 114 | slowThreshold 5000 115 | } 116 | test.doLast { 117 | System.clearProperty('testlogger.theme') 118 | } 119 | ''', 120 | 'clean test -Dtestlogger.theme=plain' 121 | ) 122 | then: 123 | def lines = getLoggerOutput(result.output).lines 124 | and: 125 | lines.size() == 4 126 | lines[0] == render('') 127 | lines[1] == render('com.adarshr.test.SingleSpec') 128 | lines[2] == render('') 129 | lines[3] == render(' Test this is a single test PASSED') 130 | and: 131 | result.task(":test").outcome == SUCCESS 132 | } 133 | 134 | private static String getSymbol() { 135 | OperatingSystem.current.windows ? '√' : '✔' 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.codehaus.groovy:groovy:3.0.19' 16 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/src/test/groovy/com/adarshr/test/FiveSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class FiveSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println "${getClass().simpleName} - stdout setupSpec" 9 | System.err.println "${getClass().simpleName} - stderr setupSpec" 10 | } 11 | 12 | def cleanupSpec() { 13 | println "${getClass().simpleName} - stdout cleanupSpec" 14 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 15 | } 16 | 17 | def setup() { 18 | println "${getClass().simpleName} - stdout setup" 19 | System.err.println "${getClass().simpleName} - stderr setup" 20 | sleep Math.round(Math.random() * 500) 21 | } 22 | 23 | def cleanup() { 24 | println "${getClass().simpleName} - stdout cleanup" 25 | System.err.println "${getClass().simpleName} - stderr cleanup" 26 | } 27 | 28 | def "this is test one"() { 29 | expect: 30 | println "${getClass().simpleName} - stdout expect" 31 | System.err.println "${getClass().simpleName} - stderr expect" 32 | true 33 | } 34 | 35 | def "this is test two"() { 36 | expect: 37 | println "${getClass().simpleName} - stdout expect" 38 | System.err.println "${getClass().simpleName} - stderr expect" 39 | true 40 | } 41 | 42 | def "this is test three"() { 43 | expect: 44 | println "${getClass().simpleName} - stdout expect" 45 | System.err.println "${getClass().simpleName} - stderr expect" 46 | true 47 | } 48 | 49 | def "this is test four"() { 50 | expect: 51 | println "${getClass().simpleName} - stdout expect" 52 | System.err.println "${getClass().simpleName} - stderr expect" 53 | true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/src/test/groovy/com/adarshr/test/FourSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class FourSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println "${getClass().simpleName} - stdout setupSpec" 9 | System.err.println "${getClass().simpleName} - stderr setupSpec" 10 | } 11 | 12 | def cleanupSpec() { 13 | println "${getClass().simpleName} - stdout cleanupSpec" 14 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 15 | } 16 | 17 | def setup() { 18 | println "${getClass().simpleName} - stdout setup" 19 | System.err.println "${getClass().simpleName} - stderr setup" 20 | sleep Math.round(Math.random() * 500) 21 | } 22 | 23 | def cleanup() { 24 | println "${getClass().simpleName} - stdout cleanup" 25 | System.err.println "${getClass().simpleName} - stderr cleanup" 26 | } 27 | 28 | def "this is test one"() { 29 | expect: 30 | println "${getClass().simpleName} - stdout expect" 31 | System.err.println "${getClass().simpleName} - stderr expect" 32 | true 33 | } 34 | 35 | def "this is test two"() { 36 | expect: 37 | println "${getClass().simpleName} - stdout expect" 38 | System.err.println "${getClass().simpleName} - stderr expect" 39 | true 40 | } 41 | 42 | def "this is test three"() { 43 | expect: 44 | println "${getClass().simpleName} - stdout expect" 45 | System.err.println "${getClass().simpleName} - stderr expect" 46 | true 47 | } 48 | 49 | def "this is test four"() { 50 | expect: 51 | println "${getClass().simpleName} - stdout expect" 52 | System.err.println "${getClass().simpleName} - stderr expect" 53 | true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/src/test/groovy/com/adarshr/test/OneSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class OneSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println "${getClass().simpleName} - stdout setupSpec" 9 | System.err.println "${getClass().simpleName} - stderr setupSpec" 10 | } 11 | 12 | def cleanupSpec() { 13 | println "${getClass().simpleName} - stdout cleanupSpec" 14 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 15 | } 16 | 17 | def setup() { 18 | println "${getClass().simpleName} - stdout setup" 19 | System.err.println "${getClass().simpleName} - stderr setup" 20 | sleep Math.round(Math.random() * 500) 21 | } 22 | 23 | def cleanup() { 24 | println "${getClass().simpleName} - stdout cleanup" 25 | System.err.println "${getClass().simpleName} - stderr cleanup" 26 | } 27 | 28 | def "this is test one"() { 29 | expect: 30 | println "${getClass().simpleName} - stdout expect" 31 | System.err.println "${getClass().simpleName} - stderr expect" 32 | true 33 | } 34 | 35 | def "this is test two"() { 36 | expect: 37 | println "${getClass().simpleName} - stdout expect" 38 | System.err.println "${getClass().simpleName} - stderr expect" 39 | true 40 | } 41 | 42 | def "this is test three"() { 43 | expect: 44 | println "${getClass().simpleName} - stdout expect" 45 | System.err.println "${getClass().simpleName} - stderr expect" 46 | true 47 | } 48 | 49 | def "this is test four"() { 50 | expect: 51 | println "${getClass().simpleName} - stdout expect" 52 | System.err.println "${getClass().simpleName} - stderr expect" 53 | true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/src/test/groovy/com/adarshr/test/ThreeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class ThreeSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println "${getClass().simpleName} - stdout setupSpec" 9 | System.err.println "${getClass().simpleName} - stderr setupSpec" 10 | } 11 | 12 | def cleanupSpec() { 13 | println "${getClass().simpleName} - stdout cleanupSpec" 14 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 15 | } 16 | 17 | def setup() { 18 | println "${getClass().simpleName} - stdout setup" 19 | System.err.println "${getClass().simpleName} - stderr setup" 20 | sleep Math.round(Math.random() * 500) 21 | } 22 | 23 | def cleanup() { 24 | println "${getClass().simpleName} - stdout cleanup" 25 | System.err.println "${getClass().simpleName} - stderr cleanup" 26 | } 27 | 28 | def "this is test one"() { 29 | expect: 30 | println "${getClass().simpleName} - stdout expect" 31 | System.err.println "${getClass().simpleName} - stderr expect" 32 | true 33 | } 34 | 35 | def "this is test two"() { 36 | expect: 37 | println "${getClass().simpleName} - stdout expect" 38 | System.err.println "${getClass().simpleName} - stderr expect" 39 | true 40 | } 41 | 42 | def "this is test three"() { 43 | expect: 44 | println "${getClass().simpleName} - stdout expect" 45 | System.err.println "${getClass().simpleName} - stderr expect" 46 | true 47 | } 48 | 49 | def "this is test four"() { 50 | expect: 51 | println "${getClass().simpleName} - stdout expect" 52 | System.err.println "${getClass().simpleName} - stderr expect" 53 | true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/many-spock-tests/src/test/groovy/com/adarshr/test/TwoSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class TwoSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println "${getClass().simpleName} - stdout setupSpec" 9 | System.err.println "${getClass().simpleName} - stderr setupSpec" 10 | } 11 | 12 | def cleanupSpec() { 13 | println "${getClass().simpleName} - stdout cleanupSpec" 14 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 15 | } 16 | 17 | def setup() { 18 | println "${getClass().simpleName} - stdout setup" 19 | System.err.println "${getClass().simpleName} - stderr setup" 20 | sleep Math.round(Math.random() * 500) 21 | } 22 | 23 | def cleanup() { 24 | println "${getClass().simpleName} - stdout cleanup" 25 | System.err.println "${getClass().simpleName} - stderr cleanup" 26 | } 27 | 28 | def "this is test one"() { 29 | expect: 30 | println "${getClass().simpleName} - stdout expect" 31 | System.err.println "${getClass().simpleName} - stderr expect" 32 | true 33 | } 34 | 35 | def "this is test two"() { 36 | expect: 37 | println "${getClass().simpleName} - stdout expect" 38 | System.err.println "${getClass().simpleName} - stderr expect" 39 | true 40 | } 41 | 42 | def "this is test three"() { 43 | expect: 44 | println "${getClass().simpleName} - stdout expect" 45 | System.err.println "${getClass().simpleName} - stderr expect" 46 | true 47 | } 48 | 49 | def "this is test four"() { 50 | expect: 51 | println "${getClass().simpleName} - stdout expect" 52 | System.err.println "${getClass().simpleName} - stderr expect" 53 | true 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit4-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'junit:junit:4.13.2' 16 | } 17 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit4-tests/src/test/java/com/adarshr/test/FirstTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import static org.junit.runners.MethodSorters.NAME_ASCENDING; 9 | 10 | 11 | @FixMethodOrder(NAME_ASCENDING) 12 | public class FirstTest { 13 | 14 | @Test 15 | public void thisTestShouldPass() { 16 | Assert.assertEquals(1, 1); 17 | } 18 | 19 | @Test 20 | public void thisTestShouldFail() { 21 | Assert.assertEquals(1, 2); 22 | } 23 | 24 | @Test 25 | @Ignore 26 | public void thisTestShouldBeSkipped() { 27 | Assert.assertEquals(1, 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit4-tests/src/test/java/com/adarshr/test/SecondTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import static org.junit.runners.MethodSorters.NAME_ASCENDING; 9 | 10 | 11 | @FixMethodOrder(NAME_ASCENDING) 12 | public class SecondTest { 13 | 14 | @Test 15 | public void thisTestShouldPass() { 16 | Assert.assertEquals(1, 1); 17 | } 18 | 19 | @Test 20 | public void thisTestShouldFail() { 21 | Assert.assertEquals(1, 2); 22 | } 23 | 24 | @Test 25 | @Ignore 26 | public void thisTestShouldBeSkipped() { 27 | Assert.assertEquals(1, 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-deep-nested-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' 16 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-deep-nested-tests/src/test/java/com/adarshr/test/DeepNestedTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.BeforeAll; 6 | import org.junit.jupiter.api.AfterAll; 7 | import org.junit.jupiter.api.Nested; 8 | import org.junit.jupiter.api.DisplayName; 9 | 10 | public class DeepNestedTest { 11 | 12 | 13 | @BeforeAll 14 | public static void beforeAllDeepNestedTest() { 15 | System.out.println("DeepNestedTest.beforeAllDeepNestedTest"); 16 | } 17 | 18 | @AfterAll 19 | public static void afterAllDeepNestedTest() { 20 | System.out.println("DeepNestedTest.afterAllDeepNestedTest"); 21 | } 22 | 23 | @Nested 24 | public class NestedTestsetLevelOne { 25 | 26 | @Test 27 | public void nestedTestsetLevelOne() { 28 | System.out.println("NestedTestsetLevelOne.nestedTestsetLevelOne"); 29 | Assertions.assertEquals(1, 1); 30 | } 31 | 32 | @Nested 33 | @DisplayName("Nested test set level two") 34 | public class NestedTestsetLevelTwo { 35 | 36 | @Test 37 | public void nestedTestsetLevelTwo() { 38 | System.out.println("NestedTestsetLevelTwo.nestedTestsetLevelTwo"); 39 | Assertions.assertEquals(1, 1); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-nested-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' 16 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-nested-tests/src/test/java/com/adarshr/test/NestedTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Disabled; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.Nested; 7 | 8 | public class NestedTest { 9 | 10 | @Nested 11 | public class NestedTestsetOne { 12 | 13 | @Test 14 | public void firstTestOfNestedTestsetOne() { 15 | Assertions.assertEquals(1, 1); 16 | } 17 | 18 | @Test 19 | public void secondTestOfNestedTestsetOne() { 20 | Assertions.assertEquals(1, 1); 21 | } 22 | } 23 | 24 | @Nested 25 | public class NestedTestsetTwo { 26 | 27 | @Test 28 | public void firstTestOfNestedTestsetTwo() { 29 | Assertions.assertEquals(1, 1); 30 | } 31 | 32 | @Test 33 | public void secondTestOfNestedTestsetTwo() { 34 | Assertions.assertEquals(1, 1); 35 | } 36 | } 37 | 38 | @Nested 39 | public class NestedTestsetThree { 40 | 41 | @Test 42 | public void firstTestOfNestedTestsetThree() { 43 | Assertions.assertEquals(1, 1); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' 16 | testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.0' 17 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' 18 | } 19 | 20 | test { 21 | useJUnitPlatform() 22 | } 23 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-tests/src/test/java/com/adarshr/test/FirstTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Disabled; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.DisplayName; 7 | 8 | public class FirstTest { 9 | 10 | @Test 11 | public void thisTestShouldPass() { 12 | Assertions.assertEquals(1, 1); 13 | } 14 | 15 | @Test 16 | @DisplayName("this test should fail") 17 | public void thisTestShouldFail() { 18 | Assertions.assertEquals(1, 2); 19 | } 20 | 21 | @Test 22 | @Disabled 23 | public void thisTestShouldBeSkipped() { 24 | Assertions.assertEquals(1, 1); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-tests/src/test/java/com/adarshr/test/ParamTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Disabled; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.api.DisplayName; 7 | import org.junit.jupiter.params.ParameterizedTest; 8 | import org.junit.jupiter.params.provider.MethodSource; 9 | 10 | import java.util.List; 11 | import java.util.Arrays; 12 | 13 | public class ParamTest { 14 | 15 | @MethodSource("allParams") 16 | @DisplayName("Testing parameterized tests") 17 | @ParameterizedTest(name = "param {0} is not null") 18 | public void testThisParam(String param) { 19 | Assertions.assertNotNull(param); 20 | } 21 | 22 | private static List allParams() { 23 | return Arrays.asList("One", "Two", "Three"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-jupiter-tests/src/test/java/com/adarshr/test/SecondTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Disabled; 5 | import org.junit.jupiter.api.Test; 6 | 7 | public class SecondTest { 8 | 9 | @Test 10 | public void thisTestShouldPass() { 11 | Assertions.assertEquals(1, 1); 12 | } 13 | 14 | @Test 15 | public void thisTestShouldFail() { 16 | Assertions.assertEquals(1, 2); 17 | } 18 | 19 | @Test 20 | @Disabled 21 | public void thisTestShouldBeSkipped() { 22 | Assertions.assertEquals(1, 1); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-vintage-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'junit:junit:4.13.2' 16 | testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.10.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-vintage-tests/src/test/java/com/adarshr/test/FirstTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import static org.junit.runners.MethodSorters.NAME_ASCENDING; 9 | 10 | 11 | @FixMethodOrder(NAME_ASCENDING) 12 | public class FirstTest { 13 | 14 | @Test 15 | public void thisTestShouldPass() { 16 | Assert.assertEquals(1, 1); 17 | } 18 | 19 | @Test 20 | public void thisTestShouldFail() { 21 | Assert.assertEquals(1, 2); 22 | } 23 | 24 | @Test 25 | @Ignore 26 | public void thisTestShouldBeSkipped() { 27 | Assert.assertEquals(1, 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-junit5-vintage-tests/src/test/java/com/adarshr/test/SecondTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | 8 | import static org.junit.runners.MethodSorters.NAME_ASCENDING; 9 | 10 | 11 | @FixMethodOrder(NAME_ASCENDING) 12 | public class SecondTest { 13 | 14 | @Test 15 | public void thisTestShouldPass() { 16 | Assert.assertEquals(1, 1); 17 | } 18 | 19 | @Test 20 | public void thisTestShouldFail() { 21 | Assert.assertEquals(1, 2); 22 | } 23 | 24 | @Test 25 | @Ignore 26 | public void thisTestShouldBeSkipped() { 27 | Assert.assertEquals(1, 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-kotest-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.jetbrains.kotlin.jvm' version '1.9.10' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.7.2' 16 | } 17 | 18 | test { 19 | useJUnitPlatform() 20 | } 21 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-kotest-tests/src/test/kotlin/com/adarshr/test/WordSpecTest.kt: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import io.kotest.core.spec.style.WordSpec 4 | import io.kotest.matchers.comparables.shouldBeGreaterThan 5 | import kotlin.time.Duration.Companion.milliseconds 6 | import kotlin.time.ExperimentalTime 7 | 8 | 9 | @ExperimentalTime 10 | class WordSpecTest : WordSpec() { 11 | 12 | init { 13 | "a context" should { 14 | "have a test" { 15 | 2.shouldBeGreaterThan(1) 16 | } 17 | "have another test" { 18 | 2.shouldBeGreaterThan(1) 19 | } 20 | "have a test with config".config(enabled = false) { 21 | 22 | } 23 | } 24 | 25 | "another context" When { 26 | 27 | "using when" Should { 28 | "have a test" { 29 | 2.shouldBeGreaterThan(1) 30 | } 31 | "have a test with config".config(timeout = 10000.milliseconds) { 32 | 2.shouldBeGreaterThan(1) 33 | } 34 | } 35 | 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spek-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.jetbrains.kotlin.jvm' version '1.9.10' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.jetbrains.kotlin:kotlin-test:1.9.10' 16 | testImplementation 'org.spekframework.spek2:spek-dsl-jvm:2.0.19' 17 | 18 | testRuntimeOnly 'org.spekframework.spek2:spek-runner-junit5:2.0.19' 19 | testRuntimeOnly 'org.jetbrains.kotlin:kotlin-reflect:1.9.10' 20 | } 21 | 22 | test { 23 | useJUnitPlatform { 24 | includeEngines 'spek2' 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spek-tests/src/test/kotlin/com/adarshr/test/Calculator.kt: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | 4 | class Calculator { 5 | fun add(a: Int, b: Int): Int { 6 | return a + b 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spek-tests/src/test/kotlin/com/adarshr/test/CalculatorSpec.kt: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import org.spekframework.spek2.Spek 4 | import org.spekframework.spek2.style.specification.describe 5 | import kotlin.test.assertEquals 6 | 7 | 8 | object CalculatorSpec: Spek({ 9 | describe("A calculator") { 10 | val calculator by memoized { Calculator() } 11 | 12 | describe("addition") { 13 | it("returns the sum of its arguments") { 14 | assertEquals(3, calculator.add(1, 2)) 15 | } 16 | } 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests-system-exit/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.codehaus.groovy:groovy:3.0.19' 16 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests-system-exit/src/test/groovy/com/adarshr/test/FirstSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Ignore 4 | import spock.lang.Narrative 5 | import spock.lang.Specification 6 | import spock.lang.Stepwise 7 | 8 | @Stepwise 9 | @Narrative('Test that calls System.exit from setup method') 10 | class FirstSpec extends Specification { 11 | 12 | def setupSpec() { 13 | println "${getClass().simpleName} - stdout setupSpec" 14 | System.err.println "${getClass().simpleName} - stderr setupSpec" 15 | } 16 | 17 | def cleanupSpec() { 18 | println "${getClass().simpleName} - stdout cleanupSpec" 19 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 20 | } 21 | 22 | def setup() { 23 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout setup" 24 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr setup" 25 | 26 | System.exit(5) 27 | } 28 | 29 | def cleanup() { 30 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout cleanup" 31 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr cleanup" 32 | } 33 | 34 | def "this test should pass"() { 35 | expect: 36 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 37 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 38 | 1 == 1 39 | } 40 | 41 | def "this test should fail"() { 42 | expect: 43 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 44 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 45 | 1 == 2 46 | } 47 | 48 | @Ignore 49 | def "this test should be skipped"() { 50 | expect: 51 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 52 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 53 | 1 == 2 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests-system-exit/src/test/groovy/com/adarshr/test/SecondSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Ignore 4 | import spock.lang.Narrative 5 | import spock.lang.Specification 6 | import spock.lang.Stepwise 7 | 8 | @Stepwise 9 | @Narrative('Test that calls System.exit from setupSpec method') 10 | class SecondSpec extends Specification { 11 | 12 | def setupSpec() { 13 | println "${getClass().simpleName} - stdout setupSpec" 14 | System.err.println "${getClass().simpleName} - stderr setupSpec" 15 | 16 | System.exit(5) 17 | } 18 | 19 | def cleanupSpec() { 20 | println "${getClass().simpleName} - stdout cleanupSpec" 21 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 22 | } 23 | 24 | def setup() { 25 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout setup" 26 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr setup" 27 | } 28 | 29 | def cleanup() { 30 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout cleanup" 31 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr cleanup" 32 | } 33 | 34 | def "this test should pass"() { 35 | expect: 36 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 37 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 38 | 1 == 1 39 | } 40 | 41 | def "this test should fail"() { 42 | expect: 43 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 44 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 45 | 1 == 2 46 | } 47 | 48 | @Ignore 49 | def "this test should be skipped"() { 50 | expect: 51 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 52 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 53 | 1 == 2 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.codehaus.groovy:groovy:3.0.19' 16 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests/src/test/groovy/com/adarshr/test/FirstSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Ignore 4 | import spock.lang.Specification 5 | import spock.lang.Stepwise 6 | 7 | @Stepwise 8 | class FirstSpec extends Specification { 9 | 10 | def setupSpec() { 11 | println "${getClass().simpleName} - stdout setupSpec" 12 | System.err.println "${getClass().simpleName} - stderr setupSpec" 13 | } 14 | 15 | def cleanupSpec() { 16 | println "${getClass().simpleName} - stdout cleanupSpec" 17 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 18 | } 19 | 20 | def setup() { 21 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout setup" 22 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr setup" 23 | } 24 | 25 | def cleanup() { 26 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout cleanup" 27 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr cleanup" 28 | } 29 | 30 | def "this test should pass"() { 31 | expect: 32 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 33 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 34 | 1 == 1 35 | } 36 | 37 | def "this test should fail"() { 38 | expect: 39 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 40 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 41 | 1 == 2 42 | } 43 | 44 | @Ignore 45 | def "this test should be skipped"() { 46 | expect: 47 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 48 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 49 | 1 == 2 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests/src/test/groovy/com/adarshr/test/SecondSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Ignore 4 | import spock.lang.Specification 5 | import spock.lang.Stepwise 6 | 7 | @Stepwise 8 | class SecondSpec extends Specification { 9 | 10 | def setupSpec() { 11 | println "${getClass().simpleName} - stdout setupSpec" 12 | System.err.println "${getClass().simpleName} - stderr setupSpec" 13 | } 14 | 15 | def cleanupSpec() { 16 | println "${getClass().simpleName} - stdout cleanupSpec" 17 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 18 | } 19 | 20 | def setup() { 21 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout setup" 22 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr setup" 23 | } 24 | 25 | def cleanup() { 26 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout cleanup" 27 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr cleanup" 28 | } 29 | 30 | def "this test should pass"() { 31 | expect: 32 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 33 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 34 | 1 == 1 35 | } 36 | 37 | def "this test should fail"() { 38 | expect: 39 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 40 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 41 | 1 == 2 42 | } 43 | 44 | @Ignore 45 | def "this test should be skipped"() { 46 | expect: 47 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 48 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 49 | 1 == 2 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-spock-tests/src/test/groovy/com/adarshr/test/ThirdSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | 4 | import spock.lang.Specification 5 | import spock.lang.Stepwise 6 | 7 | @Stepwise 8 | class ThirdSpec extends Specification { 9 | 10 | def setupSpec() { 11 | println "${getClass().simpleName} - stdout setupSpec" 12 | System.err.println "${getClass().simpleName} - stderr setupSpec" 13 | } 14 | 15 | def cleanupSpec() { 16 | println "${getClass().simpleName} - stdout cleanupSpec" 17 | System.err.println "${getClass().simpleName} - stderr cleanupSpec" 18 | } 19 | 20 | def setup() { 21 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout setup" 22 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr setup" 23 | } 24 | 25 | def cleanup() { 26 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout cleanup" 27 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr cleanup" 28 | } 29 | 30 | def "this test should pass"() { 31 | expect: 32 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 33 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 34 | 1 == 1 35 | } 36 | 37 | def "this test should also pass"() { 38 | expect: 39 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 40 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 41 | 1 == 1 42 | } 43 | 44 | def "this test should pass too"() { 45 | expect: 46 | println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stdout expect" 47 | System.err.println "${getClass().simpleName} - ${specificationContext.currentFeature.name} - stderr expect" 48 | 1 == 1 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-testng-tests/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.testng:testng:7.0.0' 16 | } 17 | 18 | test { 19 | useTestNG { 20 | preserveOrder = true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-testng-tests/src/test/java/com/adarshr/test/FirstTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.testng.Assert; 4 | import org.testng.annotations.Ignore; 5 | import org.testng.annotations.Test; 6 | 7 | public class FirstTest { 8 | 9 | @Test 10 | public void thisTestShouldPass() { 11 | Assert.assertEquals(1, 1); 12 | } 13 | 14 | @Test 15 | public void thisTestShouldFail() { 16 | Assert.assertEquals(1, 2); 17 | } 18 | 19 | @Ignore 20 | public void thisTestShouldBeSkipped() { 21 | Assert.assertEquals(1, 1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test-functional/resources/sample-testng-tests/src/test/java/com/adarshr/test/SecondTest.java: -------------------------------------------------------------------------------- 1 | package com.adarshr.test; 2 | 3 | import org.testng.Assert; 4 | import org.testng.annotations.Ignore; 5 | import org.testng.annotations.Test; 6 | 7 | public class SecondTest { 8 | 9 | @Test 10 | public void thisTestShouldPass() { 11 | Assert.assertEquals(1, 1); 12 | } 13 | 14 | @Test 15 | public void thisTestShouldFail() { 16 | Assert.assertEquals(1, 2); 17 | } 18 | 19 | @Ignore 20 | public void thisTestShouldBeSkipped() { 21 | Assert.assertEquals(1, 1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test-functional/resources/single-spock-test/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.codehaus.groovy:groovy:3.0.19' 16 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | -------------------------------------------------------------------------------- /src/test-functional/resources/single-spock-test/src/test/groovy/com/adarshr/test/SingleSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class SingleSpec extends Specification { 6 | 7 | def setupSpec() { 8 | println 'stdout setupSpec' 9 | System.err.println 'stderr setupSpec' 10 | } 11 | 12 | def cleanupSpec() { 13 | println 'stdout cleanupSpec' 14 | System.err.println 'stderr cleanupSpec' 15 | } 16 | 17 | def setup() { 18 | println 'stdout setup' 19 | System.err.println 'stderr setup' 20 | } 21 | 22 | def cleanup() { 23 | println 'stdout cleanup' 24 | System.err.println 'stderr cleanup' 25 | } 26 | 27 | def "this is a single test"() { 28 | expect: 29 | println 'stdout expect' 30 | System.err.println 'stderr expect' 31 | true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test-functional/resources/slow-spock-test/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'groovy' 3 | id 'com.adarshr.test-logger' apply false 4 | } 5 | 6 | apply from: 'test-marker.gradle' 7 | apply plugin: 'com.adarshr.test-logger' 8 | 9 | repositories { 10 | mavenLocal() 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testImplementation 'org.codehaus.groovy:groovy:3.0.19' 16 | testImplementation 'org.spockframework:spock-core:2.0-groovy-3.0' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } 22 | -------------------------------------------------------------------------------- /src/test-functional/resources/slow-spock-test/src/test/groovy/com/adarshr/test/SlowSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.test 2 | 3 | import spock.lang.Specification 4 | 5 | class SlowSpec extends Specification { 6 | 7 | def "this is a slow test"() { 8 | expect: 9 | sleep 3000 10 | true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test-functional/resources/test-marker.gradle: -------------------------------------------------------------------------------- 1 | ext.markerLevel = 'lifecycle' 2 | ext.printFirstNewLine = true 3 | 4 | LogLevel getMarkerLevel() { 5 | LogLevel.valueOf(project.property('markerLevel').toString().toUpperCase()) 6 | } 7 | 8 | boolean getPrintFirstNewLine() { 9 | project.property('printFirstNewLine').asBoolean() 10 | } 11 | 12 | tasks.withType(Test) { 13 | boolean started = false 14 | def suites = [] 15 | 16 | beforeSuite { suite -> 17 | if (suite.className?.startsWith('com.adarshr') && !started) { 18 | started = true 19 | logger.log markerLevel, '__START__' 20 | } 21 | if (suite.className?.startsWith('com.adarshr') && !suites.contains(suite.className)) { 22 | logger.log markerLevel, "__SUITE=${suite.className}__" 23 | suites << suite.className 24 | } 25 | } 26 | 27 | afterTest { suite, result -> 28 | if (suite.className.startsWith('com.adarshr') && 29 | result.resultType == TestResult.ResultType.SUCCESS && test.testlogger.showPassed || 30 | result.resultType == TestResult.ResultType.SKIPPED && test.testlogger.showSkipped || 31 | result.resultType == TestResult.ResultType.FAILURE && test.testlogger.showFailed) { 32 | 33 | logger.log markerLevel, "__TEST=${suite.className}:${suite.name}__${printFirstNewLine ? '\n' : ''}" 34 | 35 | if (printFirstNewLine) { 36 | printFirstNewLine = false 37 | } 38 | } 39 | } 40 | 41 | afterSuite { suite, result -> 42 | if (!suite.parent) { 43 | logger.log markerLevel, '__SUMMARY__' 44 | } 45 | } 46 | 47 | // Do after evaluate to make sure this listener comes after the plugin's 48 | afterEvaluate { 49 | afterSuite { suite, result -> 50 | if (!suite.parent) { 51 | logger.log markerLevel, '__END__' 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/TestDescriptorWrapperSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger 2 | 3 | import org.gradle.api.tasks.testing.TestDescriptor 4 | import spock.lang.Specification 5 | 6 | class TestDescriptorWrapperSpec extends Specification { 7 | 8 | def testDescriptorMock = GroovyMock(TestDescriptor) 9 | def testLoggerExtensionMock = Mock(TestLoggerExtension) 10 | def wrapper = new TestDescriptorWrapper(testDescriptorMock, testLoggerExtensionMock, [ 11 | Mock(TestDescriptorWrapper) { displayName >> 'great grand parent' }, 12 | Mock(TestDescriptorWrapper) { displayName >> 'grand parent' }, 13 | Mock(TestDescriptorWrapper) { displayName >> 'parent' }, 14 | ]) 15 | 16 | def "id delegates to id property"() { 17 | given: 18 | testDescriptorMock.properties >> [id: 'identifier'] 19 | expect: 20 | wrapper.id == 'identifier' 21 | } 22 | 23 | def "trail is constructed based on ancestors"() { 24 | given: 25 | testDescriptorMock.displayName >> 'child' 26 | expect: 27 | wrapper.trail == 'great grand parent > grand parent > parent' 28 | } 29 | 30 | def "depth is constructed based on ancestors"() { 31 | given: 32 | testDescriptorMock.displayName >> 'child' 33 | expect: 34 | wrapper.depth == 3 35 | } 36 | 37 | def "display name is escaped"() { 38 | given: 39 | testDescriptorMock.displayName >> 'display name [escaped]' 40 | expect: 41 | wrapper.displayName == 'display name \\[escaped\\]' 42 | } 43 | 44 | def "display name of top level descriptor is #displayName if actual display name #actualDisplayName, className is #className and showSimpleNames is false"() { 45 | given: 46 | wrapper = new TestDescriptorWrapper(testDescriptorMock, testLoggerExtensionMock, []) 47 | testDescriptorMock.displayName >> actualDisplayName 48 | testDescriptorMock.className >> className 49 | testLoggerExtensionMock.showSimpleNames >> false 50 | expect: 51 | wrapper.displayName == displayName 52 | where: 53 | className | actualDisplayName | displayName 54 | 'com.adarshr.Test' | 'Test' | 'com.adarshr.Test' 55 | 'com.adarshr.Test' | 'Bar' | 'Bar' 56 | 'com.adarshr.Test' | 'com.adarshr.Test' | 'com.adarshr.Test' 57 | 'com.adarshr.Test$One$Two' | 'Two' | 'com.adarshr.Test$One$Two' 58 | } 59 | 60 | def "display name of top level descriptor is #displayName when actual display name is #actualDisplayName, className is #className and showSimpleNames is true"() { 61 | given: 62 | wrapper = new TestDescriptorWrapper(testDescriptorMock, testLoggerExtensionMock, []) 63 | testDescriptorMock.displayName >> actualDisplayName 64 | testDescriptorMock.className >> className 65 | testLoggerExtensionMock.showSimpleNames >> true 66 | expect: 67 | wrapper.displayName == displayName 68 | where: 69 | className | actualDisplayName | displayName 70 | 'com.adarshr.Test' | 'Test' | 'Test' 71 | 'com.adarshr.Test' | 'Bar' | 'Bar' 72 | 'com.adarshr.Test$One$Two' | 'Two' | 'Two' 73 | 'com.adarshr.Test$One$Two' | 'Two' | 'Two' 74 | 'com.adarshr.Test$One$Two' | 'Bar' | 'Bar' 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/logger/ConsoleLoggerSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.renderer.TextRenderer 4 | import org.gradle.api.logging.Logger 5 | import spock.lang.Specification 6 | 7 | import static org.gradle.api.logging.LogLevel.DEBUG 8 | import static org.gradle.api.logging.LogLevel.LIFECYCLE 9 | 10 | class ConsoleLoggerSpec extends Specification { 11 | 12 | def loggerMock = Mock(Logger) 13 | def textRendererMock = Mock(TextRenderer) 14 | ConsoleLogger consoleLogger 15 | 16 | def setup() { 17 | consoleLogger = new ConsoleLogger(loggerMock, LIFECYCLE, textRendererMock) 18 | } 19 | 20 | def "log"() { 21 | given: 22 | def text = '[red]text \\[escaped\\]to be logged[/]' 23 | textRendererMock.render(text) >> 'text \\[escaped\\] to be logged' 24 | when: 25 | consoleLogger.log(text) 26 | then: 27 | 1 * loggerMock.log(LIFECYCLE, 'text [escaped] to be logged') 28 | } 29 | 30 | def "does not log empty strings"() { 31 | when: 32 | consoleLogger.log('') 33 | then: 34 | 0 * loggerMock._ 35 | 0 * textRendererMock._ 36 | } 37 | 38 | def "log new line"() { 39 | when: 40 | consoleLogger.logNewLine() 41 | then: 42 | 1 * loggerMock.log(LIFECYCLE, '') 43 | } 44 | 45 | def "configurable log level"() { 46 | given: 47 | consoleLogger = new ConsoleLogger(loggerMock, DEBUG, textRendererMock) 48 | textRendererMock.render('text to be logged') >> 'rendered ansi text' 49 | when: 50 | consoleLogger.log('text to be logged') 51 | then: 52 | 1 * loggerMock.log(DEBUG, 'rendered ansi text') 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/logger/OutputCollectorSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import spock.lang.Specification 5 | 6 | class OutputCollectorSpec extends Specification { 7 | 8 | def "output collector collects output by descriptor ID and removes them accordingly"() { 9 | given: 10 | def collector = new OutputCollector() 11 | def descriptor1Mock = Mock(TestDescriptorWrapper) { id >> 'desc-1' } 12 | def descriptor2Mock = Mock(TestDescriptorWrapper) { id >> 'desc-2' } 13 | def descriptor3Mock = Mock(TestDescriptorWrapper) { id >> 'desc-3' } 14 | when: 15 | collector.collect(descriptor1Mock, 'Suite line 1\n') 16 | collector.collect(descriptor1Mock, 'Suite line 2\n') 17 | collector.collect(descriptor2Mock, 'Test line 1\n') 18 | collector.collect(descriptor3Mock, 'Test line 2\n') 19 | then: 20 | collector.pop(descriptor2Mock) == 'Test line 1\n' 21 | !collector.pop(descriptor2Mock) 22 | collector.pop(descriptor3Mock) == 'Test line 2\n' 23 | !collector.pop(descriptor3Mock) 24 | collector.pop(descriptor1Mock) == 'Suite line 1\nSuite line 2\n' 25 | !collector.pop(descriptor1Mock) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/logger/TestLoggerWrapperSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.logger 2 | 3 | import com.adarshr.gradle.testlogger.TestLoggerExtension 4 | import com.adarshr.gradle.testlogger.theme.ThemeType 5 | import org.gradle.StartParameter 6 | import org.gradle.api.logging.Logger 7 | import org.gradle.api.logging.configuration.ConsoleOutput 8 | import org.gradle.api.tasks.testing.Test 9 | import spock.lang.Specification 10 | 11 | class TestLoggerWrapperSpec extends Specification { 12 | 13 | def extensionMock = Mock(TestLoggerExtension) 14 | def testMock = Mock(Test) { 15 | getLogger() >> Mock(Logger) 16 | } 17 | def startParameterMock = Mock(StartParameter) { 18 | getConsoleOutput() >> ConsoleOutput.Auto 19 | } 20 | 21 | def "wrapper delegates to sequential test logger if parallel theme is not applied"() { 22 | given: 23 | extensionMock.theme >> ThemeType.STANDARD 24 | when: 25 | def wrapper = new TestLoggerWrapper(startParameterMock, testMock, extensionMock) 26 | then: 27 | wrapper.testLoggerDelegate instanceof SequentialTestLogger 28 | } 29 | 30 | def "wrapper delegates to parallel test logger if parallel theme is applied"() { 31 | given: 32 | extensionMock.theme >> ThemeType.STANDARD_PARALLEL 33 | when: 34 | def wrapper = new TestLoggerWrapper(startParameterMock, testMock, extensionMock) 35 | then: 36 | wrapper.testLoggerDelegate instanceof ParallelTestLogger 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/renderer/AnsiTextRendererSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.renderer 2 | 3 | import spock.lang.Specification 4 | import spock.lang.Unroll 5 | 6 | import static org.fusesource.jansi.Ansi.Erase.* 7 | import static org.fusesource.jansi.Ansi.ansi 8 | 9 | class AnsiTextRendererSpec extends Specification { 10 | 11 | def renderer = new AnsiTextRenderer() 12 | 13 | @Unroll 14 | def "render #input"() { 15 | expect: 16 | renderer.render(input) == expectedOutput.toString() 17 | where: 18 | input | expectedOutput 19 | '[red]text' | ansi().fgRed().a('text') 20 | '[bright-red]text' | ansi().fgBrightRed().a('text') 21 | '[foo]text' | '[foo]text' 22 | '[red,foo]text' | ansi().fgRed().a('[foo]text') 23 | '[yellow]text' | ansi().fgYellow().a('text') 24 | '[bright-yellow]text' | ansi().fgBrightYellow().a('text') 25 | '[green]text' | ansi().fgGreen().a('text') 26 | '[bright-green]text' | ansi().fgBrightGreen().a('text') 27 | '[bold][red]text[/]' | ansi().bold().fgRed().a('text').reset() 28 | '[bold,red]text[/]' | ansi().bold().fgRed().a('text').reset() 29 | '[erase-all,bold,red]text[/]' | ansi().eraseLine(ALL).bold().fgRed().a('text').reset() 30 | '[erase-ahead,bold,red]text[/]' | ansi().eraseLine(FORWARD).bold().fgRed().a('text').reset() 31 | '[erase-back,bold,red]text[/]' | ansi().eraseLine(BACKWARD).bold().fgRed().a('text').reset() 32 | '[cursor-up-line]text[/]' | ansi().cursorUpLine().a('text').reset() 33 | '[red]\\[red\\][/]' | ansi().fgRed().a('[red]').reset() 34 | '[bold]text[bold-off]' | ansi().bold().a('text').boldOff() 35 | '[default]text' | ansi().fgDefault().a('text') 36 | '[grey]text' | ansi().fgBrightBlack().a('text') 37 | '[cyan]text' | ansi().fgCyan().a('text') 38 | '[bright-cyan]text' | ansi().fgBrightCyan().a('text') 39 | '[blue]text' | ansi().fgBlue().a('text') 40 | '[bright-blue]text' | ansi().fgBrightBlue().a('text') 41 | '[magenta]text' | ansi().fgMagenta().a('text') 42 | '[bright-magenta]text' | ansi().fgBrightMagenta().a('text') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/AbstractThemeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestLoggerExtension 5 | import com.adarshr.gradle.testlogger.TestResultWrapper 6 | import groovy.transform.InheritConstructors 7 | import spock.lang.Unroll 8 | 9 | import static java.lang.System.lineSeparator 10 | 11 | class AbstractThemeSpec extends BaseThemeSpec { 12 | 13 | private static final Throwable TEST_EXCEPTION = generateException() 14 | 15 | private static final Throwable generateException() { 16 | def error = new Serializable() { 17 | Throwable one() { two() } 18 | 19 | Throwable two() { three() } 20 | 21 | Throwable three() { 22 | new RuntimeException('Middle error', new RuntimeException('Inner error')) 23 | } 24 | } 25 | 26 | lightenStackTrace(new RuntimeException('Outer error', error.one())) 27 | } 28 | 29 | private static Throwable lightenStackTrace(Throwable throwable) { 30 | throwable.stackTrace = throwable.stackTrace.findAll { 31 | it.className.contains 'adarshr' 32 | } 33 | 34 | if (throwable.cause) { 35 | lightenStackTrace(throwable.cause) 36 | } 37 | 38 | throwable 39 | } 40 | 41 | private static final int LINE_NUMBER = generateException().stackTrace.find { it.className == owner.name }.lineNumber 42 | 43 | def testLoggerExtensionMock = Mock(TestLoggerExtension) 44 | def theme = new TestTheme(testLoggerExtensionMock) 45 | def testDescriptorMock = Mock(TestDescriptorWrapper) 46 | def testResultMock = Mock(TestResultWrapper) 47 | 48 | @Unroll 49 | def "suite text returns '#expected' when loggable is #loggable"() { 50 | given: 51 | testResultMock.loggable >> loggable 52 | expect: 53 | theme.suiteText(testDescriptorMock, testResultMock) == expected 54 | where: 55 | expected | loggable 56 | 'suiteTextInternal' | true 57 | '' | false 58 | } 59 | 60 | @Unroll 61 | def "test text returns '#expected' when loggable is #loggable"() { 62 | given: 63 | testResultMock.loggable >> loggable 64 | expect: 65 | theme.testText(testDescriptorMock, testResultMock) == expected 66 | where: 67 | expected | loggable 68 | 'testTextInternal' | true 69 | '' | false 70 | } 71 | 72 | @Unroll 73 | def "suite standard stream text returns '#expected' when loggable is #loggable"() { 74 | given: 75 | testResultMock.loggable >> loggable 76 | expect: 77 | theme.suiteStandardStreamText(testDescriptorMock, 'lines', testResultMock) == expected 78 | where: 79 | expected | loggable 80 | 'suiteStandardStreamTextInternal' | true 81 | '' | false 82 | } 83 | 84 | @Unroll 85 | def "test standard stream text returns '#expected' when loggable is #loggable"() { 86 | given: 87 | testResultMock.standardStreamLoggable >> loggable 88 | expect: 89 | theme.testStandardStreamText(testDescriptorMock, 'lines', testResultMock) == expected 90 | where: 91 | expected | loggable 92 | 'testStandardStreamTextInternal' | true 93 | '' | false 94 | } 95 | 96 | def "test exception text with default settings"() { 97 | given: 98 | testLoggerExtensionMock.showExceptions >> true 99 | testLoggerExtensionMock.showCauses >> true 100 | testLoggerExtensionMock.showStackTraces >> true 101 | testDescriptorMock.className >> AbstractThemeSpec.name 102 | testResultMock.exception >> TEST_EXCEPTION 103 | when: 104 | def actual = theme.exceptionText(testDescriptorMock, testResultMock) 105 | then: 106 | actual == 107 | """| 108 | | 109 | | java.lang.RuntimeException: Outer error 110 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.generateException(AbstractThemeSpec.groovy:${LINE_NUMBER}) 111 | | Caused by: java.lang.RuntimeException: Middle error 112 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.generateException(AbstractThemeSpec.groovy:${LINE_NUMBER}) 113 | | Caused by: java.lang.RuntimeException: Inner error 114 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.generateException(AbstractThemeSpec.groovy:${LINE_NUMBER}) 115 | |""".stripMargin().replace('\n', lineSeparator()) 116 | } 117 | 118 | def "test exception text with showFullStackTraces on"() { 119 | given: 120 | testLoggerExtensionMock.showExceptions >> true 121 | testLoggerExtensionMock.showCauses >> true 122 | testLoggerExtensionMock.showStackTraces >> true 123 | testLoggerExtensionMock.showFullStackTraces >> true 124 | testDescriptorMock.className >> AbstractThemeSpec.name 125 | testResultMock.exception >> TEST_EXCEPTION 126 | when: 127 | def actual = theme.exceptionText(testDescriptorMock, testResultMock) 128 | then: 129 | actual == 130 | """| 131 | | 132 | | java.lang.RuntimeException: Outer error 133 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.generateException(AbstractThemeSpec.groovy:${LINE_NUMBER}) 134 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.(AbstractThemeSpec.groovy:13) 135 | | Caused by: java.lang.RuntimeException: Middle error 136 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec\$1.three(AbstractThemeSpec.groovy:22) 137 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec\$1.two(AbstractThemeSpec.groovy:19) 138 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec\$1.one(AbstractThemeSpec.groovy:17) 139 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec\$1\$one.call(Unknown Source) 140 | | ... 2 more 141 | | Caused by: java.lang.RuntimeException: Inner error 142 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec\$1.three(AbstractThemeSpec.groovy:22) 143 | | ... 5 more 144 | |""".stripMargin().replace('\n', lineSeparator()) 145 | } 146 | 147 | def "test exception text with showCauses off"() { 148 | given: 149 | testLoggerExtensionMock.showExceptions >> true 150 | testLoggerExtensionMock.showCauses >> false 151 | testLoggerExtensionMock.showStackTraces >> true 152 | testLoggerExtensionMock.showFullStackTraces >> true 153 | testDescriptorMock.className >> AbstractThemeSpec.name 154 | testResultMock.exception >> TEST_EXCEPTION 155 | when: 156 | def actual = theme.exceptionText(testDescriptorMock, testResultMock) 157 | then: 158 | actual == 159 | """| 160 | | 161 | | java.lang.RuntimeException: Outer error 162 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.generateException(AbstractThemeSpec.groovy:${LINE_NUMBER}) 163 | | at com.adarshr.gradle.testlogger.theme.AbstractThemeSpec.(AbstractThemeSpec.groovy:13) 164 | |""".stripMargin().replace('\n', lineSeparator()) 165 | } 166 | 167 | def "test exception text with showStackTraces off"() { 168 | given: 169 | testLoggerExtensionMock.showExceptions >> true 170 | testLoggerExtensionMock.showCauses >> true 171 | testLoggerExtensionMock.showStackTraces >> false 172 | testDescriptorMock.className >> AbstractThemeSpec.name 173 | testResultMock.exception >> TEST_EXCEPTION 174 | when: 175 | def actual = theme.exceptionText(testDescriptorMock, testResultMock) 176 | then: 177 | actual == 178 | """| 179 | | 180 | | java.lang.RuntimeException: Outer error 181 | |""".stripMargin().replace('\n', lineSeparator()) 182 | } 183 | 184 | def "test exception text with showExceptions off"() { 185 | given: 186 | testLoggerExtensionMock.showExceptions >> false 187 | testDescriptorMock.className >> AbstractThemeSpec.name 188 | testResultMock.exception >> TEST_EXCEPTION 189 | when: 190 | def actual = theme.exceptionText(testDescriptorMock, testResultMock) 191 | then: 192 | !actual 193 | } 194 | 195 | @InheritConstructors 196 | static class TestTheme extends AbstractTheme { 197 | 198 | ThemeType type = null 199 | 200 | @Override 201 | protected String suiteTextInternal(TestDescriptorWrapper descriptor) { 202 | 'suiteTextInternal' 203 | } 204 | 205 | @Override 206 | protected String testTextInternal(TestDescriptorWrapper descriptor, TestResultWrapper result) { 207 | 'testTextInternal' 208 | } 209 | 210 | @Override 211 | protected suiteStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 212 | 'suiteStandardStreamTextInternal' 213 | } 214 | 215 | @Override 216 | protected testStandardStreamTextInternal(TestDescriptorWrapper descriptor, String lines) { 217 | 'testStandardStreamTextInternal' 218 | } 219 | 220 | @Override 221 | String summaryText(TestDescriptorWrapper descriptor, TestResultWrapper result) { 222 | 'summaryText' 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/BaseThemeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestDescriptorWrapper 4 | import com.adarshr.gradle.testlogger.TestLoggerExtension 5 | import com.adarshr.gradle.testlogger.TestResultWrapper 6 | import spock.lang.Specification 7 | 8 | import static java.lang.System.lineSeparator 9 | 10 | abstract class BaseThemeSpec extends Specification { 11 | 12 | protected def testLoggerExtensionMock = Mock(TestLoggerExtension) 13 | protected def testDescriptorMock = Mock(TestDescriptorWrapper) 14 | protected def testResultMock = Mock(TestResultWrapper) 15 | protected def streamLines = "Hello${lineSeparator()}World [brackets] \u001B[0mANSI" 16 | 17 | def setup() { 18 | testResultMock.loggable >> true 19 | testResultMock.standardStreamLoggable >> true 20 | testLoggerExtensionMock.slowThreshold >> 2000 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/PlainParallelThemeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | 4 | import spock.lang.Unroll 5 | 6 | import static java.lang.System.lineSeparator 7 | import static java.lang.System.setOut 8 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 9 | 10 | class PlainParallelThemeSpec extends BaseThemeSpec { 11 | 12 | // right at the top to minimise line number changes 13 | private static AssertionError getException() { 14 | new AssertionError('This is wrong') 15 | } 16 | 17 | private static final int LINE_NUMBER = exception.stackTrace.find { it.className == owner.name }.lineNumber 18 | 19 | Theme theme 20 | 21 | def setup() { 22 | theme = new PlainParallelTheme(testLoggerExtensionMock) 23 | } 24 | 25 | def "before suite"() { 26 | expect: 27 | !theme.suiteText(testDescriptorMock, testResultMock) 28 | } 29 | 30 | @Unroll 31 | def "after test with result type #resultType"() { 32 | given: 33 | testResultMock.resultType >> resultType 34 | testDescriptorMock.trail >> 'ClassName' 35 | testDescriptorMock.displayName >> 'test name' 36 | when: 37 | def actual = theme.testText(testDescriptorMock, testResultMock) 38 | then: 39 | actual == expected 40 | where: 41 | resultType | expected 42 | SUCCESS | 'ClassName test name PASSED' 43 | FAILURE | 'ClassName test name FAILED' 44 | SKIPPED | 'ClassName test name SKIPPED' 45 | } 46 | 47 | def "after test with result type failure and showExceptions true"() { 48 | given: 49 | testLoggerExtensionMock.showExceptions >> true 50 | testLoggerExtensionMock.showStackTraces >> true 51 | testLoggerExtensionMock.showCauses >> true 52 | theme = new PlainParallelTheme(testLoggerExtensionMock) 53 | and: 54 | testResultMock.resultType >> FAILURE 55 | testResultMock.exception >> exception 56 | testDescriptorMock.displayName >> 'floppy test' 57 | testDescriptorMock.className >> this.class.name 58 | testDescriptorMock.trail >> this.class.name 59 | when: 60 | def actual = theme.testText(testDescriptorMock, testResultMock) 61 | then: 62 | actual == 63 | """|com.adarshr.gradle.testlogger.theme.PlainParallelThemeSpec floppy test FAILED 64 | | 65 | | java.lang.AssertionError: This is wrong 66 | | at com.adarshr.gradle.testlogger.theme.PlainParallelThemeSpec.getException(PlainParallelThemeSpec.groovy:${LINE_NUMBER}) 67 | |""".stripMargin().replace('\n', lineSeparator()) 68 | } 69 | 70 | def "exception text when showExceptions is true"() { 71 | given: 72 | testLoggerExtensionMock.showExceptions >> true 73 | testLoggerExtensionMock.showStackTraces >> true 74 | testLoggerExtensionMock.showCauses >> true 75 | theme = new PlainParallelTheme(testLoggerExtensionMock) 76 | and: 77 | testResultMock.resultType >> FAILURE 78 | testResultMock.exception >> exception 79 | testDescriptorMock.displayName >> 'floppy test' 80 | testDescriptorMock.trail >> this.class.name 81 | testDescriptorMock.className >> this.class.name 82 | expect: 83 | theme.exceptionText(testDescriptorMock, testResultMock) == 84 | """| 85 | | 86 | | java.lang.AssertionError: This is wrong 87 | | at com.adarshr.gradle.testlogger.theme.PlainParallelThemeSpec.getException(PlainParallelThemeSpec.groovy:${LINE_NUMBER}) 88 | |""".stripMargin().replace('\n', lineSeparator()) 89 | } 90 | 91 | def "exception text when showExceptions is false"() { 92 | given: 93 | testLoggerExtensionMock.showExceptions >> false 94 | testResultMock.resultType >> FAILURE 95 | testDescriptorMock.displayName >> 'floppy test' 96 | expect: 97 | !theme.exceptionText(testDescriptorMock, testResultMock) 98 | } 99 | 100 | 101 | def "exception text when showExceptions is true but exception is null"() { 102 | given: 103 | testLoggerExtensionMock.showExceptions >> true 104 | testResultMock.resultType >> FAILURE 105 | testResultMock.exception >> null 106 | testDescriptorMock.displayName >> 'exception is null test' 107 | expect: 108 | !theme.exceptionText(testDescriptorMock, testResultMock) 109 | } 110 | 111 | def "show duration if slowThreshold is exceeded"() { 112 | given: 113 | testResultMock.tooSlow >> true 114 | testResultMock.duration >> '10s' 115 | testResultMock.resultType >> SUCCESS 116 | testDescriptorMock.trail >> 'ClassName' 117 | testDescriptorMock.displayName >> 'test name' 118 | when: 119 | def actual = theme.testText(testDescriptorMock, testResultMock) 120 | then: 121 | actual == 'ClassName test name PASSED (10s)' 122 | } 123 | 124 | @Unroll 125 | def "summary text given #success success, #failure failed and #skipped skipped tests"() { 126 | given: 127 | testLoggerExtensionMock.showSummary >> true 128 | testResultMock.successfulTestCount >> success 129 | testResultMock.failedTestCount >> failure 130 | testResultMock.skippedTestCount >> skipped 131 | testResultMock.testCount >> success + failure + skipped 132 | testResultMock.duration >> '10s' 133 | testResultMock.resultType >> (failure ? FAILURE : SUCCESS) // what Gradle would do 134 | and: 135 | theme = new PlainParallelTheme(testLoggerExtensionMock) 136 | when: 137 | def actual = theme.summaryText(testDescriptorMock, testResultMock) 138 | then: 139 | actual == summaryText 140 | where: 141 | summaryText | success | failure | skipped 142 | "SUCCESS: Executed 10 tests in 10s${lineSeparator()}" | 10 | 0 | 0 143 | "SUCCESS: Executed 7 tests in 10s (2 skipped)${lineSeparator()}" | 5 | 0 | 2 144 | "FAILURE: Executed 8 tests in 10s (3 failed)${lineSeparator()}" | 5 | 3 | 0 145 | "FAILURE: Executed 10 tests in 10s (3 failed, 2 skipped)${lineSeparator()}" | 5 | 3 | 2 146 | } 147 | 148 | def "summary when showSummary is false"() { 149 | expect: 150 | !theme.summaryText(testDescriptorMock, testResultMock) 151 | } 152 | 153 | def "standard stream text"() { 154 | given: 155 | testLoggerExtensionMock.showStandardStreams >> true 156 | theme = new PlainParallelTheme(testLoggerExtensionMock) 157 | testResultMock.resultType >> SUCCESS 158 | expect: 159 | theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 160 | '''| 161 | | Hello 162 | | World \\[brackets\\] \u001B\\[0mANSI 163 | |'''.stripMargin().replace('\n', lineSeparator()) 164 | } 165 | 166 | def "standard stream text when showStandardStreams is false"() { 167 | given: 168 | testLoggerExtensionMock.showStandardStreams >> false 169 | theme = new PlainParallelTheme(testLoggerExtensionMock) 170 | testResultMock.resultType >> SUCCESS 171 | expect: 172 | !theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) 173 | } 174 | 175 | def "suite stream text"() { 176 | given: 177 | testLoggerExtensionMock.showStandardStreams >> true 178 | theme = new PlainParallelTheme(testLoggerExtensionMock) 179 | testResultMock.resultType >> SUCCESS 180 | expect: 181 | theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 182 | '''| 183 | | Hello 184 | | World \\[brackets\\] \u001B\\[0mANSI 185 | |'''.stripMargin().replace('\n', lineSeparator()) 186 | } 187 | 188 | def "suite stream text when showStandardStreams is false"() { 189 | given: 190 | testLoggerExtensionMock.showStandardStreams >> false 191 | theme = new PlainParallelTheme(testLoggerExtensionMock) 192 | testResultMock.resultType >> SUCCESS 193 | expect: 194 | !theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/PlainThemeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | 4 | import spock.lang.Unroll 5 | 6 | import static java.lang.System.lineSeparator 7 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 8 | 9 | class PlainThemeSpec extends BaseThemeSpec { 10 | 11 | // right at the top to minimise line number changes 12 | private static AssertionError getException() { 13 | new AssertionError('This is wrong') 14 | } 15 | 16 | private static final int LINE_NUMBER = exception.stackTrace.find { it.className == owner.name }.lineNumber 17 | 18 | Theme theme 19 | 20 | def setup() { 21 | theme = new PlainTheme(testLoggerExtensionMock) 22 | } 23 | 24 | def "suite text"() { 25 | given: 26 | testDescriptorMock.displayName >> 'ClassName' 27 | testDescriptorMock.depth >> 0 28 | when: 29 | def actual = theme.suiteText(testDescriptorMock, testResultMock) 30 | then: 31 | actual == "ClassName${lineSeparator()}" 32 | } 33 | 34 | @Unroll 35 | def "after test with result type #resultType"() { 36 | given: 37 | testResultMock.resultType >> resultType 38 | testDescriptorMock.displayName >> 'test name' 39 | testDescriptorMock.depth >> 1 40 | when: 41 | def actual = theme.testText(testDescriptorMock, testResultMock) 42 | then: 43 | actual == expected 44 | where: 45 | resultType | expected 46 | SUCCESS | ' Test test name PASSED' 47 | FAILURE | ' Test test name FAILED' 48 | SKIPPED | ' Test test name SKIPPED' 49 | } 50 | 51 | def "after test with result type failure and showExceptions true"() { 52 | given: 53 | testLoggerExtensionMock.showExceptions >> true 54 | testLoggerExtensionMock.showStackTraces >> true 55 | testLoggerExtensionMock.showCauses >> true 56 | theme = new PlainTheme(testLoggerExtensionMock) 57 | and: 58 | testResultMock.resultType >> FAILURE 59 | testResultMock.exception >> exception 60 | testDescriptorMock.displayName >> 'floppy test' 61 | testDescriptorMock.className >> this.class.name 62 | testDescriptorMock.depth >> 1 63 | when: 64 | def actual = theme.testText(testDescriptorMock, testResultMock) 65 | then: 66 | actual == 67 | """| Test floppy test FAILED 68 | | 69 | | java.lang.AssertionError: This is wrong 70 | | at com.adarshr.gradle.testlogger.theme.PlainThemeSpec.getException(PlainThemeSpec.groovy:${LINE_NUMBER}) 71 | |""".stripMargin().replace('\n', lineSeparator()) 72 | } 73 | 74 | def "exception text when showExceptions is true"() { 75 | given: 76 | testLoggerExtensionMock.showExceptions >> true 77 | testLoggerExtensionMock.showStackTraces >> true 78 | testLoggerExtensionMock.showCauses >> true 79 | theme = new PlainTheme(testLoggerExtensionMock) 80 | and: 81 | testResultMock.resultType >> FAILURE 82 | testResultMock.exception >> exception 83 | testDescriptorMock.displayName >> 'floppy test' 84 | testDescriptorMock.className >> this.class.name 85 | testDescriptorMock.depth >> 1 86 | expect: 87 | theme.exceptionText(testDescriptorMock, testResultMock) == 88 | """| 89 | | 90 | | java.lang.AssertionError: This is wrong 91 | | at com.adarshr.gradle.testlogger.theme.PlainThemeSpec.getException(PlainThemeSpec.groovy:${LINE_NUMBER}) 92 | |""".stripMargin().replace('\n', lineSeparator()) 93 | } 94 | 95 | def "exception text when showExceptions is false"() { 96 | given: 97 | testLoggerExtensionMock.showExceptions >> false 98 | testResultMock.resultType >> FAILURE 99 | testDescriptorMock.displayName >> 'floppy test' 100 | testDescriptorMock.depth >> 1 101 | expect: 102 | !theme.exceptionText(testDescriptorMock, testResultMock) 103 | } 104 | 105 | 106 | def "exception text when showExceptions is true but exception is null"() { 107 | given: 108 | testLoggerExtensionMock.showExceptions >> true 109 | testResultMock.resultType >> FAILURE 110 | testResultMock.exception >> null 111 | testDescriptorMock.displayName >> 'exception is null test' 112 | testDescriptorMock.depth >> 1 113 | expect: 114 | !theme.exceptionText(testDescriptorMock, testResultMock) 115 | } 116 | 117 | def "show duration if slowThreshold is exceeded"() { 118 | given: 119 | testResultMock.duration >> '10s' 120 | testResultMock.tooSlow >> true 121 | testResultMock.resultType >> SUCCESS 122 | testDescriptorMock.displayName >> 'test name' 123 | testDescriptorMock.depth >> 1 124 | when: 125 | def actual = theme.testText(testDescriptorMock, testResultMock) 126 | then: 127 | actual == ' Test test name PASSED (10s)' 128 | } 129 | 130 | @Unroll 131 | def "summary text given #success success, #failure failed and #skipped skipped tests"() { 132 | given: 133 | testLoggerExtensionMock.showSummary >> true 134 | testResultMock.successfulTestCount >> success 135 | testResultMock.failedTestCount >> failure 136 | testResultMock.skippedTestCount >> skipped 137 | testResultMock.testCount >> success + failure + skipped 138 | testResultMock.duration >> '10s' 139 | testResultMock.resultType >> (failure ? FAILURE : SUCCESS) // what Gradle would do 140 | and: 141 | theme = new PlainTheme(testLoggerExtensionMock) 142 | when: 143 | def actual = theme.summaryText(testDescriptorMock, testResultMock) 144 | then: 145 | actual == summaryText 146 | where: 147 | summaryText | success | failure | skipped 148 | "SUCCESS: Executed 10 tests in 10s${lineSeparator()}" | 10 | 0 | 0 149 | "SUCCESS: Executed 7 tests in 10s (2 skipped)${lineSeparator()}" | 5 | 0 | 2 150 | "FAILURE: Executed 8 tests in 10s (3 failed)${lineSeparator()}" | 5 | 3 | 0 151 | "FAILURE: Executed 10 tests in 10s (3 failed, 2 skipped)${lineSeparator()}" | 5 | 3 | 2 152 | } 153 | 154 | def "summary when showSummary is false"() { 155 | expect: 156 | !theme.summaryText(testDescriptorMock, testResultMock) 157 | } 158 | 159 | def "standard stream text"() { 160 | given: 161 | testLoggerExtensionMock.showStandardStreams >> true 162 | theme = new PlainTheme(testLoggerExtensionMock) 163 | expect: 164 | theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 165 | '''| 166 | | Hello 167 | | World \\[brackets\\] \u001B\\[0mANSI 168 | |'''.stripMargin().replace('\n', lineSeparator()) 169 | } 170 | 171 | def "standard stream text when showStandardStreams is false"() { 172 | given: 173 | testLoggerExtensionMock.showStandardStreams >> false 174 | theme = new PlainTheme(testLoggerExtensionMock) 175 | expect: 176 | !theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) 177 | } 178 | 179 | def "suite stream text"() { 180 | given: 181 | testLoggerExtensionMock.showStandardStreams >> true 182 | theme = new PlainTheme(testLoggerExtensionMock) 183 | expect: 184 | theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 185 | '''| 186 | | Hello 187 | | World \\[brackets\\] \u001B\\[0mANSI 188 | |'''.stripMargin().replace('\n', lineSeparator()) 189 | } 190 | 191 | def "suite stream text when showStandardStreams is false"() { 192 | given: 193 | testLoggerExtensionMock.showStandardStreams >> false 194 | theme = new PlainTheme(testLoggerExtensionMock) 195 | expect: 196 | !theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/StandardThemeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | 4 | import spock.lang.Unroll 5 | 6 | import static java.lang.System.lineSeparator 7 | import static org.gradle.api.tasks.testing.TestResult.ResultType.* 8 | 9 | class StandardThemeSpec extends BaseThemeSpec { 10 | 11 | // right at the top to minimise line number changes 12 | private static AssertionError getException() { 13 | new AssertionError('This is wrong') 14 | } 15 | 16 | private static final int LINE_NUMBER = exception.stackTrace.find { it.className == owner.name }.lineNumber 17 | 18 | Theme theme 19 | 20 | def setup() { 21 | theme = new StandardTheme(testLoggerExtensionMock) 22 | } 23 | 24 | def "suite text"() { 25 | given: 26 | testDescriptorMock.displayName >> 'ClassName' 27 | testDescriptorMock.depth >> 0 28 | when: 29 | def actual = theme.suiteText(testDescriptorMock, testResultMock) 30 | then: 31 | actual == "[erase-ahead,bold]ClassName[/]${lineSeparator()}" 32 | } 33 | 34 | @Unroll 35 | def "after test with result type #resultType"() { 36 | given: 37 | testResultMock.resultType >> resultType 38 | testDescriptorMock.displayName >> 'test name' 39 | testDescriptorMock.depth >> 1 40 | when: 41 | def actual = theme.testText(testDescriptorMock, testResultMock) 42 | then: 43 | actual == expected 44 | where: 45 | resultType | expected 46 | SUCCESS | '[erase-ahead,bold] Test [bold-off]test name[green] PASSED[/]' 47 | FAILURE | '[erase-ahead,bold] Test [bold-off]test name[red] FAILED[/]' 48 | SKIPPED | '[erase-ahead,bold] Test [bold-off]test name[yellow] SKIPPED[/]' 49 | } 50 | 51 | def "after test with result type failure and showExceptions true"() { 52 | given: 53 | testLoggerExtensionMock.showExceptions >> true 54 | testLoggerExtensionMock.showStackTraces >> true 55 | testLoggerExtensionMock.showCauses >> true 56 | theme = new StandardTheme(testLoggerExtensionMock) 57 | and: 58 | testResultMock.resultType >> FAILURE 59 | testResultMock.exception >> exception 60 | testDescriptorMock.displayName >> 'floppy test' 61 | testDescriptorMock.className >> this.class.name 62 | testDescriptorMock.depth >> 1 63 | when: 64 | def actual = theme.testText(testDescriptorMock, testResultMock) 65 | then: 66 | actual == 67 | """|[erase-ahead,bold] Test [bold-off]floppy test[red] FAILED[red] 68 | | 69 | | java.lang.AssertionError: This is wrong 70 | | at com.adarshr.gradle.testlogger.theme.StandardThemeSpec.getException(StandardThemeSpec.groovy:${LINE_NUMBER}) 71 | |[/]""".stripMargin().replace('\n', lineSeparator()) 72 | } 73 | 74 | def "exception text when showExceptions is true"() { 75 | given: 76 | testLoggerExtensionMock.showExceptions >> true 77 | testLoggerExtensionMock.showStackTraces >> true 78 | testLoggerExtensionMock.showCauses >> true 79 | theme = new StandardTheme(testLoggerExtensionMock) 80 | and: 81 | testResultMock.resultType >> FAILURE 82 | testResultMock.exception >> exception 83 | testDescriptorMock.displayName >> 'floppy test' 84 | testDescriptorMock.className >> this.class.name 85 | testDescriptorMock.depth >> 1 86 | expect: 87 | theme.exceptionText(testDescriptorMock, testResultMock) == 88 | """|[red] 89 | | 90 | | java.lang.AssertionError: This is wrong 91 | | at com.adarshr.gradle.testlogger.theme.StandardThemeSpec.getException(StandardThemeSpec.groovy:${LINE_NUMBER}) 92 | |""".stripMargin().replace('\n', lineSeparator()) 93 | } 94 | 95 | def "exception text when showExceptions is false"() { 96 | given: 97 | testLoggerExtensionMock.showExceptions >> false 98 | testResultMock.resultType >> FAILURE 99 | testDescriptorMock.displayName >> 'floppy test' 100 | testDescriptorMock.depth >> 1 101 | expect: 102 | !theme.exceptionText(testDescriptorMock, testResultMock) 103 | } 104 | 105 | def "exception text when showExceptions is true but exception is null"() { 106 | given: 107 | testLoggerExtensionMock.showExceptions >> true 108 | testResultMock.resultType >> FAILURE 109 | testResultMock.exception >> null 110 | testDescriptorMock.displayName >> 'exception is null test' 111 | testDescriptorMock.depth >> 1 112 | expect: 113 | !theme.exceptionText(testDescriptorMock, testResultMock) 114 | } 115 | 116 | @Unroll 117 | def "show duration if slowThreshold is exceeded for resultType #resultType"() { 118 | given: 119 | testResultMock.resultType >> resultType 120 | testResultMock.duration >> '10s' 121 | testResultMock.tooSlow >> true 122 | testDescriptorMock.displayName >> 'test name' 123 | testDescriptorMock.depth >> 1 124 | when: 125 | def actual = theme.testText(testDescriptorMock, testResultMock) 126 | then: 127 | actual == text 128 | where: 129 | resultType | text 130 | SUCCESS | '[erase-ahead,bold] Test [bold-off]test name[green] PASSED[red] (10s)[/]' 131 | FAILURE | '[erase-ahead,bold] Test [bold-off]test name[red] FAILED[red] (10s)[/]' 132 | } 133 | 134 | @Unroll 135 | def "show duration if slowThreshold is approaching for resultType #resultType"() { 136 | given: 137 | testResultMock.resultType >> resultType 138 | testResultMock.duration >> '1.5s' 139 | testResultMock.mediumSlow >> true 140 | testDescriptorMock.displayName >> 'test name' 141 | testDescriptorMock.depth >> 1 142 | when: 143 | def actual = theme.testText(testDescriptorMock, testResultMock) 144 | then: 145 | actual == text 146 | where: 147 | resultType | text 148 | SUCCESS | '[erase-ahead,bold] Test [bold-off]test name[green] PASSED[yellow] (1.5s)[/]' 149 | FAILURE | '[erase-ahead,bold] Test [bold-off]test name[red] FAILED[yellow] (1.5s)[/]' 150 | } 151 | 152 | @Unroll 153 | def "summary text given #success success, #failure failed and #skipped skipped tests"() { 154 | given: 155 | testLoggerExtensionMock.showSummary >> true 156 | testResultMock.successfulTestCount >> success 157 | testResultMock.failedTestCount >> failure 158 | testResultMock.skippedTestCount >> skipped 159 | testResultMock.testCount >> success + failure + skipped 160 | testResultMock.duration >> '10s' 161 | testResultMock.resultType >> (failure ? FAILURE : SUCCESS) // what Gradle would do 162 | and: 163 | theme = new StandardTheme(testLoggerExtensionMock) 164 | when: 165 | def actual = theme.summaryText(testDescriptorMock, testResultMock) 166 | then: 167 | actual == summaryText 168 | where: 169 | summaryText | success | failure | skipped 170 | "[erase-ahead,bold,green]SUCCESS: [default]Executed 10 tests in 10s[/]${lineSeparator()}" | 10 | 0 | 0 171 | "[erase-ahead,bold,green]SUCCESS: [default]Executed 7 tests in 10s (2 skipped)[/]${lineSeparator()}" | 5 | 0 | 2 172 | "[erase-ahead,bold,red]FAILURE: [default]Executed 8 tests in 10s (3 failed)[/]${lineSeparator()}" | 5 | 3 | 0 173 | "[erase-ahead,bold,red]FAILURE: [default]Executed 10 tests in 10s (3 failed, 2 skipped)[/]${lineSeparator()}" | 5 | 3 | 2 174 | } 175 | 176 | def "summary when showSummary is false"() { 177 | expect: 178 | !theme.summaryText(testDescriptorMock, testResultMock) 179 | } 180 | 181 | def "standard stream text"() { 182 | given: 183 | testLoggerExtensionMock.showStandardStreams >> true 184 | theme = new StandardTheme(testLoggerExtensionMock) 185 | expect: 186 | theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 187 | '''|[default] 188 | | Hello 189 | | World \\[brackets\\] \u001B\\[0mANSI[/] 190 | |'''.stripMargin().replace('\n', lineSeparator()) 191 | } 192 | 193 | def "standard stream text when showStandardStreams is false"() { 194 | given: 195 | testLoggerExtensionMock.showStandardStreams >> false 196 | theme = new StandardTheme(testLoggerExtensionMock) 197 | expect: 198 | !theme.testStandardStreamText(testDescriptorMock, streamLines, testResultMock) 199 | } 200 | 201 | def "suite stream text"() { 202 | given: 203 | testLoggerExtensionMock.showStandardStreams >> true 204 | theme = new StandardTheme(testLoggerExtensionMock) 205 | expect: 206 | theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) == 207 | '''|[default] 208 | | Hello 209 | | World \\[brackets\\] \u001B\\[0mANSI[/] 210 | |'''.stripMargin().replace('\n', lineSeparator()) 211 | } 212 | 213 | def "suite stream text when showStandardStreams is false"() { 214 | given: 215 | testLoggerExtensionMock.showStandardStreams >> false 216 | theme = new StandardTheme(testLoggerExtensionMock) 217 | expect: 218 | !theme.suiteStandardStreamText(testDescriptorMock, streamLines, testResultMock) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/ThemeFactorySpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import com.adarshr.gradle.testlogger.TestLoggerExtension 4 | import org.gradle.StartParameter 5 | import org.gradle.api.logging.Logger 6 | import org.gradle.api.logging.configuration.ConsoleOutput 7 | import org.gradle.api.tasks.testing.Test 8 | import org.gradle.api.tasks.testing.testng.TestNGOptions 9 | import spock.lang.Specification 10 | import spock.lang.Unroll 11 | 12 | class ThemeFactorySpec extends Specification { 13 | 14 | def extensionMock = Mock(TestLoggerExtension) 15 | def loggerMock = Mock(Logger) 16 | def testMock = Mock(Test) { 17 | getLogger() >> loggerMock 18 | } 19 | def startParameterMock = Mock(StartParameter) { 20 | getConsoleOutput() >> ConsoleOutput.Auto 21 | } 22 | 23 | @Unroll 24 | def "loads theme #theme.simpleName when theme type is #themeType"() { 25 | given: 26 | extensionMock.theme >> themeType 27 | expect: 28 | ThemeFactory.getTheme(startParameterMock, testMock, extensionMock).class == theme 29 | where: 30 | themeType | theme 31 | ThemeType.PLAIN | PlainTheme 32 | ThemeType.PLAIN_PARALLEL | PlainParallelTheme 33 | ThemeType.STANDARD | StandardTheme 34 | ThemeType.STANDARD_PARALLEL | StandardParallelTheme 35 | ThemeType.MOCHA | MochaTheme 36 | ThemeType.MOCHA_PARALLEL | MochaParallelTheme 37 | } 38 | 39 | def "console type of plain overrides any theme configuration"() { 40 | given: 41 | extensionMock.theme >> ThemeType.MOCHA 42 | testMock.name >> 'fooTask' 43 | def startParameterMock = Mock(StartParameter) { 44 | getConsoleOutput() >> ConsoleOutput.Plain 45 | } 46 | when: 47 | def theme = ThemeFactory.getTheme(startParameterMock, testMock, extensionMock) 48 | then: 49 | theme.type == ThemeType.PLAIN 50 | and: 51 | 1 * loggerMock.info('Test logger theme for task fooTask overridden from mocha to plain') 52 | } 53 | 54 | @Unroll 55 | def "parallel fallback theme is applied when maxParallelForks > 1 but a parallel theme is not used"() { 56 | given: 57 | extensionMock.theme >> extensionTheme 58 | testMock.maxParallelForks >> 2 59 | testMock.name >> 'fooTask' 60 | when: 61 | def theme = ThemeFactory.getTheme(startParameterMock, testMock, extensionMock) 62 | then: 63 | theme.type == actualTheme 64 | and: 65 | 1 * loggerMock.info("Test logger theme for task fooTask overridden " + 66 | "from ${extensionTheme.name} to ${actualTheme.name}") 67 | where: 68 | extensionTheme | actualTheme 69 | ThemeType.PLAIN | ThemeType.PLAIN_PARALLEL 70 | ThemeType.STANDARD | ThemeType.STANDARD_PARALLEL 71 | ThemeType.MOCHA | ThemeType.MOCHA_PARALLEL 72 | } 73 | 74 | @Unroll 75 | def "parallel fallback theme is applied when TestNG parallel mode is on but a parallel theme is not used"() { 76 | given: 77 | extensionMock.theme >> extensionTheme 78 | testMock.maxParallelForks >> 2 79 | testMock.name >> 'fooTask' 80 | testMock.options >> Mock(TestNGOptions) { 81 | getParallel() >> 'methods' 82 | } 83 | when: 84 | def theme = ThemeFactory.getTheme(startParameterMock, testMock, extensionMock) 85 | then: 86 | theme.type == actualTheme 87 | and: 88 | 1 * loggerMock.info("Test logger theme for task fooTask overridden " + 89 | "from ${extensionTheme.name} to ${actualTheme.name}") 90 | where: 91 | extensionTheme | actualTheme 92 | ThemeType.PLAIN | ThemeType.PLAIN_PARALLEL 93 | ThemeType.STANDARD | ThemeType.STANDARD_PARALLEL 94 | ThemeType.MOCHA | ThemeType.MOCHA_PARALLEL 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/theme/ThemeTypeSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.theme 2 | 3 | import org.gradle.api.GradleException 4 | import spock.lang.Specification 5 | import spock.lang.Unroll 6 | 7 | 8 | class ThemeTypeSpec extends Specification { 9 | 10 | @Unroll 11 | def "fromName can construct ThemeType from name #name"() { 12 | expect: 13 | ThemeType.fromName(name) == themeType 14 | where: 15 | name | themeType 16 | 'plain' | ThemeType.PLAIN 17 | 'plain-parallel' | ThemeType.PLAIN_PARALLEL 18 | 'standard' | ThemeType.STANDARD 19 | 'standard-parallel' | ThemeType.STANDARD_PARALLEL 20 | 'mocha' | ThemeType.MOCHA 21 | 'mocha-parallel' | ThemeType.MOCHA_PARALLEL 22 | } 23 | 24 | def "fromName throws exception if unknown theme name is specified"() { 25 | when: 26 | ThemeType.fromName('unknown') 27 | then: 28 | thrown(GradleException) 29 | } 30 | 31 | def "get all theme names"() { 32 | expect: 33 | ThemeType.allThemeNames == "'plain', 'plain-parallel', 'standard', 'standard-parallel', 'mocha', 'mocha-parallel'" 34 | } 35 | 36 | def "get parallel theme names"() { 37 | expect: 38 | ThemeType.parallelThemeNames == "'plain-parallel', 'standard-parallel', 'mocha-parallel'" 39 | } 40 | 41 | @Unroll 42 | def "instance of #themeType theme has the correct type set"() { 43 | expect: 44 | themeType.themeClass.newInstance(null).type == themeType 45 | where: 46 | themeType << ThemeType.values().toList() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/util/RendererUtilsSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.util 2 | 3 | import spock.lang.Specification 4 | import spock.lang.Unroll 5 | 6 | 7 | class RendererUtilsSpec extends Specification { 8 | 9 | @Unroll 10 | def "escape #text"() { 11 | expect: 12 | RendererUtils.escape(text) == expected 13 | where: 14 | text | expected 15 | null | null 16 | '' | '' 17 | '[escape]' | '\\[escape\\]' 18 | '\u001Btext' | '\u001Btext' 19 | } 20 | 21 | @Unroll 22 | def "unescape #text"() { 23 | expect: 24 | RendererUtils.unescape(text) == expected 25 | where: 26 | text | expected 27 | null | null 28 | '' | '' 29 | '[red]\\[escaped\\]' | '[red][escaped]' 30 | '\u001B[0mANSI' | '\u001B[0mANSI' 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/groovy/com/adarshr/gradle/testlogger/util/TimeUtilsSpec.groovy: -------------------------------------------------------------------------------- 1 | package com.adarshr.gradle.testlogger.util 2 | 3 | import spock.lang.Specification 4 | import spock.lang.Unroll 5 | 6 | class TimeUtilsSpec extends Specification { 7 | 8 | @Unroll 9 | def "duration is #duration when millis is #millis"() { 10 | expect: 11 | duration == TimeUtils.humanDuration(millis) 12 | where: 13 | duration | millis 14 | '0ms' | 0 15 | '999ms' | 999 16 | '1s' | 1000 17 | '3.4s' | 3403 18 | '4.9s' | 4999 19 | '59.9s' | 59999 20 | '1m' | 60000 21 | '1m 2s' | 62567 22 | '59m 59s' | 3599_999 23 | '1h' | 3600_000 24 | '5h 32m' | 19_920_000 25 | '23h 59m' | 86_364_000 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /uploadTestResults.ps1: -------------------------------------------------------------------------------- 1 | Set-Location build/test-results/test 2 | 3 | Write-Host "- About to upload test results" 4 | 5 | $wc = New-Object "System.Net.WebClient" 6 | 7 | Get-ChildItem -Path *.xml | 8 | Foreach-Object { 9 | $wc.UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path $_.FullName)) 10 | } 11 | 12 | Write-Host "- Results uploaded" 13 | --------------------------------------------------------------------------------