diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0068402 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3a24f7f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Java 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 25 + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Compile + run: ./gradlew clean assemble + + - name: Test + run: ./gradlew -PtestMode=slow test + + - name: Checkstyle + if: success() || failure() + run: ./gradlew checkstyleMain + + - name: Jacoco Coverage Verification + if: success() || failure() + run: ./gradlew jacocoTestCoverageVerification + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: build/reports/tests/xml/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: jars + path: build/libs/*.jar diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9bc621c..51b6586 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,22 +1,9 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# ******** NOTE ******** - name: "CodeQL" on: push: branches: [ master ] pull_request: - # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '45 16 * * 2' @@ -30,39 +17,24 @@ jobs: fail-fast: false matrix: language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Set up Java 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 25 - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - #- run: | - # make bootstrap - # make release + - name: Build + run: ./gradlew clean assemble - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/build.gradle b/build.gradle index 38ca0ab..fa4c846 100644 --- a/build.gradle +++ b/build.gradle @@ -1,38 +1,123 @@ -apply from: 'build.shared' -apply plugin: 'checkstyle' -apply plugin: 'jacoco' +plugins { + id 'java' + id 'checkstyle' + id 'jacoco' + id 'maven-publish' + id 'signing' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(25) + } +} -plugins.withType(JavaPlugin) { - checkstyle.sourceSets = [sourceSets.main] +repositories { + mavenCentral() } +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:6.0.3' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher:6.0.3' + implementation "org.threadly:threadly:$threadlyVersion" +} + +compileJava { + options.compilerArgs << '-Xlint:all' << '-Xlint:-deprecation' << '-Xlint:-this-escape' << '-Werror' +} + +compileTestJava { + options.compilerArgs << '-Xlint:all' +} + +checkstyle { + sourceSets = [sourceSets.main] +} + +def testMode = project.findProperty('testMode') ?: 'default' + test { - maxParallelForks = Math.min(8, Math.max(1, (int)(Runtime.getRuntime().availableProcessors() / 4))) + useJUnitPlatform() + + switch (testMode) { + case 'slow': + maxParallelForks = 1 + systemProperty 'systemSpeed', 'slow' + break + case 'stress': + systemProperty 'systemSpeed', 'slow' + systemProperty 'testProfile', 'stress' + maxParallelForks = Math.min(8, Math.max(1, (int)(Runtime.getRuntime().availableProcessors() / 2))) + break + default: + maxParallelForks = Math.min(8, Math.max(1, (int)(Runtime.getRuntime().availableProcessors() / 4))) + break + } + + reports { + junitXml.outputLocation = layout.buildDirectory.dir('reports/tests/xml') + html.outputLocation = layout.buildDirectory.dir('reports/tests/html') + } + binaryResultsDirectory = layout.buildDirectory.dir('reports/tests/bin') + jacoco { - excludes = ['**/package-info**','**/*Test'] - destinationFile = file("$buildDir/reports/jacoco/test.exec") + excludes = ['**/package-info**', '**/*Test'] + destinationFile = layout.buildDirectory.file('reports/jacoco/test.exec').get().asFile } } -test.dependsOn("jar") +test.dependsOn('jar') + +jar { + manifest { + attributes( + 'Implementation-Title': 'Threadly Test Utilities', + 'Implementation-Version': archiveVersion.get() + ) + } +} + +javadoc { + source = sourceSets.main.allJava + excludes = ['**/ThreadlyInternalAccessor**', '**/ArgumentVerifier**'] + options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PUBLIC +} + +tasks.register('javadocJar', Jar) { + dependsOn javadoc + archiveClassifier.set('javadoc') + from javadoc.destinationDir +} + +tasks.register('sourcesJar', Jar) { + from sourceSets.main.allSource + archiveClassifier.set('sources') +} + +tasks.register('copyLibs', Copy) { + into layout.buildDirectory.dir('dependencies') + from configurations.runtimeClasspath +} + +build.finalizedBy('copyLibs') jacocoTestReport { reports { csv.required = false xml.required = true - xml.destination = file("$buildDir/reports/jacoco/jacoco.xml") + xml.outputLocation = layout.buildDirectory.file('reports/jacoco/jacoco.xml') html.required = true - html.destination = file("$buildDir/reports/jacoco/html") + html.outputLocation = layout.buildDirectory.dir('reports/jacoco/html') } doLast { println "Test results available at:" - println "html - $buildDir/reports/tests/html/index.html" + println "html - ${layout.buildDirectory.dir('reports/tests/html').get().asFile}/index.html" println "Test coverage reports available at:" - println "html - $buildDir/reports/jacoco/html/index.html" + println "html - ${layout.buildDirectory.dir('reports/jacoco/html').get().asFile}/index.html" } } -test.finalizedBy("jacocoTestReport") +test.finalizedBy('jacocoTestReport') jacocoTestCoverageVerification { violationRules { @@ -63,4 +148,68 @@ jacocoTestCoverageVerification { } } -jacocoTestReport.finalizedBy("jacocoTestCoverageVerification") +jacocoTestReport.finalizedBy('jacocoTestCoverageVerification') + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + + artifact(tasks.named('sourcesJar')) + artifact(tasks.named('javadocJar')) + + pom { + name = 'Threadly Test Utilities' + description = 'A library of tools to assist with testing concurrent java applications.' + url = 'https://threadly.org/' + + scm { + url = 'scm:git@github.com:threadly/threadly-test.git' + connection = 'scm:git@github.com:threadly/threadly-test.git' + developerConnection = 'scm:git@github.com:threadly/threadly-test.git' + } + + issueManagement { + system = 'GitHub' + url = 'https://github.com/threadly/threadly-test/issues' + } + + licenses { + license { + name = 'Mozilla Public License Version 2.0' + url = 'https://www.mozilla.org/MPL/2.0/' + distribution = 'repo' + } + } + + developers { + developer { + id = 'jent' + name = 'Mike Jensen' + email = 'jent@threadly.org' + } + } + } + } + } + repositories { + maven { + def releasesRepoUrl = 'https://oss.sonatype.org/service/local/staging/deploy/maven2' + def snapshotsRepoUrl = 'https://oss.sonatype.org/content/repositories/snapshots' + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username = findProperty('sonatypeUsername') ?: '' + password = findProperty('sonatypePassword') ?: '' + } + } + } +} + +tasks.withType(GenerateMavenPom).configureEach { + destination = layout.buildDirectory.file('generated-pom.xml').get().asFile +} + +signing { + required = { !version.toString().contains('SNAPSHOT') && gradle.taskGraph.hasTask('publish') } + sign publishing.publications.mavenJava +} diff --git a/build.shared b/build.shared deleted file mode 100644 index b9b2b6d..0000000 --- a/build.shared +++ /dev/null @@ -1,71 +0,0 @@ -apply plugin: 'java' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation ( - "junit:junit:4.13.2", - ) - - implementation ( - "org.threadly:threadly:$threadlyVersion" - ) -} - -compileJava { - options.compilerArgs << "-Xlint:all" << "-Xlint:-deprecation" << "-Werror" - - if (JavaVersion.current().isJava8()) { - options.compilerArgs << "-XDenableSunApiLintControl" << "-Xlint:-sunapi" - } -} - -compileTestJava { - options.compilerArgs << "-Xlint:all" -} - -test { - getReports().getJunitXml().setDestination(file("$buildDir/reports/tests/xml")) - getReports().getHtml().setDestination(file("$buildDir/reports/tests/html")) - setBinaryResultsDirectory(file("$buildDir/reports/tests/bin")) -} - -jar { - manifest { - attributes ( - 'Implementation-Title': 'Threadly Test Utilities', - 'Implementation-Version': archiveVersion - ) - } -} - -javadoc { - source = sourceSets.main.allJava - excludes = ['**/ThreadlyInternalAccessor**', '**/ArgumentVerifier**'] - options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PUBLIC -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from 'build/docs/javadoc' -} - -task sourcesJar(type: Jar) { - from sourceSets.main.allSource - classifier = 'sources' -} - -task copyLibs(type: Copy) { - into "$buildDir/dependencies/" - from configurations.runtimeClasspath -} - -build.finalizedBy("copyLibs") - -artifacts { - //archives jar // already specified by default - archives javadocJar - archives sourcesJar -} diff --git a/build.slow b/build.slow deleted file mode 100644 index 8f3e930..0000000 --- a/build.slow +++ /dev/null @@ -1,7 +0,0 @@ -apply from: 'build.gradle' -apply plugin: 'jacoco' - -test { - maxParallelForks = 1 - systemProperty 'systemSpeed', 'slow' -} diff --git a/build.stress b/build.stress deleted file mode 100644 index 764edc1..0000000 --- a/build.stress +++ /dev/null @@ -1,8 +0,0 @@ -apply from: 'build.gradle' -apply plugin: 'jacoco' - -test { - systemProperty 'systemSpeed', 'slow' - systemProperty 'testProfile', 'stress' - maxParallelForks = Math.min(8, Math.max(1, (int)(Runtime.getRuntime().availableProcessors() / 2))) -} diff --git a/build.upload b/build.upload deleted file mode 100644 index e7df192..0000000 --- a/build.upload +++ /dev/null @@ -1,122 +0,0 @@ -apply from: 'build.shared' -apply plugin: 'maven-publish' -apply plugin: 'signing' - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -test { - maxParallelForks = Math.max(1, (int)(Runtime.getRuntime().availableProcessors() / 2)) -} - -test.dependsOn("jar") - -def findJVM() { - String[] java8Paths = new String[5] - java8Paths[0] = "/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/" - java8Paths[1] = "/usr/lib/jvm/java-8-openjdk/jre/lib/" - java8Paths[2] = "/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/" - java8Paths[3] = "/usr/lib/jvm/java-1.8.0-openjdk/jre/lib/" - java8Paths[4] = "/usr/lib/jvm/java-8-sun/jre/lib/" - for (String path : java8Paths) { - if (new java.io.File(path).exists()) { - return path - } - } - return null -} - -compileJava { - def jvmPath = findJVM() - if (jvmPath == null) { - println 'Unable to find java 8 rt.jar, will cause failure so exiting now' - println '' - System.exit(1) - } - println 'Using java 8: ' + jvmPath - options.bootstrapClasspath = fileTree(include: ['*.jar'], dir: jvmPath) -} - -compileTestJava { - options.compilerArgs << "-Xlint:all" << "-Xlint:-deprecation" << "-Werror" - - options.bootstrapClasspath = fileTree(include: ['*.jar'], dir: findJVM()) -} - -signing { - sign configurations.archives - if (! version.contains('SNAPSHOT')) { - sign publishing.publications - } -} - -publishing { - publications { - mavenJava(MavenPublication) { - pom { - name = 'Threadly' - description = 'A library of tools to assist with safe concurrent java development.' - url = 'http://threadly.org/' - - scm { - url = 'scm:git@github.com:threadly/threadly.git' - connection = 'scm:git@github.com:threadly/threadly.git' - developerConnection = 'scm:git@github.com:threadly/threadly.git' - } - - issueManagement { - system = 'GitHub' - url = 'https://github.com/threadly/threadly/issues' - } - - licenses { - license { - name = 'Mozilla Public License Version 2.0' - url = 'https://www.mozilla.org/MPL/2.0/' - distribution = 'repo' - } - } - - developers { - developer { - id = 'jent' - name = 'Mike Jensen' - email = 'jent@threadly.org' - } - } - } - - from components.java - - artifact(sourcesJar) { - classifier = 'sources' - } - artifact(javadocJar) { - classifier = 'javadoc' - } - } - } - repositories { - maven { - def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" - def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots" - url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl - credentials { - username sonatypeUsername - password sonatypePassword - } - } - } -} - -model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToMavenRepository { - dependsOn project.tasks.signArchives - } -} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 4a5e0dd..7f97afb 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -16,8 +16,7 @@ - - + diff --git a/gradle.properties b/gradle.properties index fb5053c..ac5fe3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.threadly -version = 1.1-SNAPSHOT +version = 2.0-SNAPSHOT org.gradle.parallel = false threadlyVersion = 6.0 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180..61285a6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e589..37f78a6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -133,22 +132,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -165,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..e509b2d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,32 +59,33 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/pipeline.yml b/pipeline.yml deleted file mode 100644 index b63e5f4..0000000 --- a/pipeline.yml +++ /dev/null @@ -1,27 +0,0 @@ -steps: - - command: "./gradlew clean assemble" - label: "compile" - artifact_paths: "build/libs/*.jar" - timeout: 10 - - - wait - - - command: "./gradlew -b build.slow test" - label: "unit tests" - artifact_paths: "build/reports/tests/xml/*.xml" - timeout: 20 - - - wait: ~ - continue_on_failure: true - - - plugins: - junit-annotate#v1.2.0: - artifacts: build/reports/tests/xml/*.xml - - - command: "./gradlew checkstyleMain" - label: "checkstyle" - timeout: 10 - - - command: "./gradlew jacocoTestCoverageVerification" - label: "jacoco" - timeout: 10 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..a55e95b --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'threadly-test' diff --git a/src/test/java/org/threadly/Repeat.java b/src/test/java/org/threadly/Repeat.java deleted file mode 100644 index e5d8ded..0000000 --- a/src/test/java/org/threadly/Repeat.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.threadly; - -import static java.lang.annotation.ElementType.ANNOTATION_TYPE; -import static java.lang.annotation.ElementType.METHOD; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@SuppressWarnings("javadoc") -@Retention(RetentionPolicy.RUNTIME) -@Target({ METHOD, ANNOTATION_TYPE }) -public @interface Repeat { - public int value() default 1; -} diff --git a/src/test/java/org/threadly/RepeatRule.java b/src/test/java/org/threadly/RepeatRule.java deleted file mode 100644 index 2acea8a..0000000 --- a/src/test/java/org/threadly/RepeatRule.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.threadly; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -@SuppressWarnings("javadoc") -public class RepeatRule implements TestRule { - @Override - public Statement apply(Statement statement, Description description) { - Statement result = statement; - Repeat repeat = description.getAnnotation(Repeat.class); - if (repeat != null) { - int times = repeat.value(); - result = new RepeatStatement(statement, times); - } - return result; - } - - private static class RepeatStatement extends Statement { - private final Statement statement; - private final int repeat; - - public RepeatStatement(Statement statement, int repeat) { - this.statement = statement; - this.repeat = repeat; - } - - @Override - public void evaluate() throws Throwable { - for (int i = 0; i < repeat; i++) { - statement.evaluate(); - } - } - } -} diff --git a/src/test/java/org/threadly/ThreadlyTester.java b/src/test/java/org/threadly/ThreadlyTester.java index 51e388a..e5cdc4a 100644 --- a/src/test/java/org/threadly/ThreadlyTester.java +++ b/src/test/java/org/threadly/ThreadlyTester.java @@ -2,7 +2,6 @@ import java.lang.Thread.UncaughtExceptionHandler; -import org.junit.Rule; import org.threadly.util.ExceptionHandler; import org.threadly.util.ExceptionUtils; import org.threadly.util.StringUtils; @@ -131,9 +130,6 @@ public static void setIgnoreExceptionHandler() { ExceptionUtils.setDefaultExceptionHandler(ieh); } - @Rule - public RepeatRule repeatRule = new RepeatRule(); - private static class IgnoreExceptionHandler implements UncaughtExceptionHandler, ExceptionHandler { @Override diff --git a/src/test/java/org/threadly/test/concurrent/AsyncVerifierTest.java b/src/test/java/org/threadly/test/concurrent/AsyncVerifierTest.java index be126a6..5ac4aa9 100644 --- a/src/test/java/org/threadly/test/concurrent/AsyncVerifierTest.java +++ b/src/test/java/org/threadly/test/concurrent/AsyncVerifierTest.java @@ -1,56 +1,56 @@ package org.threadly.test.concurrent; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.util.concurrent.TimeoutException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.threadly.ThreadlyTester; import org.threadly.util.ExceptionUtils; import org.threadly.util.StringUtils; @SuppressWarnings("javadoc") public class AsyncVerifierTest extends ThreadlyTester { - @BeforeClass + @BeforeAll public static void setupClass() { setIgnoreExceptionHandler(); } - - @AfterClass + + @AfterAll public static void cleanupClass() { Thread.setDefaultUncaughtExceptionHandler(null); } - + private static final int ASYNC_WAIT_AMOUNT = 2; - + private AsyncVerifier verifier; - - @Before + + @BeforeEach public void setup() { verifier = new AsyncVerifier(); } - - @After + + @AfterEach public void cleanup() { verifier = null; } - - @Test (expected = TimeoutException.class) - public void waitForTestTimeout() throws InterruptedException, TimeoutException { - verifier.waitForTest(1); + + @Test + public void waitForTestTimeout() { + assertThrows(TimeoutException.class, () -> verifier.waitForTest(1)); } - + @Test public void signalCompleteBeforeWaitTest() throws InterruptedException, TimeoutException { verifier.signalComplete(); - + verifier.waitForTest(1); // no exception should throw as no blocking is needed } - + @Test public void signalCompleteAnotherThreadTest() { TestRunnable waitRunnable = new TestRunnable() { @@ -64,53 +64,53 @@ public void handleRunFinish() { } }; new Thread(waitRunnable).start(); - + // should unblock thread verifier.signalComplete(); - + waitRunnable.blockTillFinished(); // should return quickly } - + @Test public void assertTrueTest() { verifier.assertTrue(true); } - - @Test (expected = RuntimeException.class) + + @Test public void assertTrueFail() { - verifier.assertTrue(false); + assertThrows(RuntimeException.class, () -> verifier.assertTrue(false)); } - + @Test public void assertFalseTest() { verifier.assertFalse(false); } - - @Test (expected = RuntimeException.class) + + @Test public void assertFalseFail() { - verifier.assertFalse(true); + assertThrows(RuntimeException.class, () -> verifier.assertFalse(true)); } - + @Test public void assertNullTest() { verifier.assertNull(null); } - - @Test (expected = RuntimeException.class) + + @Test public void assertNullFail() { - verifier.assertNull(new Object()); + assertThrows(RuntimeException.class, () -> verifier.assertNull(new Object())); } - + @Test public void assertNotNullTest() { verifier.assertNotNull(new Object()); } - - @Test (expected = RuntimeException.class) + + @Test public void assertNotNullFail() { - verifier.assertNotNull(null); + assertThrows(RuntimeException.class, () -> verifier.assertNotNull(null)); } - + @Test public void assertEqualsTest() { Object o = new Object(); @@ -120,22 +120,22 @@ public void assertEqualsTest() { verifier.assertEquals(1L, 1L); verifier.assertEquals(1.1, 1.1); } - - @Test (expected = RuntimeException.class) + + @Test public void assertEqualsObjectFail() { - verifier.assertEquals(null, new Object()); + assertThrows(RuntimeException.class, () -> verifier.assertEquals(null, new Object())); } - - @Test (expected = RuntimeException.class) + + @Test public void assertEqualsNumberFail() { - verifier.assertEquals(1, 10.0); + assertThrows(RuntimeException.class, () -> verifier.assertEquals(1, 10.0)); } - - @Test (expected = RuntimeException.class) + + @Test public void failTest() { - verifier.fail(); + assertThrows(RuntimeException.class, () -> verifier.fail()); } - + @Test public void failMsgTest() { String msg = StringUtils.makeRandomString(5); @@ -146,7 +146,7 @@ public void failMsgTest() { assertEquals(msg, e.getMessage()); } } - + @Test public void failThrowableTest() { Exception failure = new Exception(); @@ -157,156 +157,172 @@ public void failThrowableTest() { assertTrue(failure == e.getCause()); } } - - @Test (expected = RuntimeException.class) - public void assertTrueFailAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void assertTrueFailAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.assertTrue(false); } - - verifier.assertTrue(false); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void assertFalseFailAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void assertFalseFailAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.assertFalse(true); } - - verifier.assertFalse(true); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void assertNullFailAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void assertNullFailAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.assertNull(new Object()); } - - verifier.assertNull(new Object()); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void assertNotNullFailAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void assertNotNullFailAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.assertNotNull(null); } - - verifier.assertNotNull(null); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void assertEqualsObjectFailAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void assertEqualsObjectFailAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.assertEquals(new Object(), new Object()); } - - verifier.assertEquals(new Object(), new Object()); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void failTestAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void failTestAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.fail(); } - - verifier.fail(); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void failMsgTestAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void failMsgTestAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.fail("foo"); } - - verifier.fail("foo"); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } - - @Test (expected = RuntimeException.class) - public void failThrowableTestAnotherThreadTest() throws InterruptedException, TimeoutException { - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(ASYNC_WAIT_AMOUNT); - } catch (InterruptedException e) { - // let thread exit - return; + + @Test + public void failThrowableTestAnotherThreadTest() { + assertThrows(RuntimeException.class, () -> { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(ASYNC_WAIT_AMOUNT); + } catch (InterruptedException e) { + // let thread exit + return; + } + + verifier.fail(new Exception()); } - - verifier.fail(new Exception()); - } - }).start(); - - verifier.waitForTest(); + }).start(); + + verifier.waitForTest(); + }); } } diff --git a/src/test/java/org/threadly/test/concurrent/TestConditionTest.java b/src/test/java/org/threadly/test/concurrent/TestConditionTest.java index 1276051..5d7f141 100644 --- a/src/test/java/org/threadly/test/concurrent/TestConditionTest.java +++ b/src/test/java/org/threadly/test/concurrent/TestConditionTest.java @@ -1,11 +1,11 @@ package org.threadly.test.concurrent; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayDeque; import java.util.Queue; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.threadly.ThreadlyTester; import org.threadly.util.Clock; @@ -17,15 +17,15 @@ public void blockTest() { long startTime = Clock.accurateForwardProgressingMillis(); dc.blockTillTrue((DELAY_TIME * 10) + 5000); long endTime = Clock.accurateForwardProgressingMillis(); - + assertTrue(endTime - startTime >= DELAY_TIME); assertTrue(endTime - startTime <= DELAY_TIME + (SLOW_MACHINE ? 5000 : 1000)); - + startTime = Clock.accurateForwardProgressingMillis(); dc.blockTillTrue(); // should return immediately assertTrue(Clock.accurateForwardProgressingMillis() - startTime <= 10); } - + @Test public void blockFail() { DelayCondition dc = new DelayCondition(DELAY_TIME * 100); @@ -37,39 +37,41 @@ public void blockFail() { assertTrue(Clock.accurateForwardProgressingMillis() - startTime >= DELAY_TIME); } } - - @Test (expected = RuntimeException.class) + + @Test public void defaultConstructorFail() { - TestCondition tc = new TestCondition(); - tc.get(); + assertThrows(RuntimeException.class, () -> { + TestCondition tc = new TestCondition(); + tc.get(); + }); } - + @Test public void supplierPredicateTest() { Queue values = new ArrayDeque<>(8); values.add("foo"); values.add("false"); values.add("true"); - + new TestCondition(() -> values.remove(), Boolean::parseBoolean).blockTillTrue(); } - + private class DelayCondition extends TestCondition { private final long delayTime; private long firstGetTime; - + private DelayCondition(long delayTime) { this.delayTime = delayTime; firstGetTime = -1; } - + @Override public boolean get() { if (firstGetTime < 0) { firstGetTime = Clock.accurateForwardProgressingMillis(); return false; } - + return Clock.accurateForwardProgressingMillis() - firstGetTime >= delayTime; } } diff --git a/src/test/java/org/threadly/test/concurrent/TestRunnableTest.java b/src/test/java/org/threadly/test/concurrent/TestRunnableTest.java index ef0eb52..da530a1 100644 --- a/src/test/java/org/threadly/test/concurrent/TestRunnableTest.java +++ b/src/test/java/org/threadly/test/concurrent/TestRunnableTest.java @@ -1,10 +1,10 @@ package org.threadly.test.concurrent; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.threadly.ThreadlyTester; import org.threadly.test.concurrent.TestCondition.ConditionTimeoutException; import org.threadly.util.Clock; @@ -12,44 +12,44 @@ @SuppressWarnings("javadoc") public class TestRunnableTest extends ThreadlyTester { private TestRunnable instance; - - @Before + + @BeforeEach public void setup() { instance = new TestRunnable(); } - - @After + + @AfterEach public void cleanup() { instance = null; } - + @Test public void constructorTest() { assertEquals(0, instance.getRunCount()); assertFalse(instance.ranOnce()); assertFalse(instance.ranConcurrently()); assertEquals(0, instance.getRunDelayInMillis()); - + instance = new TestRunnable(DELAY_TIME); assertEquals(DELAY_TIME, instance.getRunDelayInMillis()); } - + @Test public void setRunDelayInMillisTest() { assertEquals(0, instance.getRunDelayInMillis()); instance.setRunDelayInMillis(DELAY_TIME); assertEquals(DELAY_TIME, instance.getRunDelayInMillis()); } - + @Test public void runTest() { TestTestRunnable ttr = new TestTestRunnable(); long start = Clock.accurateForwardProgressingMillis(); - + TestUtils.blockTillClockAdvances(); - + ttr.run(); - + assertTrue(ttr.handleRunStartCalled); assertTrue(ttr.handleRunFinishCalled); assertTrue(ttr.startCalledBeforeFinish); @@ -58,35 +58,35 @@ public void runTest() { assertTrue(ttr.getDelayTillFirstRun() > 0); TestUtils.blockTillClockAdvances(); - + ttr.run(); - + TestUtils.blockTillClockAdvances(); - + long now = Clock.accurateForwardProgressingMillis(); assertTrue(ttr.getDelayTillRun(2) <= now - start); assertTrue(ttr.getDelayTillRun(2) > ttr.getDelayTillFirstRun()); } - + @Test public void runWithDelay() { int runCount = TEST_QTY / 2; instance.setRunDelayInMillis(DELAY_TIME); - + long startTime = Clock.accurateForwardProgressingMillis(); for (int i = 0; i < runCount; i++) { instance.run(); } long endTime = Clock.accurateForwardProgressingMillis(); - + assertTrue(endTime - startTime >= ((DELAY_TIME * runCount)- ALLOWED_VARIANCE)); } - + @Test public void blockTillRunTest() { TestRunnable tr = new TestRunnable() { private boolean firstRun = true; - + @Override public void handleRunStart() throws InterruptedException { if (firstRun) { @@ -97,73 +97,75 @@ public void handleRunStart() throws InterruptedException { } }; new Thread(tr).start(); - + long startTime = Clock.accurateForwardProgressingMillis(); tr.blockTillFinished(1000, 2); long endTime = Clock.accurateForwardProgressingMillis(); - + assertTrue(endTime - startTime >= (DELAY_TIME - ALLOWED_VARIANCE)); } - - @Test (expected = ConditionTimeoutException.class) + + @Test public void blockTillRunTestFail() { - instance.blockTillFinished(DELAY_TIME); - - fail("Exception should have thrown"); + assertThrows(ConditionTimeoutException.class, () -> { + instance.blockTillFinished(DELAY_TIME); + + fail("Exception should have thrown"); + }); } - + @Test public void ranConcurrentlyTest() { TestRunnable notConcurrentTR = new TestRunnable(); - + notConcurrentTR.run(); notConcurrentTR.run(); assertFalse(notConcurrentTR.ranConcurrently()); - + TestRunnable concurrentTR = new TestRunnable() { private boolean ranOnce = false; - + @Override public void handleRunStart() { if (! ranOnce) { // used to prevent infinite recursion ranOnce = true; - + this.run(); } } }; - + concurrentTR.run(); assertTrue(concurrentTR.ranConcurrently()); } - + @Test public void currentlyRunningTest() { BlockingTestRunnable btr = new BlockingTestRunnable(); - + assertFalse(btr.isRunning()); - + new Thread(btr).start(); try { btr.blockTillStarted(); - + assertTrue(btr.isRunning()); } finally { btr.unblock(); } } - + private class TestTestRunnable extends TestRunnable { private boolean handleRunStartCalled = false; private boolean handleRunFinishCalled = false; private boolean startCalledBeforeFinish = false; - + @Override public void handleRunStart() { handleRunStartCalled = true; } - + @Override public void handleRunFinish() { handleRunFinishCalled = true; diff --git a/src/test/java/org/threadly/test/concurrent/TestUtilTest.java b/src/test/java/org/threadly/test/concurrent/TestUtilTest.java index 8a5fd28..a5368b5 100644 --- a/src/test/java/org/threadly/test/concurrent/TestUtilTest.java +++ b/src/test/java/org/threadly/test/concurrent/TestUtilTest.java @@ -1,10 +1,10 @@ package org.threadly.test.concurrent; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.threadly.ThreadlyTester; import org.threadly.concurrent.SingleThreadScheduler; import org.threadly.concurrent.future.ListenableFuture; @@ -19,7 +19,7 @@ public void sleepTest() { long end = Clock.accurateForwardProgressingMillis(); assertTrue(end - start >= (DELAY_TIME - ALLOWED_VARIANCE)); } - + @Test public void sleepInterruptedTest() { SingleThreadScheduler sts = new SingleThreadScheduler(); @@ -34,15 +34,15 @@ public void run() { // spin } TestUtils.sleep(DELAY_TIME); - + testThread.interrupt(); } }); - + aboutToSleep.set(true); TestUtils.sleep(1000 * 20); // should wake up from interrupt - + assertTrue(Thread.interrupted()); } finally { sts.shutdownNow(); @@ -51,7 +51,7 @@ public void run() { } } } - + @Test public void blockTillClockAdvancesTest() { long before = Clock.accurateTimeMillis(); diff --git a/src/test/java/org/threadly/test/concurrent/TestableSchedulerTest.java b/src/test/java/org/threadly/test/concurrent/TestableSchedulerTest.java index b60d684..a3b1be2 100644 --- a/src/test/java/org/threadly/test/concurrent/TestableSchedulerTest.java +++ b/src/test/java/org/threadly/test/concurrent/TestableSchedulerTest.java @@ -1,6 +1,6 @@ package org.threadly.test.concurrent; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.Iterator; @@ -10,9 +10,9 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.threadly.ThreadlyTester; import org.threadly.concurrent.CentralThreadlyPool; import org.threadly.concurrent.DoNothingRunnable; @@ -24,56 +24,56 @@ @SuppressWarnings("javadoc") public class TestableSchedulerTest extends ThreadlyTester { protected TestableScheduler scheduler; - - @Before + + @BeforeEach public void setup() { scheduler = new TestableScheduler(); } - - @After + + @AfterEach public void cleanup() { scheduler = null; } - + private static List getRunnableList() { List result = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { result.add(new TestRunnable()); } - + return result; } - + private static List getCallableList() { List result = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { result.add(new TestCallable()); } - + return result; } - + @Test public void isShutdownTest() { assertFalse(scheduler.isShutdown()); } - + @Test public void getDefaultPriorityTest() { assertEquals(TaskPriority.High, scheduler.getDefaultPriority()); assertEquals(TaskPriority.Low, new TestableScheduler(TaskPriority.Low, 100).getDefaultPriority()); } - + @Test public void getMaxWaitForLowPriorityTest() { assertEquals(100, new TestableScheduler(TaskPriority.Low, 100).getMaxWaitForLowPriority()); } - + @Test public void getQueuedTaskCountTest() { for (int i = 0; i < TEST_QTY; i++) { TaskPriority priority = i % 2 == 0 ? TaskPriority.High : TaskPriority.Low; - + assertEquals(i, scheduler.getQueuedTaskCount()); assertEquals(i / 2, scheduler.getQueuedTaskCount(priority)); @@ -84,15 +84,15 @@ public void getQueuedTaskCountTest() { } } } - + @Test public void getWaitingForExecutionTaskCountTest() { for (int i = 0; i < TEST_QTY; i++) { TaskPriority priority = i % 2 == 0 ? TaskPriority.High : TaskPriority.Low; - + assertEquals(i, scheduler.getWaitingForExecutionTaskCount()); assertEquals(i / 2, scheduler.getWaitingForExecutionTaskCount(priority)); - + if (i <= 5) { scheduler.submit(DoNothingRunnable.instance(), priority); } else { @@ -101,37 +101,37 @@ public void getWaitingForExecutionTaskCountTest() { scheduler.submitScheduled(DoNothingRunnable.instance(), 10_000, priority); // task should be ignored } } - + @Test public void getActiveTaskCountTest() { BlockingTestRunnable btr = new BlockingTestRunnable(); try { scheduler.execute(btr); - + assertEquals(0, scheduler.getActiveTaskCount()); - + CentralThreadlyPool.isolatedTaskPool().execute(() -> scheduler.tick()); - + new TestCondition(() -> scheduler.getActiveTaskCount() == 1).blockTillTrue(); } finally { btr.unblock(); } } - + @Test public void lastTickTimeTest() { long now = Clock.lastKnownTimeMillis(); - + scheduler.tick(now + 1000); - + assertEquals(now + 1000, scheduler.getLastTickTime()); } - + @Test public void advanceWithoutHandlerThrowsRuntimeExceptionTest() { RuntimeException failure = new StackSuppressedRuntimeException(); scheduler.execute(new TestRuntimeFailureRunnable(failure)); - + try { scheduler.advance(10); fail("Exception should have thrown"); @@ -139,29 +139,29 @@ public void advanceWithoutHandlerThrowsRuntimeExceptionTest() { assertTrue(e == failure); } } - + @Test public void advanceHandlesRuntimeExceptionTest() { RuntimeException failure = new StackSuppressedRuntimeException(); final AtomicReference handledException = new AtomicReference<>(null); scheduler.execute(new TestRuntimeFailureRunnable(failure)); - + int runCount = scheduler.advance(10, new ExceptionHandler() { @Override public void handleException(Throwable thrown) { handledException.set(thrown); } }); - + assertEquals(1, runCount); assertTrue(handledException.get() == failure); } - + @Test public void tickWithoutHandlerThrowsRuntimeExceptionTest() { RuntimeException failure = new StackSuppressedRuntimeException(); scheduler.execute(new TestRuntimeFailureRunnable(failure)); - + try { scheduler.tick(); fail("Exception should have thrown"); @@ -169,44 +169,44 @@ public void tickWithoutHandlerThrowsRuntimeExceptionTest() { assertTrue(e == failure); } } - + @Test public void tickHandlesRuntimeExceptionTest() { RuntimeException failure = new StackSuppressedRuntimeException(); final AtomicReference handledException = new AtomicReference<>(null); scheduler.execute(new TestRuntimeFailureRunnable(failure)); - + int runCount = scheduler.tick(new ExceptionHandler() { @Override public void handleException(Throwable thrown) { handledException.set(thrown); } }); - + assertEquals(1, runCount); assertTrue(handledException.get() == failure); } - + @Test public void advanceTest() { long now = scheduler.getLastTickTime(); scheduler.advance(1000); - + assertEquals(now + 1000, scheduler.getLastTickTime()); } - + @Test public void advanceThenTickTest() { scheduler.execute(DoNothingRunnable.instance()); scheduler.advance(1000 * 10); - + TestRunnable tr = new TestRunnable(); scheduler.execute(tr); - + assertEquals(1, scheduler.tick()); assertTrue(tr.ranOnce()); } - + @Test public void executeTest() { List runnables = getRunnableList(); @@ -215,24 +215,24 @@ public void executeTest() { while (it.hasNext()) { scheduler.execute(it.next()); } - + // all should run now assertEquals(TEST_QTY, scheduler.tick(Clock.lastKnownTimeMillis())); - + it = runnables.iterator(); while (it.hasNext()) { assertEquals(1, it.next().getRunCount()); } - + // verify no more run after a second tick assertEquals(scheduler.tick(), 0); - + it = runnables.iterator(); while (it.hasNext()) { assertEquals(1, it.next().getRunCount()); } } - + @Test public void submitRunnableTest() { List runnables = getRunnableList(); @@ -243,29 +243,29 @@ public void submitRunnableTest() { assertNotNull(future); futures.add(future); } - + // all should run now assertEquals(TEST_QTY, scheduler.tick(Clock.lastKnownTimeMillis())); - + it = runnables.iterator(); while (it.hasNext()) { assertEquals(1, it.next().getRunCount()); } - + // verify no more run after a second tick assertEquals(0, scheduler.tick()); - + it = runnables.iterator(); while (it.hasNext()) { assertEquals(1, it.next().getRunCount()); } - + Iterator> futureIt = futures.iterator(); while (futureIt.hasNext()) { assertTrue(futureIt.next().isDone()); } } - + @Test public void submitCallableTest() throws InterruptedException, ExecutionException { List callables = getCallableList(); @@ -276,10 +276,10 @@ public void submitCallableTest() throws InterruptedException, ExecutionException assertNotNull(future); futures.add(future); } - + // all should run now assertEquals(TEST_QTY, scheduler.tick(Clock.lastKnownTimeMillis())); - + it = callables.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); @@ -290,19 +290,19 @@ public void submitCallableTest() throws InterruptedException, ExecutionException while (futureIt.hasNext()) { Future future = futureIt.next(); TestCallable tc = it.next(); - + assertTrue(future.isDone()); assertTrue(tc.getReturnedResult() == future.get()); } } - + @Test public void scheduleRunnableTest() { long scheduleDelay = 1000 * 10; - + TestRunnable executeRun = new TestRunnable(); TestRunnable scheduleRun = new TestRunnable(); - + scheduler.schedule(scheduleRun, scheduleDelay); scheduler.execute(executeRun); @@ -310,25 +310,25 @@ public void scheduleRunnableTest() { assertEquals(1, executeRun.getRunCount()); // should have run assertEquals(0, scheduleRun.getRunCount()); // should NOT have run yet - + assertEquals(1, scheduler.advance(scheduleDelay)); - + assertEquals(1, executeRun.getRunCount()); // should NOT have run again assertEquals(1, scheduleRun.getRunCount()); // should have run - + assertEquals(scheduler.advance(scheduleDelay), 0); // should not execute anything - + assertEquals(1, executeRun.getRunCount()); // should NOT have run again assertEquals(1, scheduleRun.getRunCount()); // should NOT have run again } - + @Test public void submitScheduledRunnableTest() { long scheduleDelay = 1000 * 10; - + TestRunnable submitRun = new TestRunnable(); TestRunnable scheduleRun = new TestRunnable(); - + Future future = scheduler.submit(submitRun); assertNotNull(future); future = scheduler.submitScheduled(scheduleRun, scheduleDelay); @@ -338,18 +338,18 @@ public void submitScheduledRunnableTest() { assertEquals(1, submitRun.getRunCount()); // should have run assertEquals(0, scheduleRun.getRunCount()); // should NOT have run yet - + assertEquals(1, scheduler.advance(scheduleDelay)); - + assertEquals(1, submitRun.getRunCount()); // should NOT have run again assertEquals(1, scheduleRun.getRunCount()); // should have run - + assertEquals(0, scheduler.advance(scheduleDelay)); // should not execute anything - + assertEquals(1, submitRun.getRunCount()); // should NOT have run again assertEquals(1, scheduleRun.getRunCount()); // should NOT have run again } - + @Test public void submitScheduledRunnableFail() { try { @@ -365,14 +365,14 @@ public void submitScheduledRunnableFail() { // expected } } - + @Test public void submitScheduledCallableTest() { long scheduleDelay = 1000 * 10; - + TestCallable submitRun = new TestCallable(); TestCallable scheduleRun = new TestCallable(); - + Future future = scheduler.submit(submitRun); assertNotNull(future); future = scheduler.submitScheduled(scheduleRun, scheduleDelay); @@ -382,14 +382,14 @@ public void submitScheduledCallableTest() { assertTrue(submitRun.isDone()); // should have run assertFalse(scheduleRun.isDone()); // should NOT have run yet - + assertEquals(1, scheduler.advance(scheduleDelay)); - + assertTrue(scheduleRun.isDone()); // should have run - + assertEquals(0, scheduler.advance(scheduleDelay)); // should not execute anything } - + @Test public void submitScheduledCallableFail() { try { @@ -405,18 +405,18 @@ public void submitScheduledCallableFail() { // expected } } - + @Test public void scheduleWithFixedDelayTest() { scheduleRecurringTest(true); } - + private void scheduleRecurringTest(boolean fixedDelay) { long delay = 1000 * 10; - + TestRunnable immediateRun = new TestRunnable(); TestRunnable initialDelay = new TestRunnable(); - + if (fixedDelay) { scheduler.scheduleWithFixedDelay(immediateRun, 0, delay); scheduler.scheduleWithFixedDelay(initialDelay, delay, delay); @@ -426,26 +426,26 @@ private void scheduleRecurringTest(boolean fixedDelay) { } assertEquals(1, scheduler.tick()); - + assertEquals(1, immediateRun.getRunCount()); // should have run assertEquals(0, initialDelay.getRunCount()); // should NOT have run yet assertEquals(2, scheduler.advance(delay)); - + assertEquals(2, immediateRun.getRunCount()); // should have run again assertEquals(1, initialDelay.getRunCount()); // should have run for the first time - + assertEquals(2, scheduler.advance(delay)); - + assertEquals(3, immediateRun.getRunCount()); // should have run again assertEquals(2, initialDelay.getRunCount()); // should have run again - + assertEquals(0, scheduler.tick(scheduler.getLastTickTime())); // should not execute anything - + assertEquals(3, immediateRun.getRunCount()); // should NOT have run again assertEquals(2, initialDelay.getRunCount()); // should NOT have run again } - + @Test public void scheduleWithFixedDelayFail() { try { @@ -467,12 +467,12 @@ public void scheduleWithFixedDelayFail() { // expected } } - + @Test public void scheduleAtFixedRateTest() { scheduleRecurringTest(false); } - + @Test public void scheduleAtFixedRateFail() { try { @@ -494,74 +494,74 @@ public void scheduleAtFixedRateFail() { // expected } } - + @Test public void removeRunnableTest() { long delay = 1000 * 10; - + TestRunnable immediateRun = new TestRunnable(); TestRunnable initialDelay = new TestRunnable(); - + assertFalse(scheduler.remove(immediateRun)); - + scheduler.scheduleWithFixedDelay(immediateRun, 0, delay, TaskPriority.High); assertTrue(scheduler.remove(immediateRun)); - + scheduler.scheduleWithFixedDelay(immediateRun, 0, delay); scheduler.scheduleWithFixedDelay(initialDelay, delay, delay); - + assertEquals(1, scheduler.tick()); - + assertEquals(1, immediateRun.getRunCount()); // should have run assertEquals(0, initialDelay.getRunCount()); // should NOT have run yet - + assertTrue(scheduler.remove(immediateRun)); - + assertEquals(1, scheduler.advance(delay)); - + assertEquals(1, immediateRun.getRunCount()); // should NOT have run again assertEquals(1, initialDelay.getRunCount()); // should have run - + assertEquals(0, scheduler.tick(scheduler.getLastTickTime())); // should not execute anything - + assertEquals(1, immediateRun.getRunCount()); // should NOT have run assertEquals(1, initialDelay.getRunCount()); // should NOT have run } - + @Test public void removeCallableTest() { long delay = 1000 * 10; - + TestCallable immediateRun = new TestCallable(); TestCallable delayRun = new TestCallable(); - + assertFalse(scheduler.remove(immediateRun)); - + scheduler.submitScheduled(immediateRun, 0); assertTrue(scheduler.remove(immediateRun)); assertFalse(scheduler.remove(immediateRun)); - + scheduler.submitScheduled(delayRun, delay); - + assertEquals(0, scheduler.tick()); - + // neither should run yet assertFalse(immediateRun.isDone()); assertFalse(delayRun.isDone()); - + scheduler.submitScheduled(immediateRun, 0); - + assertEquals(2, scheduler.advance(delay)); - + // both should run now assertTrue(immediateRun.isDone()); assertTrue(delayRun.isDone()); - + // neither should be in scheduler any more assertFalse(scheduler.remove(immediateRun)); assertFalse(scheduler.remove(delayRun)); } - + @Test public void removeWhileRunningTest() { TestRunnable tr = new TestRunnable() { @@ -570,56 +570,58 @@ public void handleRunStart() { assertTrue(scheduler.remove(this)); } }; - + scheduler.scheduleWithFixedDelay(tr, 0, 0, TaskPriority.High); - + assertEquals(1, scheduler.tick()); - + // should be removed for subsequent ticks assertEquals(0, scheduler.tick()); - + assertEquals(1, tr.getRunCount()); } - @Test (expected = IllegalArgumentException.class) + @Test public void tickFail() { - scheduler.tick(scheduler.getLastTickTime() - 1); - fail("Exception should have been thrown"); + assertThrows(IllegalArgumentException.class, () -> { + scheduler.tick(scheduler.getLastTickTime() - 1); + fail("Exception should have been thrown"); + }); } - + @Test public void hasTaskReadyToRunTest() { assertFalse(scheduler.hasTaskReadyToRun()); - + scheduler.schedule(DoNothingRunnable.instance(), 1); assertFalse(scheduler.hasTaskReadyToRun()); - + scheduler.execute(DoNothingRunnable.instance()); assertTrue(scheduler.hasTaskReadyToRun()); } - + @Test public void getDelayTillNextTaskTest() { assertEquals(Long.MAX_VALUE, scheduler.getDelayTillNextTask()); - + scheduler.schedule(DoNothingRunnable.instance(), 1); assertEquals(1, scheduler.getDelayTillNextTask()); - + scheduler.execute(DoNothingRunnable.instance()); assertEquals(0, scheduler.getDelayTillNextTask()); } - + @Test public void clearTasksTest() { assertTrue(scheduler.clearTasks().isEmpty()); - + TestRunnable executeTask = new TestRunnable(); TestRunnable scheduleTask = new TestRunnable(); scheduler.execute(executeTask); scheduler.schedule(scheduleTask, 1); - + List result = scheduler.clearTasks(); - + assertEquals(2, result.size()); assertTrue(result.contains(executeTask)); assertTrue(result.contains(scheduleTask));