scala / scala-dev

Scala 2 team issues. Not for user-facing bugs or directly actionable user-facing improvements. For build/test/infra and for longer-term planning and idea tracking. Our bug tracker is at https://github.com/scala/bug/issues
Apache License 2.0
130 stars 15 forks source link

Testing DSL idea: Use string interpolation to mark positions in source code with strong typing. #393

Open retronym opened 7 years ago

retronym commented 7 years ago

I've been toying with an idea to use string interpolation to embed have strongly typed positioned assertions in fragments of source code within JUnit tests. Documenting this here for discussion.

scala>  class TestContext() {
     |     val markers = scala.collection.mutable.Buffer[Pos]()
     |     final class Pos() {
     |       markers += this
     |       var pos: Int = -1
     |       def hasCompletions(alts: String*): this.type = {
     |         // this.assertions += () => this.global.completionsAt(pos).containsAll(alts)
     |         this
     |       }
     |       def check(): Unit = ()
     |     }
     |     def check() = markers.foreach(_.check)
     |     implicit class StringContext_code(private val value: StringContext) {
     |       def code(names: Pos*): String = {
     |         var pos = 0
     |         value.parts.zipWithIndex.foreach { case (part, i) =>
     |           pos += part.length
     |           if (i > 0) names(i - 1).pos = pos
     |         }
     |         value.parts.mkString
     |       }
     |     }
     |   }
defined class TestContext

scala>   def mkContext = new TestContext()
mkContext: TestContext

scala>

scala>   val c = mkContext; import c._
c: TestContext = TestContext@14c99bf6
import c._

scala> val m1, m2, m3 = new Pos
m1: c.Pos = TestContext$Pos@661e1399
m2: c.Pos = TestContext$Pos@3ffd4b12
m3: c.Pos = TestContext$Pos@424f02b8

scala> val code: String =
     |     code"""
     |     class C {
     |       def foo = "".reverse${m1}.reverse$m2.rev${m3.hasCompletions("reverse")}
     |     }
     |     """
code: String =
"
    class C {
      def foo = "".reverse.reverse.rev
    }
    "

scala>   m2.hasCompletions("charAt")
res0: m2.type = TestContext$Pos@3ffd4b12

scala>   c.markers.foreach(m => println(m.pos))
49
53
64

scala>   c.check()

This is an alternative to the "magic comment markers" used in the presentation compiler tests, and is more general than the approach taken in https://github.com/scala/scala/blob/2.12.x/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala#L31

Different test use cases could add extension methods to Pos to work with different fragments of testing DSL.

retronym commented 7 years ago

/cc

@fommil (author of https://github.com/ensime/pcplod) @alefas / @pavelfatin who've written a variety of test DSL for IntellIJs typechecker @smarter @DarkDimius for a perspective from dotty

dwijnand commented 3 years ago

Pretty cool, IMO.