Skip to content

Commit

Permalink
Compile exercises directly with scalac instead of sbt (#31)
Browse files Browse the repository at this point in the history
Compile exercises directly with scalac instead of sbt

Co-authored-by: Erik Schierboom <[email protected]>
  • Loading branch information
jcranky and ErikSchierboom authored May 12, 2022
1 parent b5a725c commit 06428d8
Show file tree
Hide file tree
Showing 33 changed files with 211 additions and 97 deletions.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
.bsp/
.idea/
.vscode/
.metals/
.bloop/
dumps/
target/
tests/**/results.json
tests/**/build.log
tests/**/runner.log
tests/**/*.original
tests/**/*.original
project/metals.sbt
project/project
8 changes: 7 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
FROM mozilla/sbt:8u232_1.4.5
FROM mozilla/sbt:8u292_1.5.7

WORKDIR /opt/test-runner

RUN wget https://downloads.lightbend.com/scala/2.13.8/scala-2.13.8.tgz
RUN tar zxvf scala-2.13.8.tgz

ENV PATH="$PATH:/opt/test-runner/scala-2.13.8/bin"

COPY project/ project/
COPY src/ src/
COPY build.sbt build.sbt

RUN sbt assembly

COPY . .
Expand Down
2 changes: 0 additions & 2 deletions bin/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

exit_code=0

sbt test

# Iterate over all test directories
for test_dir in tests/*; do
test_dir_name=$(basename "${test_dir}")
Expand Down
39 changes: 24 additions & 15 deletions bin/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,45 @@ fi
slug="$1"
input_dir="${2%/}"
output_dir="${3%/}"

exercise=$(echo "${slug}" | sed -E 's/(^|-)([a-z])/\U\2/g')
tests_file="${input_dir}/src/test/scala/${exercise}Test.scala"
tests_results_file="${input_dir}/target/test-reports/TEST-${exercise}Test.xml"
original_tests_file="${input_dir}/src/test/scala/${exercise}Test.scala.original"

test_runner_jar=/opt/test-runner/target/scala-2.13/TestRunner-assembly-0.1.0-SNAPSHOT.jar

workdir=/tmp/exercise
workdir_target=/tmp/exercise/target

results_file="${output_dir}/results.json"
build_log_file="${output_dir}/build.log"
runner_log_file="${output_dir}/runner.log"
tests_results_file="${workdir}/test-reports/TEST-${exercise}Test.xml"

# Create the output directory if it doesn't exist
mkdir -p "${output_dir}"

echo "${slug}: testing..."
# ensure a clean workdir
rm -rf "${workdir}"
mkdir -p "${workdir}"
mkdir -p "${workdir_target}"

pushd "${input_dir}" > /dev/null
echo
echo "${slug}: testing..."

cp "${tests_file}" "${original_tests_file}"
cp -R "${input_dir}"/src "${workdir}"

# Enable all pending tests
sed -i 's/pending//g' "${tests_file}"

sbt "set offline := true" test > "${build_log_file}"
sed -i 's/pending//g' "${workdir}"/src/test/scala/*

# Sanitize the output
sed -i -E '/^\[info\]/d' "${build_log_file}"
# compile source and tests
scalac -classpath "${test_runner_jar}" -d "${workdir_target}" "${workdir}"/src/main/scala/* "${workdir}"/src/test/scala/* &> "${build_log_file}"

mv -f "${original_tests_file}" "${tests_file}"
# run tests
scala -classpath "${test_runner_jar}" org.scalatest.tools.Runner -R "${workdir_target}" -u "${workdir}"/test-reports

popd > /dev/null
# Write the results.json file in the exercism format
java -jar "${test_runner_jar}" "${build_log_file}" "${tests_results_file}" "${results_file}" &> "${runner_log_file}"

# Write the results.json file
java -jar target/scala-2.12/TestRunner-assembly-0.1.0-SNAPSHOT.jar "${build_log_file}" "${tests_results_file}" "${results_file}" > "${runner_log_file}"
# change workdir back to the original input_dir in the final results file
sed -i "s~${workdir}~${input_dir}~g" "${results_file}"

echo "${slug}: done"
7 changes: 4 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Dependencies._

ThisBuild / scalaVersion := "2.12.8"
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "org.exercism"
ThisBuild / organizationName := "exercism"

lazy val root = (project in file("."))
.settings(
name := "TestRunner",
libraryDependencies += scalaTest % Test,
libraryDependencies += "org.json" % "json" % "20201115"
// not in the Test scope so that it gets added to the fat jar generated by sbt assembly
libraryDependencies += scalaTest,
libraryDependencies += "org.json" % "json" % "20220320"
)
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sbt._

object Dependencies {
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.1"
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.10"
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.4.5
sbt.version=1.5.7
5 changes: 2 additions & 3 deletions src/main/scala/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
val fileSource = Source.fromFile(buildLogFilePath)
val rawContent = fileSource.mkString
fileSource.close
if (rawContent.contains("[error] (Compile / compileIncremental) Compilation failed") ||
rawContent.contains("[error] (Test / compileIncremental) Compilation failed")) rawContent else ""
if (rawContent.contains("error: ")) rawContent else ""
}

def toTestCaseJSON(testCase: JSONObject): JSONObject = {
Expand All @@ -53,7 +52,7 @@
def toExercismJSON(buildLogFilePath: String, testResultsFilePath: String): JSONObject = {
val baseObject = new JSONObject().put("version", 2)
val errorMessage = findErrorsInLog(buildLogFilePath)
if(!errorMessage.isEmpty) {
if(errorMessage.nonEmpty) {
baseObject
.put("status", "error")
.put("message", errorMessage)
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/GradeSchoolTest.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import org.scalatest._

/** @version created manually **/
class GradeSchoolTest extends FunSuite with Matchers with OneInstancePerTest {
class GradeSchoolTest extends AnyFunSuite with Matchers with OneInstancePerTest {
val school = new School

test ("empty school") {
Expand Down
28 changes: 13 additions & 15 deletions src/test/resources/outputs/output_empty.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
[error] /opt/test-runner/tests/example-empty-file/src/test/scala/ExampleEmptyFileTest.scala:7:5: not found: value Leap
[error] Leap.leapYear(2015) should be (false)
[error] ^
[error] /opt/test-runner/tests/example-empty-file/src/test/scala/ExampleEmptyFileTest.scala:12:5: not found: value Leap
[error] Leap.leapYear(1996) should be (true)
[error] ^
[error] /opt/test-runner/tests/example-empty-file/src/test/scala/ExampleEmptyFileTest.scala:17:5: not found: value Leap
[error] Leap.leapYear(2100) should be (false)
[error] ^
[error] /opt/test-runner/tests/example-empty-file/src/test/scala/ExampleEmptyFileTest.scala:22:5: not found: value Leap
[error] Leap.leapYear(2000) should be (true)
[error] ^
[error] four errors found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 4 s, completed Apr 14, 2021 2:11:23 PM
/tmp/exercise/src/test/scala/ExampleEmptyFileTest.scala:7: error: not found: value Leap
Leap.leapYear(2015) should be (false)
^
/tmp/exercise/src/test/scala/ExampleEmptyFileTest.scala:12: error: not found: value Leap
Leap.leapYear(1996) should be (true)
^
/tmp/exercise/src/test/scala/ExampleEmptyFileTest.scala:17: error: not found: value Leap
Leap.leapYear(2100) should be (false)
^
/tmp/exercise/src/test/scala/ExampleEmptyFileTest.scala:22: error: not found: value Leap
Leap.leapYear(2000) should be (true)
^
4 errors
74 changes: 49 additions & 25 deletions src/test/resources/outputs/output_error.txt
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
[info] Loading project definition from /Users/pro/Exercism/scala/grade-school/project
[info] Loading settings for project grade-school from build.sbt ...
[info] Set current project to grade-school (in build file:/Users/pro/Exercism/scala/grade-school/)
[info] Compiling 1 Scala source to /Users/pro/Exercism/scala/grade-school/target/scala-2.13/test-classes ...
[error] /Users/pro/Exercism/scala/grade-school/src/test/scala/GradeSchoolTest.scala:8:15: overloaded method should with alternatives:
[error] (endWithWord: org.scalatest.words.EndWithWord)(implicit ev: GradeSchoolTest.this.school.DB <:< String): GradeSchoolTest.this.ResultOfEndWithWordForString <and>
[error] (startWithWord: org.scalatest.words.StartWithWord)(implicit ev: GradeSchoolTest.this.school.DB <:< String): GradeSchoolTest.this.ResultOfStartWithWordForString <and>
[error] (includeWord: org.scalatest.words.IncludeWord)(implicit ev: GradeSchoolTest.this.school.DB <:< String): GradeSchoolTest.this.ResultOfIncludeWordForString <and>
[error] (notExist: org.scalatest.words.ResultOfNotExist)(implicit existence: org.scalatest.enablers.Existence[GradeSchoolTest.this.school.DB]): org.scalatest.Assertion <and>
[error] (existWord: org.scalatest.words.ExistWord)(implicit existence: org.scalatest.enablers.Existence[GradeSchoolTest.this.school.DB]): org.scalatest.Assertion <and>
[error] (containWord: org.scalatest.words.ContainWord)org.scalatest.words.ResultOfContainWord[GradeSchoolTest.this.school.DB] <and>
[error] (haveWord: org.scalatest.words.HaveWord)GradeSchoolTest.this.ResultOfHaveWordForExtent[GradeSchoolTest.this.school.DB] <and>
[error] (beWord: org.scalatest.words.BeWord)GradeSchoolTest.this.ResultOfBeWordForAny[GradeSchoolTest.this.school.DB] <and>
[error] (inv: org.scalactic.TripleEqualsSupport.TripleEqualsInvocationOnSpread[GradeSchoolTest.this.school.DB])(implicit ev: Numeric[GradeSchoolTest.this.school.DB]): org.scalatest.Assertion <and>
[error] [U](inv: org.scalactic.TripleEqualsSupport.TripleEqualsInvocation[U])(implicit constraint: org.scalactic.CanEqual[GradeSchoolTest.this.school.DB,U]): org.scalatest.Assertion <and>
[error] (notWord: org.scalatest.words.NotWord)org.scalatest.words.ResultOfNotWordForAny[GradeSchoolTest.this.school.DB] <and>
[error] [TYPECLASS1[_], TYPECLASS2[_]](rightMatcherFactory2: org.scalatest.matchers.MatcherFactory2[GradeSchoolTest.this.school.DB,TYPECLASS1,TYPECLASS2])(implicit typeClass1: TYPECLASS1[GradeSchoolTest.this.school.DB], typeClass2: TYPECLASS2[GradeSchoolTest.this.school.DB]): org.scalatest.Assertion <and>
[error] [TYPECLASS1[_]](rightMatcherFactory1: org.scalatest.matchers.MatcherFactory1[GradeSchoolTest.this.school.DB,TYPECLASS1])(implicit typeClass1: TYPECLASS1[GradeSchoolTest.this.school.DB]): org.scalatest.Assertion <and>
[error] (rightMatcherX1: org.scalatest.matchers.Matcher[GradeSchoolTest.this.school.DB])org.scalatest.Assertion
[error] cannot be applied to (scala.collection.immutable.Map[Nothing,Nothing])
[error] school.db should (Map())
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 2 s, completed Feb 21, 2021, 2:37:03 PM
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:8: error: value should is not a member of Map[String,Int]
school.db should be (Map())
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:8: error: not found: value be
school.db should be (Map())
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:14: error: value should is not a member of Map[String,Int]
school.db should be (Map(2 -> Seq("Aimee")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:14: error: not found: value be
school.db should be (Map(2 -> Seq("Aimee")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:22: error: value should is not a member of Map[String,Int]
school.db should be (Map(2 -> Seq("James", "Blair", "Paul")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:22: error: not found: value be
school.db should be (Map(2 -> Seq("James", "Blair", "Paul")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:29: error: value should is not a member of Map[String,Int]
school.db should be (Map(3 -> Seq("Chelsea"), 7 -> Seq("Logan")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:29: error: not found: value be
school.db should be (Map(3 -> Seq("Chelsea"), 7 -> Seq("Logan")))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:37: error: value should is not a member of Seq[String]
school.grade(5) should be (Seq("Franklin", "Bradley"))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:37: error: not found: value be
school.grade(5) should be (Seq("Franklin", "Bradley"))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:42: error: value should is not a member of Seq[String]
school.grade(1) should be (Seq())
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:42: error: not found: value be
school.grade(1) should be (Seq())
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:56: error: value should is not a member of Map[Int,Seq[String]]
school.sorted should be (sorted)
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:56: error: not found: value be
school.sorted should be (sorted)
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:57: error: value should is not a member of List[Int]
school.sorted.keys.toList should be (Seq(3, 4, 6))
^
/tmp/exercise/src/test/scala/GradeSchoolTest.scala:57: error: not found: value be
school.sorted.keys.toList should be (Seq(3, 4, 6))
^
16 errors
5 changes: 3 additions & 2 deletions src/test/scala/ApplicationSpec.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.json.JSONObject
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class ApplicationSpec extends FunSuite with Matchers {
class ApplicationSpec extends AnyFunSuite with Matchers {

test("A successful xml should pass simply") {
val xmlTestURL = getClass.getResource("/GradeSchool_successful.xml").getPath
Expand Down
4 changes: 2 additions & 2 deletions tests/example-all-fail/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
1 change: 0 additions & 1 deletion tests/example-all-fail/project/build.properties

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

/** @version 1.3.0 */
class ExampleAllFailTest extends FunSuite with Matchers {
class ExampleAllFailTest extends AnyFunSuite with Matchers {

test("year not divisible by 4: common year") {
Leap.leapYear(2015) should be (false)
Expand Down
4 changes: 2 additions & 2 deletions tests/example-empty-file/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
2 changes: 1 addition & 1 deletion tests/example-empty-file/expected_results.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"message":"[error] /solution/src/test/scala/ExampleEmptyFileTest.scala:7:5: not found: value Leap\n[error] Leap.leapYear(2015) should be (false)\n[error] ^\n[error] /solution/src/test/scala/ExampleEmptyFileTest.scala:12:5: not found: value Leap\n[error] Leap.leapYear(1996) should be (true)\n[error] ^\n[error] /solution/src/test/scala/ExampleEmptyFileTest.scala:17:5: not found: value Leap\n[error] Leap.leapYear(2100) should be (false)\n[error] ^\n[error] /solution/src/test/scala/ExampleEmptyFileTest.scala:22:5: not found: value Leap\n[error] Leap.leapYear(2000) should be (true)\n[error] ^\n[error] four errors found\n[error] (Test / compileIncremental) Compilation failed\n[error] \n","version":2,"status":"error"}
{"message":"/solution/src/test/scala/ExampleEmptyFileTest.scala:8: error: not found: value Leap\n Leap.leapYear(2015) should be (false)\n ^\n/solution/src/test/scala/ExampleEmptyFileTest.scala:13: error: not found: value Leap\n Leap.leapYear(1996) should be (true)\n ^\n/solution/src/test/scala/ExampleEmptyFileTest.scala:18: error: not found: value Leap\n Leap.leapYear(2100) should be (false)\n ^\n/solution/src/test/scala/ExampleEmptyFileTest.scala:23: error: not found: value Leap\n Leap.leapYear(2000) should be (true)\n ^\n4 errors\n","version":2,"status":"error"}
1 change: 0 additions & 1 deletion tests/example-empty-file/project/build.properties

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

/** @version 1.3.0 */
class ExampleEmptyFileTest extends FunSuite with Matchers {
class ExampleEmptyFileTest extends AnyFunSuite with Matchers {

test("year not divisible by 4: common year") {
Leap.leapYear(2015) should be (false)
Expand Down
4 changes: 2 additions & 2 deletions tests/example-partial-fail/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
1 change: 0 additions & 1 deletion tests/example-partial-fail/project/build.properties

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

/** @version 1.3.0 */
class ExamplePartialFailTest extends FunSuite with Matchers {
class ExamplePartialFailTest extends AnyFunSuite with Matchers {

test("year not divisible by 4: common year") {
Leap.leapYear(2015) should be (false)
Expand Down
4 changes: 2 additions & 2 deletions tests/example-success/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
1 change: 0 additions & 1 deletion tests/example-success/project/build.properties

This file was deleted.

5 changes: 3 additions & 2 deletions tests/example-success/src/test/scala/ExampleSuccessTest.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

/** @version 1.3.0 */
class ExampleSuccessTest extends FunSuite with Matchers {
class ExampleSuccessTest extends AnyFunSuite with Matchers {

test("year not divisible by 4: common year") {
Leap.leapYear(2015) should be (false)
Expand Down
4 changes: 2 additions & 2 deletions tests/example-syntax-error/build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
scalaVersion := "2.12.8"
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
2 changes: 1 addition & 1 deletion tests/example-syntax-error/expected_results.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"message":"[error] /solution/src/main/scala/ExampleSyntaxError.scala:1:1: expected class or object definition\n[error] objeTTTWE @#Leap\n[error] ^\n[error] one error found\n[error] (Compile / compileIncremental) Compilation failed\n[error] \n","version":2,"status":"error"}
{"message":"/solution/src/main/scala/ExampleSyntaxError.scala:1: error: expected class or object definition\nobjeTTTWE @#Leap\n^\n1 error\n","version":2,"status":"error"}
1 change: 0 additions & 1 deletion tests/example-syntax-error/project/build.properties

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import org.scalatest.{Matchers, FunSuite}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

/** @version 1.3.0 */
class ExampleSyntaxErrorTest extends FunSuite with Matchers {
class ExampleSyntaxErrorTest extends AnyFunSuite with Matchers {

test("year not divisible by 4: common year") {
Leap.leapYear(2015) should be (false)
Expand Down
3 changes: 3 additions & 0 deletions tests/grade-school-error/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
scalaVersion := "2.13.6"

libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.10" % "test"
Loading

0 comments on commit 06428d8

Please sign in to comment.