All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.pawelkrol.CommTest.FunSpec.scala Maven / Gradle / Ivy

There is a newer version: 0.05
Show newest version
package com.github.pawelkrol.CommTest

import com.github.pawelkrol.CPU6502.{ OpCode, OpCode_JMP_ABS, OpCode_JSR_ABS, OpCode_RTS }

import java.util.NoSuchElementException

import org.scalactic.source.Position
import org.scalatest.Tag

import scala.collection.mutable.HashMap
import scala.language.existentials

trait FunSpec extends ExtendedCPU6502Spec {

  private var _outputPrg: String = _

  protected def outputPrg: String = _outputPrg

  protected def outputPrg_=(fileName: String) {
    _outputPrg = fileName
    _programData = ProgramData(fileName)
  }

  private var _programData: ProgramData = _

  private val beforeFilter = NestedStack[NestedStack[() => Unit]](NestedStack[() => Unit]())

  protected def before(procedure: => Unit) {
    beforeFilter.peek.push(() => procedure)
  }

  private val descriptionBuffer = NestedStack[String]()

  private var describedSubroutine: Option[String] = _

  override protected def describe(description: String)(fun: => Unit)(implicit pos: Position): Unit = {
    beforeFilter.push(NestedStack())
    descriptionBuffer.push(description)
    examples.push(HashMap())
    lets.push(HashMap())
    super.describe(description)(fun)(pos)
    beforeFilter.pop
    descriptionBuffer.pop
    examples.pop
    lets.pop
  }

  protected def context(description: String)(fun: => Unit)(implicit pos: Position): Unit = {
    describe(description)(fun)(pos)
  }

  private val ignoreTests = NestedStack[Boolean]()

  protected def xdescribe(description: String)(fun: => Unit)(implicit pos: Position): Unit = {
    ignoreTests.push(true)
    describe(description)(fun)(pos)
    ignoreTests.pop
  }

  private def test(
    before: List[() => Unit],
    description: Option[String],
    letValues: List[HashMap[String, Tuple2[Class[_], Any]]],
    testFun: => Any /* Assertion */
  ): () => Any = () => {
    describedSubroutine = description
    memoisedLets = letValues
    initCore(_programData)
    customHandler = NestedStack()
    before.foreach(filter => {
      customHandler.push(Map())
      filter()
    })
    memoisedMocks = customHandler.all.reverse.flatten.toMap
    testFun
  }

  protected class NestedItWord extends ItWord {
    override def apply(specText: String, testTags: Tag*)(testFun: => Any /* Assertion */)(implicit pos: Position): Unit = {
      if (ignoreTests.any)
        xit(specText, testTags: _*)(testFun)(pos)
      else {
        val before = beforeFilter.all.reverse.map(_.all.reverse).flatten
        val description = descriptionBuffer.closestMatch((item) => labelLog.contains(item))
        val letValues = lets.all
        super.apply(specText, testTags: _*)(test(before, description, letValues, testFun)())(pos)
      }
    }
  }

  override protected val it = new NestedItWord

  protected def xit(specText: String, testTags: Tag*)(testFun: => Any /* Assertion */)(implicit pos: Position): Unit = {
    ignore(specText, testTags: _*)(testFun)(pos)
  }

  private def emulateOpJSR(address: Short) {
    SP -= 2
    PC = address
  }

  private def hasSubroutineMock(opCode: OpCode, subroutineMocks: Map[String, () => Unit]): Option[(OpCode, () => Unit)] = {
    val targetAddress = memory.get_val_from_addr((register.PC + 1).toShort)
    labelLog.label(targetAddress) match {
      case Some(name) =>
        subroutineMocks.get(name) match {
          case Some(callback) =>
            Some(opCode, callback)
          case None =>
            None
        }
      case None =>
        None
    }
  }

  private def hasSubroutineMock(subroutineMocks: Map[String, () => Unit]): Option[(OpCode, () => Unit)] = {
    OpCode(memory.read(register.PC), core) match {
      case OpCode_JMP_ABS =>
        hasSubroutineMock(OpCode_JMP_ABS, subroutineMocks)
      case OpCode_JSR_ABS =>
        hasSubroutineMock(OpCode_JSR_ABS, subroutineMocks)
      case _ =>
        None
    }
  }

  private def executeMock(opCode: OpCode, callback: () => Unit) {
    opCode match {
      case OpCode_JMP_ABS =>
        callback()
        core.eval(OpCode_RTS)
      case OpCode_JSR_ABS =>
        callback()
        register.advancePC(opCode.memSize)
      case _ =>
        throw new RuntimeException("Unexpected opcode while attempting to execute a mocked subroutine call: " + opCode)
    }
  }

  private def callSubroutine(address: Short) {
    val stackPointer = SP
    emulateOpJSR(address)
    while (SP != stackPointer) {
      hasSubroutineMock(memoisedMocks) match {
        case Some((opCode, callback)) =>
          executeMock(opCode, callback)
        case None =>
          core.executeInstruction
      }
    }
  }

  protected def call(name: String) {
    if (describedSubroutine === null)
      throw new UnsupportedOperationException("Subroutine call not allowed outside of an example scope")

    val subroutine = label2address(name)

    callSubroutine(subroutine)
  }

  protected def call {
    call(subroutineName)
  }

  private def subroutineName =
    describedSubroutine match {
      case Some(name) =>
        name
      case None =>
        throw new UnsupportedOperationException("No subroutine to call specified")
    }

  private var examples: NestedStack[HashMap[String, () => Unit]] = NestedStack(HashMap())

  protected def includeExamples(name: String) {
    examples.all.foldLeft[Option[() => Unit]](None)((result, scopedExamples) => {
      result match {
        case Some(_) =>
          result
        case None => {
          scopedExamples.get(name) match {
            case Some(procedure) =>
              Some(procedure)
            case None =>
              result
          }
        }
      }
    }) match {
      case Some(procedure) =>
        describe(name) {
          procedure()
        }
      case None =>
        throw new NoSuchElementException("Undefined shared examples name '" + name + "'")
    }
  }

  protected def sharedExamples(name: String)(procedure: => Unit) {
    examples.peek.put(name, () => procedure)
  }

  private val lets: NestedStack[HashMap[String, Tuple2[Class[_], Any]]] = NestedStack(HashMap())

  private var memoisedLets: List[HashMap[String, Tuple2[Class[_], Any]]] = _

  protected def let(name: String)(value: => Any) {
    lets.peek.put(name, (value.getClass, value))
  }

  protected def get(name: String) = {
    val (className, assignmentValue) = memoisedLets.find((scopedLets) => scopedLets.contains(name)) match {
      case Some(value) =>
        value(name)
      case None =>
        throw new NoSuchElementException("Undefined variable '" + name + "' (did you forget to say 'let(\"" + name + "\") { ... }'?)")
    }

    className.cast(assignmentValue)
  }

  def expect[T](code: => T) = Expectation(() => code)

  private var memoisedMocks: Map[String, () => Unit] = _

  private var customHandler: NestedStack[Map[String, () => Unit]] = NestedStack()

  def set_custom_handler(name: String)(callback: => Unit) {
    customHandler.any match {
      case true =>
        customHandler.push(customHandler.pop.updated(name, () => callback))
      case false =>
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy