Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ tests/**/build/
tests/**/gradle/
tests/**/.gradle/
tests/**/*.groovy.original
target/
jansi-tmp/
29 changes: 21 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
FROM gradle:6.3.0-jdk11
# === Build maven cache ===

FROM maven:3.8.3-jdk-11 AS cache

# Ensure exercise dependencies are downloaded
WORKDIR /opt/exercise
COPY src/ src/
COPY pom.xml .
RUN mvn test dependency:go-offline -DexcludeReactor=false

# === Build runtime image ===

FROM maven:3.8.3-jdk-11
WORKDIR /opt/test-runner

RUN apt-get update && \
apt-get install -y jq && \
apt-get purge --auto-remove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

WORKDIR /opt/test-runner
# necessary because of https://github.com/keeganwitt/docker-gradle#reusing-the-gradle-cache
ENV GRADLE_USER_HOME /root/
# Copy resources
COPY . .

COPY src/ src/
COPY build.gradle .
RUN gradle build
# Copy cached dependencies
COPY --from=cache /root/.m2 /root/.m2

COPY . .
# Copy Maven pom.xml
COPY --from=cache /opt/exercise/pom.xml /root/pom.xml

ENTRYPOINT ["/opt/test-runner/bin/run.sh"]

37 changes: 25 additions & 12 deletions bin/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ results_file="${output_dir}/results.json"
# Create the output directory if it doesn't exist
mkdir -p "${output_dir}"

# Run this once to not show the welcome message
gradle --version > /dev/null

echo "${slug}: testing..."

cp "${tests_file}" "${tests_file_original}"
Expand All @@ -47,11 +44,22 @@ sed -i -E "s/^class/@Stepwise\nclass/" "${tests_file}"

pushd "${input_dir}" > /dev/null

cp /root/pom.xml .

# jansi tmp directory needs to be a RWX folder
mkdir -p /solution/jansi-tmp
# Tuning those parametes can speed up things
export JAVA_TOOL_OPTIONS="-Djansi.tmpdir=/solution/jansi-tmp -Xss128m -Xms256m -Xmx2G -XX:+UseG1GC"

# Remove maven cache if it exists
rm -rf target
# Run the tests for the provided implementation file and redirect stdout and
# stderr to capture it
test_output=$(gradle --offline --console=plain test 2>&1)
test_output=$(mvn --offline --legacy-local-repository --batch-mode --non-recursive --quiet test 2>&1)
exit_code=$?

rm -f pom.xml

popd > /dev/null

# Restore the original file
Expand All @@ -62,20 +70,25 @@ mv -f "${tests_file_original}" "${tests_file}"
if [ $exit_code -eq 0 ]; then
jq -n '{version: 1, status: "pass"}' > ${results_file}
else

# Sanitize the output
sanitized_output=$(printf "${test_output}" | \
sed -E \
-e 's/^Starting a Gradle Daemon.*$//' \
-e 's/See the report.*//' \
-e '/^> Task/d' | \
sed -n '/Try:/q;p' | \
sed -e '/./,$!d' -e :a -e '/^\n*$/{$d;N;ba' -e '}' | \
sed -e '/^$/N;/^\n$/D')
-e '/Picked up JAVA_TOOL_OPTIONS*/d' \
-e '/\[ERROR\] Picked up JAVA_TOOL_OPTIONS*/d' \
-e '/\[ERROR\] Please refer to*/d' \
-e '/\[ERROR\] To see the full stack trace*/d' \
-e '/\[ERROR\] -> \[Help 1\]*/d' \
-e '/\[ERROR\] $/d' \
-e '/\[ERROR\] For more information about the errors*/d' \
-e '/\[ERROR\] Re-run Maven using the -X*/d' \
-e '/\[ERROR\] Failed to execute goal*/d' \
-e '/\[ERROR\] \[Help 1\]*/d' |
sed -e 's/Time elapsed:.*s//g')

# Manually add colors to the output to help scanning the output for errors
colorized_test_output=$(echo "${sanitized_output}" | \
GREP_COLOR='01;31' grep --color=always -E -e '(^FAIL.*$|.*FAILED$)|$' | \
GREP_COLOR='01;32' grep --color=always -E -e '.*PASSED$|$')
GREP_COLOR='01;31' grep --color=always -E -e '^\[ERROR\].+$|$')

jq -n --arg output "${colorized_test_output}" '{version: 1, status: "fail", message: $output}' > ${results_file}
fi
Expand Down
129 changes: 129 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.exercism</groupId>
<artifactId>exercise</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<groovy.version>3.0.8</groovy.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>2.0-groovy-3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.8</version>
<type>pom</type>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
</plugin>

<plugin>
<!-- The gmavenplus plugin is used to compile Groovy code. To learn more about this plugin,
visit https://github.com/groovy/GMavenPlus/wiki -->
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.13.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Optional plugins for using Spock -->
<!-- Only required if names of spec classes don't match default Surefire patterns (`*Test` etc.) -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<useModulePath>false</useModulePath> <!-- https://issues.apache.org/jira/browse/SUREFIRE-1809 -->
<useFile>false</useFile>
<includes>
<include>**/*Test</include>
<include>**/*Spec</include>
</includes>
<statelessTestsetReporter implementation="org.apache.maven.plugin.surefire.extensions.junit5.JUnit5Xml30StatelessReporter">
<disable>false</disable>
<version>3.0</version>
<usePhrasedFileName>false</usePhrasedFileName>
<usePhrasedTestSuiteClassName>true</usePhrasedTestSuiteClassName>
<usePhrasedTestCaseClassName>true</usePhrasedTestCaseClassName>
<usePhrasedTestCaseMethodName>true</usePhrasedTestCaseMethodName>
</statelessTestsetReporter>
</configuration>
</plugin>

</plugins>
</build>
</project>
2 changes: 1 addition & 1 deletion tests/example-all-fail/expected_results.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 1,
"status": "fail",
"message": "\u001b[01;31m\u001b[KExampleAllFailSpec > Year not divisible by 4 in common year FAILED\u001b[m\u001b[K\n Condition not satisfied:\n\n new ExampleAllFail(year).isLeapYear() == expected\n | | | | |\n | 2015 true | false\n <ExampleAllFail year=2015> false\n at ExampleAllFailSpec.Year not divisible by 4 in common year(ExampleAllFailSpec.groovy:8)\n\n1 test completed, 1 failed\n\n\u001b[01;31m\u001b[KFAILURE: Build failed with an exception.\u001b[m\u001b[K\n\n* What went wrong:\nExecution failed for task ':test'.\n> There were failing tests. "
"message": "\u001b[01;31m\u001b[K[ERROR] Tests run: 3, Failures: 1, Errors: 0, Skipped: 1, <<< FAILURE! - in ExampleAllFailSpec\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExampleAllFailSpec.Year not divisible by 4 in common year(Object,Object) <<< FAILURE!\u001b[m\u001b[K\norg.spockframework.runtime.SpockComparisonFailure: \nCondition not satisfied:\n\nnew ExampleAllFail(year).isLeapYear() == expected\n| | | | |\n| 2015 true | false\n<ExampleAllFail year=2015> false\n\n\tat ExampleAllFailSpec.Year not divisible by 4 in common year(ExampleAllFailSpec.groovy:8)\n\n\u001b[01;31m\u001b[K[ERROR] Failures: \u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExampleAllFailSpec.Year not divisible by 4 in common year(Object,Object)\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] Run 1: ExampleAllFailSpec.Year not divisible by 4 in common year:8 Condition not satisfied:\u001b[m\u001b[K\n\nnew ExampleAllFail(year).isLeapYear() == expected\n| | | | |\n| 2015 true | false\n<ExampleAllFail year=2015> false\n\n\u001b[01;31m\u001b[K[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 1\u001b[m\u001b[K"
}
2 changes: 1 addition & 1 deletion tests/example-empty-file/expected_results.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 1,
"status": "fail",
"message": "\u001b[01;31m\u001b[KExampleEmptyFileSpec > Year not divisible by 4 in common year FAILED\u001b[m\u001b[K\n Condition failed with Exception:\n\n new ExampleEmptyFile(year).isLeapYear() == expected\n | |\n | 2015\n groovy.lang.GroovyRuntimeException: Could not find matching constructor for: ExampleEmptyFile(Integer)\n \tat ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n at ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n\n Caused by:\n groovy.lang.GroovyRuntimeException: Could not find matching constructor for: ExampleEmptyFile(Integer)\n at ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n\n1 test completed, 1 failed\n\n\u001b[01;31m\u001b[KFAILURE: Build failed with an exception.\u001b[m\u001b[K\n\n* What went wrong:\nExecution failed for task ':test'.\n> There were failing tests. "
"message": "\u001b[01;31m\u001b[K[ERROR] Tests run: 3, Failures: 1, Errors: 0, Skipped: 1, <<< FAILURE! - in ExampleEmptyFileSpec\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExampleEmptyFileSpec.Year not divisible by 4 in common year(Object,Object) <<< FAILURE!\u001b[m\u001b[K\norg.spockframework.runtime.ConditionFailedWithExceptionError: \nCondition failed with Exception:\n\nnew ExampleEmptyFile(year).isLeapYear() == expected\n| |\n| 2015\ngroovy.lang.GroovyRuntimeException: Could not find matching constructor for: ExampleEmptyFile(Integer)\n\tat ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n\n\tat ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\nCaused by: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: ExampleEmptyFile(Integer)\n\tat ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n\n\u001b[01;31m\u001b[K[ERROR] Failures: \u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExampleEmptyFileSpec.Year not divisible by 4 in common year(Object,Object)\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] Run 1: ExampleEmptyFileSpec.Year not divisible by 4 in common year:8 Condition failed with Exception:\u001b[m\u001b[K\n\nnew ExampleEmptyFile(year).isLeapYear() == expected\n| |\n| 2015\ngroovy.lang.GroovyRuntimeException: Could not find matching constructor for: ExampleEmptyFile(Integer)\n\tat ExampleEmptyFileSpec.Year not divisible by 4 in common year(ExampleEmptyFileSpec.groovy:8)\n\n\u001b[01;31m\u001b[K[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 1\u001b[m\u001b[K"
}
2 changes: 1 addition & 1 deletion tests/example-partial-fail/expected_results.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 1,
"status": "fail",
"message": "\u001b[01;32m\u001b[KExamplePartialFailSpec > Year not divisible by 4 in common year PASSED\u001b[m\u001b[K\n\n\u001b[01;32m\u001b[KExamplePartialFailSpec > Year divisible by 2, not divisible by 4 in common year PASSED\u001b[m\u001b[K\n\n\u001b[01;32m\u001b[KExamplePartialFailSpec > Year divisible by 4, not divisible by 100 in leap year PASSED\u001b[m\u001b[K\n\n\u001b[01;32m\u001b[KExamplePartialFailSpec > Year divisible by 100, not divisible by 400 in common year PASSED\u001b[m\u001b[K\n\n\u001b[01;32m\u001b[KExamplePartialFailSpec > Year divisible by 100 but not by 3 is still not a leap year PASSED\u001b[m\u001b[K\n\n\u001b[01;31m\u001b[KExamplePartialFailSpec > Year divisible by 400 in leap year FAILED\u001b[m\u001b[K\n Condition not satisfied:\n\n new ExamplePartialFail(year).isLeapYear() == expected\n | | | | |\n | 2000 false | true\n <ExamplePartialFail year=2000> false\n at ExamplePartialFailSpec.Year divisible by 400 in leap year(ExamplePartialFailSpec.groovy:58)\n\n6 tests completed, 1 failed\n\n\u001b[01;31m\u001b[KFAILURE: Build failed with an exception.\u001b[m\u001b[K\n\n* What went wrong:\nExecution failed for task ':test'.\n> There were failing tests. "
"message": "\u001b[01;31m\u001b[K[ERROR] Tests run: 14, Failures: 1, Errors: 0, Skipped: 2, <<< FAILURE! - in ExamplePartialFailSpec\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExamplePartialFailSpec.Year divisible by 400 in leap year(Object,Object) <<< FAILURE!\u001b[m\u001b[K\norg.spockframework.runtime.SpockComparisonFailure: \nCondition not satisfied:\n\nnew ExamplePartialFail(year).isLeapYear() == expected\n| | | | |\n| 2000 false | true\n<ExamplePartialFail year=2000> false\n\n\tat ExamplePartialFailSpec.Year divisible by 400 in leap year(ExamplePartialFailSpec.groovy:58)\n\n\u001b[01;31m\u001b[K[ERROR] Failures: \u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ExamplePartialFailSpec.Year divisible by 400 in leap year(Object,Object)\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] Run 1: ExamplePartialFailSpec.Year divisible by 400 in leap year:58 Condition not satisfied:\u001b[m\u001b[K\n\nnew ExamplePartialFail(year).isLeapYear() == expected\n| | | | |\n| 2000 false | true\n<ExamplePartialFail year=2000> false\n\n\u001b[01;31m\u001b[K[ERROR] Tests run: 13, Failures: 1, Errors: 0, Skipped: 2\u001b[m\u001b[K"
}
2 changes: 1 addition & 1 deletion tests/example-syntax-error/expected_results.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 1,
"status": "fail",
"message": "startup failed:\n/solution/src/main/groovy/ExampleSyntaxError.groovy: 1: Unexpected input: 'ExampleSyntaxError@' @ line 1, column 29.\n classEESD ExampleSyntaxError@@\n ^\n\n1 error\n\n\u001b[01;31m\u001b[KFAILURE: Build failed with an exception.\u001b[m\u001b[K\n\n* What went wrong:\nExecution failed for task ':compileGroovy'.\n> Compilation failed; see the compiler error output for details."
"message": "\u001b[01;31m\u001b[K[ERROR] /solution/src/main/groovy/ExampleSyntaxError.groovy: 1: Unexpected input: '@' @ line 1, column 29.\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] classEESD ExampleSyntaxError@@\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] ^\u001b[m\u001b[K\n\u001b[01;31m\u001b[K[ERROR] 1 error\u001b[m\u001b[K"
}