aboutsummaryrefslogblamecommitdiff
path: root/compiler/test/debug/Gen.scala
blob: dee6b33e3c12a9321421b2e4c0dcb1537a40402c (plain) (tree)




















                                                                                              
                     




















































































































































                                                                                                                
import scala.io.Source
import scala.collection.mutable.ListBuffer

/** Automate testing debuggability of generated code using JDB and expect
 *
 *  The debugging information is annotated as comments to the code in brackets:
 *
 *  val x = f(3) // [break] [next: line=5]
 *  val y = 5
 *
 *  1. A jdb command must be wrapped in brackets, like `[step]`. All jdb commands can be used.
 *  2. To check output of jdb for a command, use `[cmd: expect]`.
 *  3. If `expect` is wrapped in double quotes, regex is supported.
 *  4. Break commands are collected and set globally.
 *  5. Other commands will be send to jdb in the order they appear in the source file
 *
 *  Note: jdb uses line number starts from 1
 */

object Gen {
  val MainObject = "Test"
  val CommandWait = 1

  sealed trait Tree

  case class Break(line: Int) extends Tree

  case class Command(val name: String, val expect: Expect = EmptyExpect) extends Tree

  sealed trait Expect

  case object EmptyExpect extends Expect

  case class LitExpect(lit: String) extends Expect

  case class PatExpect(pat: String) extends Expect

  case class Program(breaks: Seq[Break], commands: Seq[Command])

  def error(msg: String): Nothing = {
    throw new Exception(msg)
  }

  def parseCommand(command: String, lineNo: Int): Tree = {
    val index = command.indexOf(':')
    if (index == -1) {
      // simple command
      if (command == "break") Break(lineNo)
      else Command(command)
    } else {
      val Seq(cmd, rhs) = command.split(":", 2).toSeq.map(_.trim)
      if (rhs.startsWith("\"")) {
        // regex match
        val content = "\"(.+)\"".r
        rhs match {
          case content(expect) => Command(cmd, PatExpect(expect))
          case _ => error(s"""incorrect specification: `$rhs` for `$cmd` at line $lineNo. Ending " expected.""")
        }
      } else {
        // literal match
        Command(cmd, LitExpect(rhs))
      }
    }
  }

  def parse(file: String): Program = {
    val lines = Source.fromFile(file).getLines.toBuffer

    val breaks = new ListBuffer[Break]()
    val cmds = new ListBuffer[Command]()
    lines.zipWithIndex.map { case (code, line) =>
      val comment = if (code.indexOf("//") != -1) code.split("//").last else ""
      val regex = """(?<=\[).*?(?=\])""".r
      for (p <- regex findAllIn comment) parseCommand(p.trim, line + 1) match { // jdb index from 0
        case b: Break   => breaks += b
        case c: Command => cmds += c
      }
    }

    Program(breaks, cmds)
  }

  def generate(program: Program, source: String = "tests/debug/"): String = {
    val Program(breaks, cmds) = program
    val breakpoints = (breaks.map {
      case Break(point) =>
          s"""|send "stop at $MainObject$$:$point\\r"
              |sleep $CommandWait
              |expect "breakpoint $MainObject$$:$point"
              |expect -re $$
              """.stripMargin
    }).mkString("\n\n")

    val commands = (cmds.map {
    case Command(cmd, EmptyExpect)      =>
        s"""|# send_user "send command `$cmd`\\n"
            |send "$cmd\\r"
            |sleep $CommandWait
            |expect -re $$
            """.stripMargin
    case Command(cmd, LitExpect(lit))   =>
        s"""|# send_user "send command `$cmd`\\n"
            |send "$cmd\\r"
            |sleep $CommandWait
            |expect {
            |   "*$lit*" { send_user "success - $cmd : $lit \\n" }
            |   timeout {
            |       send_user "timeout while waiting for response: $cmd : $lit\\n"
            |       exit 1
            |    }
            |}
            |expect -re $$
            |""".stripMargin
    case Command(cmd, PatExpect(pat))   =>
        s"""|# send_user "send command `$cmd`\\n"
            |send "$cmd\\r"
            |sleep $CommandWait
            |expect {
            |   -re {$pat} { send_user "success - $cmd : $pat \\n" }
            |   timeout {
            |       send_user "timeout while waiting for response: $cmd : $pat\\n"
            |       exit 1
            |    }
            |}
            |expect -re $$
            |""".stripMargin
    }).mkString("\n\n")

s"""|#!/usr/bin/expect
    |
    |# log_user 1
    |# exp_internal 1
    |# set timeout 5
    |
    |send_user "spawning job...\\n"
    |
    |spawn jdb -attach 5005 -sourcepath $source
    |
    |send_user "interacting...\\n"
    |
    |expect {
    |  "*VM Started*" { send_user "success - connected to server \\n" }
    |  timeout {
    |      send_user "timeout while waiting for: *VM Started*\\n"
    |      exit 1
    |  }
    |}
    |
    |send_user "setting breakpoints...\\n"
    |
    |# breakpoints
    |$breakpoints
    |
    |# run
    |send_user "run program...\\n"
    |send "run\\r"
    |expect "Breakpoint hit"
    |
    |# interactions
    |$commands""".stripMargin
  }

  def main(args: Array[String]): Unit = {
    val prog = Gen.parse(args(0))
    // println("--------------------------------")
    // println("prog:" + prog)
    // println("\n\n\n scrip:")
    // println("--------------------------------")
    println(Gen.generate(prog))
  }
}