Skip to content

Commit 8a51b89

Browse files
author
Alejandro Gómez
committed
Evaluate code with the configured timeout
Solved #540
1 parent a9917a8 commit 8a51b89

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

src/main/scala/org/scalaexercises/exercises/Evaluator.scala

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@ import java.util.concurrent.{ TimeoutException, Callable, FutureTask, TimeUnit }
2121

2222
import scala.util.Try
2323
import scala.util.control.NonFatal
24+
import scala.concurrent._
2425
import scala.concurrent.duration._
26+
import scala.concurrent.ExecutionContext
2527
import scala.language.reflectiveCalls
28+
2629
import com.twitter.util.Eval
2730

31+
import monix.execution._
32+
2833
import scala.reflect.internal.util.{ BatchSourceFile, Position }
2934

3035
sealed trait Severity
@@ -43,12 +48,14 @@ object EvalResult {
4348

4449
case object Timeout extends EvalResult[Nothing]
4550
case class Success[T](complilationInfos: CI, result: T, consoleOutput: String) extends EvalResult[T]
51+
case class Timeout[T]() extends EvalResult[T]
4652
case class EvalRuntimeError(complilationInfos: CI, runtimeError: Option[RuntimeError]) extends EvalResult[Nothing]
4753
case class CompilationError(complilationInfos: CI) extends EvalResult[Nothing]
4854
case class GeneralError(stack: Throwable) extends EvalResult[Nothing]
4955
}
5056

51-
class Evaluator(timeout: Duration = 20.seconds) {
57+
class Evaluator(timeout: FiniteDuration = 20.seconds) {
58+
implicit val scheduler: ExecutionContext = Scheduler.io("evaluation-scheduler")
5259

5360
def convert(errors: (Position, String, String)): (Severity, List[CompilationInfo]) = {
5461
val (pos, msg, severity) = errors
@@ -60,11 +67,7 @@ class Evaluator(timeout: Duration = 20.seconds) {
6067
(sev, CompilationInfo(msg, Some(RangePosition(pos.start, pos.point, pos.end))) :: Nil)
6168
}
6269

63-
def apply[T](pre: String, code: String): EvalResult[T] = {
64-
val allCode = s"""
65-
|$pre
66-
|$code
67-
""".stripMargin
70+
def eval[T](code: String): EvalResult[T] = {
6871
val eval = new Eval {
6972
@volatile var errors: Map[Severity, List[CompilationInfo]] = Map.empty
7073

@@ -82,8 +85,8 @@ class Evaluator(timeout: Duration = 20.seconds) {
8285
}
8386

8487
val result = for {
85-
_ Try(eval.check(allCode))
86-
result Try(eval.apply[T](allCode, resetState = true))
88+
_ Try(eval.check(code))
89+
result Try(eval.apply[T](code, resetState = true))
8790
} yield result
8891

8992
val errors: Map[Severity, List[CompilationInfo]] = eval.errors.toMap
@@ -97,4 +100,15 @@ class Evaluator(timeout: Duration = 20.seconds) {
97100
}
98101
}
99102
}
103+
104+
def apply[T](pre: String, code: String): EvalResult[T] = {
105+
val allCode = s"""
106+
|$pre
107+
|$code
108+
""".stripMargin
109+
val fut = Future({ eval(allCode) })
110+
Try(
111+
Await.result(fut, timeout)
112+
).getOrElse(EvalResult.Timeout())
113+
}
100114
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* scala-exercises-runtime
3+
* Copyright (C) 2015-2016 47 Degrees, LLC. <http://www.47deg.com>
4+
*/
5+
6+
package org.scalaexercises.runtime
7+
8+
import scala.concurrent.duration._
9+
import org.scalatest._
10+
11+
class EvaluatorSpec extends FunSpec with Matchers {
12+
13+
describe("evaluation") {
14+
it("fails with a timeout when takes longer than the configured timeout") {
15+
val evaluator = new Evaluator(1 second)
16+
val result: EvalResult[Int] = evaluator("", "{ while(true) {}; 123 }")
17+
result should matchPattern {
18+
case t: EvalResult.Timeout[Int]
19+
}
20+
}
21+
}
22+
}

0 commit comments

Comments
 (0)