diff --git a/build.gradle b/build.gradle index 2300dc8..d9e61c8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,89 +5,213 @@ * See the COPYING file for more details. */ +/* + * List of tasks that you may need: + * + * - koan - Runs a single Koan. + * - dependencyUpdates - check for new dependency versions. + * + * Project properties (for use with -P): + * + * - ff - apply 'fail fast' JUnit rule. + * - fe - show the first error only on the console. + * + */ + import java.awt.* -import java.util.List +import org.gradle.util.WrapUtil + +plugins { + id 'groovy' + id 'idea' + id 'eclipse' -apply plugin: 'groovy' -apply from: 'gradle/idea.gradle' -apply from: 'gradle/eclipse.gradle' + // Gradle Versions Plugin: https://github.com/ben-manes/gradle-versions-plugin + // https://bintray.com/fooberger/maven/com.github.ben-manes%3Agradle-versions-plugin + // Use: repositories { jcenter() } + id 'com.github.ben-manes.versions' version '0.20.0' +} -version = '0.5' +/* + * Version 0.6: JUnit 5 engine support added. Tested with Gradle 4.9. + */ +version = '0.6' repositories { - jcenter() + jcenter() } dependencies { - def groovyComponents = ['groovy', 'groovy-ant', 'groovy-sql', 'groovy-test'] - testCompile groovyComponents.collect { "org.codehaus.groovy:$it:2.4.3" } - testCompile 'com.h2database:h2:1.3.168' - testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' + def final groovyComponents = ['groovy', 'groovy-ant', 'groovy-sql', 'groovy-test'] + def final junitVersion = '5.2.0' + + testCompile groovyComponents.collect { "org.codehaus.groovy:$it:2.5.2" } + testCompile 'com.h2database:h2:1.4.197' + testCompile 'org.spockframework:spock-core:1.1-groovy-2.4' + + // ---------------------- + // JUnit modules + // + testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: junitVersion // JUnit 4 support + // JUnit modules + // ---------------------- +} + +wrapper { + gradleVersion = '4.9' } task removeSolutions { - description = 'Removes the solutions from all Koans, so you can start fresh!' - - doLast { - def solutionsRemoved = 0 - def solutionRegex = $/(?sm)(// -{12} START EDITING HERE -{22})(.*?)(\s+// -{12} STOP EDITING HERE -{22})/$ - sourceSets.test.allSource.files.each { File sourceFile -> - solutionsRemoved += (sourceFile.text =~ solutionRegex).count - sourceFile.text = sourceFile.text.replaceAll(solutionRegex, '$1\n\n$3') - } - println "Removed $solutionsRemoved solutions from Koans. Good luck!" - } + description = 'Removes the solutions from all Koans, so you can start fresh!' + + doLast { + def solutionsRemoved = 0 + def solutionRegex = $/(?sm)(// -{12} START EDITING HERE -{22})(.*?)(\s+// -{12} STOP EDITING HERE -{22})/$ + sourceSets.test.allSource.files.each { File sourceFile -> + solutionsRemoved += (sourceFile.text =~ solutionRegex).count + sourceFile.text = sourceFile.text.replaceAll(solutionRegex, '$1\n\n$3') + } + println "Removed $solutionsRemoved solutions from Koans. Good luck!" + } +} + +class TestInformation { + def td + def tr + + String getClassName() { td.className } + String getMethodName() { td.name } + String getFullName() { td.className + '.' + td.name } +} +def newTestInfo = {td, tr -> new TestInformation(td: td, tr: tr)} +def failedTests = [] + +test { + description "Execute a set of tests" + // Setup JUnit... + useJUnitPlatform() + + testLogging { + events "PASSED", "FAILED" //, "STARTED", "SKIPPED" + showStandardStreams true + } + + if (project.hasProperty('ff')) { + println "Test task: Adding failFast=true" + failFast true + } + + onOutput {td, event -> + println event.message + } + + beforeSuite { td -> + if (td.className != null) println "\n*** Test suite: ${td}" + } + + afterSuite { td, tr -> + if (tr.exception != null) println "\n${tr}: ${td}, exception message:\n\n${tr.exception.message}" + } + + afterTest { td, tr -> + if (tr.exception != null) { + failedTests << newTestInfo(td, tr) // Add a test result to the list + // println "\n${tr}: ${td}, exception message:\n\n${tr.exception.message}" + } + } +} + +// launch browser if test failed. otherwise show that koan is complete +test.finalizedBy('afterTests') +task afterTests() { + doFirst { + if (tasks['test'].state.failure) { + println "Errors found. You can use lynx or another browser to view the report: ${tasks['test'].reports.html.destination}/index.html" + + // Output errors to the console... + if (failedTests) { + def cRed = "" + (char) 27 + '[31m' + def cDef = "" + (char) 27 + '[39m' + def sorted = failedTests.toSorted {a, b -> a.fullName <=> b.fullName} + def hdr = "--- THERE ARE FAILED TESTS ---" + def dashLine = '-' * hdr.length() + + def showError = { testInfo -> + def ex = testInfo.tr.exception + println "\n${cRed}-- ${testInfo.fullName} --${cDef}\n" + println "${ex.stackTrace.find {it.className == testInfo.className && it.methodName == testInfo.methodName}}:\n" + println ex.message + } + + println "\n${dashLine}\n${hdr}\n${dashLine}" + + // Show just a first error if 'fe' property specified (-Pfe) + if (project.hasProperty('fe')) + showError sorted[0] // Show the first error + else + sorted.each showError // Show all errors + + println dashLine + } + + // Open the report with a default browser... + Desktop.desktop.browse(new File(tasks['test'].reports.html.destination, 'index.html').toURI()) + } else { + println "Koan is complete. Well done!" + } + } } // Adapted from http://www.practicalgradle.org/blog/2011/01/convenient-test-execution-with-camel-case-support/ tasks.addRule('Pattern: koan: Runs a single Koan') { String taskName -> - if (taskName.startsWith('koan') && taskName.length() > 5) { - // create a dummy task for the task name specified on the command line - def dummyTask = task(taskName) - def koanName = taskName[0].toUpperCase() + taskName[1..-1] - - // make all Test tasks a dependency of the dummy task and reset the includes - tasks.withType(Test) { testTask -> - logger.info("Single Koan Execution: apply include pattern to Test task <$testTask.name>") - testTask.includes = WrapUtil.toSet("**/${koanName}.class") - dummyTask.dependsOn testTask - } - } -} + if (taskName.startsWith('koan') && taskName.length() > 5) { + // create a dummy task for the task name specified on the command line + def dummyTask = task(taskName) + def koanName = taskName[0].toUpperCase() + taskName[1..-1] -task wrapper(type: Wrapper) { - gradleVersion = '2.8' + // make all Test tasks a dependency of the dummy task and reset the includes + tasks.withType(Test) { testTask -> + logger.info("Single Koan Execution: apply include pattern to Test task <$testTask.name>") + testTask.includes = WrapUtil.toSet("**/${koanName}.class") + dummyTask.dependsOn testTask + } + } } // show welcome message -task sayHello << { - List lines = file('gradle/one-liners.txt').readLines() - def quote = lines.get(new Random().nextInt(lines.size() - 1)) - println "Groovy Koans ${version}:\n${quote}" - println "${'-' * quote.size()}\n" +task sayHello { + doLast { + def lines = file('gradle/one-liners.txt').readLines() + def quote = lines.get(new Random().nextInt(lines.size() - 1)) + println "Groovy Koans ${version}:\n${quote}" + println "${'-' * quote.size()}\n" + } } - test.dependsOn sayHello -// launch browser if test failed. otherwise show that koan is complete -gradle.taskGraph.afterTask { task, taskState -> - if (task.name != 'test') - return - - if (taskState.failure) { - String testRepDir = project.testReportDir - Desktop.desktop.browse(new File(testRepDir, 'index.html').toURI()) - } else { - println "Koan is complete. Well done!" - } -} +// +// Check for new versions of dependencies +// +dependencyUpdates { +// revision 'release' // Only look for a new releases -// since we're running in quiet logging, show the user what tests are running -tasks.withType(Test) { testTask -> - testTask.beforeTest { descriptor -> - print "Running exercises in $descriptor.name()".padRight(60, '.') - } - testTask.afterTest { descriptor, result -> - println result.resultType - } + // Exclude pre-release versions from the list... + resolutionStrategy = { + componentSelection { rules -> + rules.all { ComponentSelection selection -> + boolean rejected = [ + 'alpha', + 'beta', + 'rc', + 'cr', + 'm' + ].any { qualifier -> + selection.candidate.version ==~ /(?i).*[\.-]${qualifier}.*/ + } + if (rejected) { + selection.reject('Release candidate') + } + } + } + } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e8c6bf7..0d4a951 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 056f334..a95009c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Thu Oct 22 22:42:17 PDT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip diff --git a/gradlew b/gradlew index 9bf1d27..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" -q +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 692f8f7..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line @@ -72,7 +66,7 @@ set CMD_LINE_ARGS=%$ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% -q +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell diff --git a/readme.md b/readme.md index fa5a835..7dbb4cb 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ to offer. ![Build Status](https://api.travis-ci.org/nadavc/groovykoans.svg?branch=master) ## Getting Started ## -1. Make sure you have [JDK 1.6+][jdk] installed +1. Make sure you have [JDK 1.7+][jdk] installed 2. Download and unzip the [Koans][zip] (or clone the GitHub repository with `$ git clone https://github.com/nadavc/groovykoans.git`) 3. Remove the solutions from the Koans using `$ ./gradlew removeSolutions` 4. Execute Koan01 with `$ ./gradlew koan01` and fail (or any other Koan using `$ ./gradlew koan##`) diff --git a/src/test/groovy/org/groovykoans/koan03/JavaPerson.java b/src/test/groovy/org/groovykoans/koan03/JavaPerson.java index 177e7e4..897de61 100644 --- a/src/test/groovy/org/groovykoans/koan03/JavaPerson.java +++ b/src/test/groovy/org/groovykoans/koan03/JavaPerson.java @@ -14,8 +14,9 @@ * First and last name can be modified, but ssn is forever. */ public class JavaPerson implements Serializable { - - private String firstName; + private static final long serialVersionUID = 1L; + + private String firstName; private String lastName; private final String ssn;