How can I test a scala function? - scala

I started learning scala and in order to learn, I want to play with some functions. However, I dont know how to make a functions return value appear on the console. I am using sbt.
I tried with return and Console.println, I guess I dont use it right.
for example:
def func(ls: List[Boolean]): Boolean = ls match
{
case Nil => false
case l::ls => l != func(ls)
}
how do I see what this function returns?

Scastie is an online interactive playground for Scala and is a quick way to get started. For example, pasting the following in the editor and pressing Save button
def func(ls: List[Boolean]): Boolean = ls match {
case Nil => false
case l::ls => l != func(ls)
}
func(List(true, false, true))
should evaluate func and show the result inline like so
func(List(true, false, true)) // false: Boolean
Also try println(func(List(true, false, true)))
Another way of testing the expected result without having to print it is to use assertions like so
assert(func(List(true, false, true)) == false)
To convert above assertion to a real unit test we could instantiate an application from Scala Giter8 template like so
sbt new scala/scala-seed.g8
which setups all the furniture necessary to quickly run and test applications. Then add func to src/main/scala/example/Hello.scala like so
object Hello extends App {
def func(ls: List[Boolean]): Boolean = ls match {
case Nil => false
case l::ls => l != func(ls)
}
}
and add corresponding unit tests to src/test/scala/example/HelloSpec.scala like so
class HelloSpec extends FlatSpec with Matchers {
"func" should "return false on List(true, false, true)" in {
Hello.func(List(true, false, true)) shouldEqual false
}
it should "return false on empty list" in {
Hello.func(List()) shouldEqual false
}
// add further tests here
}
Now executing sbt test should output
[info] HelloSpec:
[info] func
[info] - should return false on List(true, false, true)
[info] - should return false on empty list
[info] Run completed in 127 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.

You can try this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def func(ls: List[Boolean]): Boolean = ls match
{
case Nil => false
case l::ls => l != func(ls)
}
// Exiting paste mode, now interpreting.
func: (ls: List[Boolean])Boolean
scala> func(List(true, false, true))
res0: Boolean = false

Related

How to generate an [error] instead of an [info] upon seeing a wrong value in expect()

Consider the following code:
import chisel3._
import chisel3.util._
import chisel3.iotesters._
class Inverter extends Module {
val io = IO(new Bundle {
val a = Input(UInt(4.W))
val s = Output(UInt(4.W))
})
io.s := ~io.a
}
class InverterTester(c: Inverter) extends PeekPokeTester(c) {
poke(c.io.a, 8)
step(1)
expect(c.io.s, 8) // Should be 7 here
}
object TestMain extends App {
chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
}
Now I run sbt 'test:runMain TestMain' and got this line (info is in purple):
[info] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
And the exit value of sbt is zero.
I need that line to be an error (with red color):
[error] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
As well as making the above sbt command exit with a non-zero value.
How can I achieve it with minimal change to existing code?
First the easy part. You can get a non-zero result code by using the result of chisel.execute like this.
val result = chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
System.exit(if(result) 0 else 1)
Changing the logging level, unfortunately requires changing each of the separate backends in the chisel-testers repo. The following is an example of changing the TreadleBackend.scala, one of the three.
def expect(signal: InstanceId, expected: BigInt, msg: => String)
(implicit logger: TestErrorLog, verbose: Boolean, base: Int) : Boolean = {
signal match {
case port: Element =>
val name = portNames(port)
val got = treadleTester.peek(name)
val good = got == expected
if (!good) {
logger error
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
else if (verbose) {
logger info
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
if(good) treadleTester.expectationsMet += 1
good
case _ => false
}
}
This would not be an unreasonable issue to file, I think a logger.error would make more sense on an expect failure. There is some concern that changing this could have unexpected consequences for existing users, who are looking for that string.
But I'd like to encourage you to take a look at freechipsproject/chisel-testers2 repo. It's where the team is putting most of their testing development time. It would be easier to change, it has a lot of other nice features for building unit tests, and we are looking at ways we can make it better than chisel-testers.

pattern matching fails on second try

I am using the following code to pattern match an instance of PrivateKey:
import java.security.interfaces.{RSAPrivateKey, RSAPublicKey}
import java.security.{PrivateKey, PublicKey}
object ClientPrivateKey {
def apply(privateKey: PrivateKey) = privateKey match {
case k: RSAPrivateKey ⇒ RSAClientPrivateKey(k)
case k: EdDSAPrivateKey ⇒ EDCClientPrivateKey(k)
}
}
val pk: PrivateKey = ....
ClientPrivateKey(pk)
I am getting a weird behavior when running tests with sbt ~test. On the first run the test passes, on subsequent tries, without restarting sbt, the test fails with:
[info] scala.MatchError: net.i2p.crypto.eddsa.EdDSAPrivateKey#e5d5feef (of class net.i2p.crypto.eddsa.EdDSAPrivateKey)
[info] at com.advancedtelematic.libtuf.data.ClientDataType$ClientPrivateKey$.apply(ClientDataType.scala:39)
[info] at com.advancedtelematic.tuf.keyserver.daemon.KeyGenerationOp$$anonfun$saveToVault$1.apply(KeyGeneratorLeader.scala:122)
[info] at com.advancedtelematic.tuf.keyserver.daemon.KeyGenerationOp$$anonfun$saveToVault$1.apply(KeyGeneratorLeader.scala:121)
[info] at scala.concurrent.Future$$anonfun$traverse$1.apply(Future.scala:576)
[info] at scala.concurrent.Future$$anonfun$traverse$1.apply(Future.scala:575)
[info] at scala.collection.TraversableOnce$$anonfun$foldLeft$1.apply(TraversableOnce.scala:157)
[info] at scala.collection.TraversableOnce$$anonfun$foldLeft$1.apply(TraversableOnce.scala:157)
Which is strange, as net.i2p.crypto.eddsa.EdDSAPrivateKey matches the type of the object being matched.
What can be interfering with this pattern matching?
One thing that comes to my mind is that your privateKey might be coming from a different classloader that the one used by default by your pattern matching code.
You can test this e.g. like that:
def apply(privateKey: PrivateKey) = privateKey match {
case k: RSAPrivateKey ⇒ RSAClientPrivateKey(k)
case k: EdDSAPrivateKey ⇒ EDCClientPrivateKey(k)
case k if k.getClass.getName == classOf[EdDSAPrivateKey].getName =>
val sameClasses = k.getClass == classOf[EdDSAPrivateKey]
val sameClasses = k.getClass.getClassLoader == classOf[EdDSAPrivateKey].getClassLoader
throw new Exception(s"Different class loaders? $sameClasses $sameClassLoaders")
}

how to ignore test utility methods when scalatest detects failures?

I have this convenience method in my tests:
def assertFormat[T: SexpFormat](start: T, expect: Sexp): Unit = {
val sexp = start.toSexp
assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
expect.convertTo[T] should be(start)
}
which is basically a convenience for running an assertion pattern that I do a lot.
It's not possible to rewrite this as a Matcher because of the implicit requirement on SexpFormat[T] (although I'd be interested in hearing of ways to do this that don't require me to write the type MyFormat in foo should roundTrip[MyFormat](...))
If any tests fail inside this utility method, scalatest will flag the internals of assertFormat as being the cause of the test failure. But I really want scalatest to detect the caller of this method to be the cause of the test. How can I do that?
i.e. current output is
[info] - should support custom missing value rules *** FAILED ***
[info] SexpNil did not equal SexpCons(SexpSymbol(:duck),SexpCons(SexpNil,SexpNil)) nil was not (:duck nil) (FormatSpec.scala:11)
[info] org.scalatest.exceptions.TestFailedException:
[info] at org.scalatest.Assertions$class.newAssertionFailedException(Assertions.scala:529)
[info] at org.scalatest.FlatSpec.newAssertionFailedException(FlatSpec.scala:1691)
[info] at org.scalatest.Assertions$AssertionsHelper.macroAssert(Assertions.scala:502)
[info] at org.ensime.sexp.formats.FormatSpec$class.assertFormat(FormatSpec.scala:11)
[info] at org.ensime.sexp.formats.test.FamilyFormatsSpec.assertFormat(FamilyFormatsSpec.scala:151)
[info] at org.ensime.sexp.formats.test.FamilyFormatsSpec.roundtrip(FamilyFormatsSpec.scala:156)
[info] at org.ensime.sexp.formats.test.FamilyFormatsSpec$$anonfun$12.apply(FamilyFormatsSpec.scala:222)
[info] at org.ensime.sexp.formats.test.FamilyFormatsSpec$$anonfun$12.apply(FamilyFormatsSpec.scala:221)
FormatSpec.scala:11 is where my assertFormat is defined. The real failure is in FamilyFormatsSpec.scala:222 (which is calling another convenience method FamilyFormatsSpec.scala:156)
This is possible in ScalaTest 3.0 by taking an implicit org.scalactic.source.Position in your custom assertion. The position will then be computed (via a macro) whenever your assertFormat method is called, and that position will be picked up by the assert and matcher expression inside assertFormat. Here is how it would look:
import org.scalactic.source
def assertFormat[T: SexpFormat](start: T, expect: Sexp)(implicit pos: source.Position): Unit = {
val sexp = start.toSexp
assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
expect.convertTo[T] should be(start)
}
The following example illstrates it. If you have ScalaTest 3.0 on the class path, just :load the following file into the Scala REPL:
:paste
import org.scalatest._
import org.scalactic._
import Matchers._
case class Sexp(o: Any) {
def compactPrint: String = o.toString
def convertTo[T: SexpFormat]: Sexp = implicitly[SexpFormat[T]].convertIt(o)
override def toString = "I'm too sexp for my shirt."
}
trait SexpFormat[T] {
def convertIt(o: Any): Sexp = new Sexp(o)
}
implicit class Sexpify(o: Any) {
def toSexp: Sexp = new Sexp(o)
}
implicit def universalSexpFormat[T]: SexpFormat[T] = new SexpFormat[T] {}
def assertFormat[T: SexpFormat](start: T, expect: Sexp): Unit = {
val sexp = start.toSexp
assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
expect.convertTo[T] should be(start)
}
import org.scalatest.exceptions.TestFailedException
val before = intercept[TestFailedException] { assertFormat(1, new Sexp) }
println(s"${before.failedCodeStackDepth} - This stack depth points to the assert call inside assertFormat")
import org.scalactic.source
def betterAssertFormat[T: SexpFormat](start: T, expect: Sexp)(implicit pos: source.Position): Unit = {
val sexp = start.toSexp
assert(sexp === expect, s"${sexp.compactPrint} was not ${expect.compactPrint}")
expect.convertTo[T] should be(start)
}
val after = intercept[TestFailedException] { betterAssertFormat(1, new Sexp) }
println(s"${after.failedCodeStackDepth} - This stack depth is the betterAssertFormat call itself in your test code")
It will print:
3 - This stack depth points to the assert call inside assertFormat
4 - This stack depth is the betterAssertFormat call itself in your test code

catch a string in boolean option

How do I prevent error when someone does not choose one of the options in scala. This is using Map to get options and I tried to implement Try and catch blocks in case options but it does not work. I'm not sure if this is the right way to do it, if there is any other way let me know. The error is Exception in thread "main" java.lang.NumberFormatException: For input string: "e".
object main extends menu {
def main(args: Array[String]): Unit = {
var opt = 0
do { opt = readOption }
while (menu(opt))
}
}
class menu extends database {
def menu(option: Int): Boolean = try {
actionMap.get(option) match {
case Some(a) => a()
case None => println("That didn't work.")
false
}
} catch {
case _: NumberFormatException => true
}
val actionMap = Map[Int, () => Boolean](1 -> cWords, 2 -> cCharacters, 3 -> exit)
def readOption: Int = {
println(
"""|Please select one of the following:
| 1 - Count Words
| 2 - Count Characters in words
| 3 - quit""".stripMargin)
StdIn.readInt()
}
Use scala.util.Try on readInt(),
import scala.io._
import scala.util._
Try(StdIn.readInt()).toOption
// returns Some(123) for input 123
Try(StdIn.readInt()).toOption
// returns None for input 1a3
Thus readOption delivers Option[Int]. Then
def menu(option: Option[Int]): Boolean = option match {
case Some(a) => actionMap(a)()
case None => println("Try again..."); false
}
Note
A more concise version of main,
def main(args: Array[String]): Unit = while (menu(readOption)) ()
Namely, while menu is true do Unit (or () ).
Here is some working implementation:
import scala.io.StdIn
import scala.util.Try
object Main extends Menu with App {
while (menu(readChoice)) ()
}
class Menu {
val actionMap = Map[Int, () => Boolean](1 -> (() => true), 2 -> (() => true), 3 -> (() => false))
def menu(choice: Option[Int]): Boolean = {
choice.flatMap(actionMap.get)
.map(action => action())
.getOrElse({ println("That didn't work."); false })
}
def readChoice: Option[Int] = {
println(
"""|Please select one of the following:
| 1 - Count Words
| 2 - Count Characters in words
| 3 - quit""".stripMargin)
Try(StdIn.readInt).toOption
}
}
For the first, you can mixin App trait to skip main method boilerplate.
You can simplify your do while loop like this, it has to do nothing inside so you either need some expression or block. A Unit value can be your expression that does nothing.
In scala we name classes using camel case, starting with capital letter.
As readInt throws whenever input is wrong you can catch that using Try, that will return Success(result) of Failure(exception) and change this result to an Option to discard the exception.
what happens in menu is shorthand for
choice match {
case Some(number) =>
actionMap.get(number) match {
case Some(action) =>
action()
case None =>
println("That didn't work.")
false
}
case None =>
println("That didn't work.")
false
}
And could be as well written with for
(for {
number <- choice
action <- actionMap.get(number)
} yield {
action()
}) getOrElse {
println("That didn't work.")
false
}
On as sidenote, you named choice of user an "option" which unfortunately is also a scala class used here extensively, I renamed the variables to avoid confusion.
I would make readOption return a Try[Int], (a Try surrounding StdIn.readInt()), then deal with the possible cases in the menu function

Partial function matching a type erased type

In following code I want the hitA to be called only when the type of i is A. The type to check is provided as a type parameters, therefore it is type erased and a match fails on it, giving me a compiler warning:
abstract type pattern T is unchecked since it is eliminated by erasure
The desired output of the code is:
Hit A
Not hit
In current version when I run the code I get Hit A followed by a ClassCastException.
I understand what is happening and why the warning and the exception is there, but I am not sure how to get around this. I have read basic articles on TypeTags and I understand how to use them in basic cases, but I fail to see how could I create a partial function using TypeTags.
import scala.reflect.runtime.universe._
object TestMatch extends App {
abstract class I
class A extends I {
def hitA() = println("Hit A")
}
class B extends I
def processOpGen[T <: I : TypeTag](op: PartialFunction[I, Unit])(i: I) = {
val fallback : PartialFunction[I, Unit] = {case _ => println("Not hit")}
(op orElse fallback)(i)
}
def partialMatchGen[T <: I : TypeTag](op: T => Unit)(i: I) = {
processOpGen[T] {
case c: T => op(c) // can TypeTag be used here for matching somehow?
}(i)
}
partialMatchGen[A](a => a.hitA())(new A)
partialMatchGen[A](a => a.hitA())(new B)
}
Just replace TypeTag with ClassTag:
import scala.reflect._
object Main extends App {
abstract class I
class A extends I {
def hitA() = println("Hit A")
}
class B extends I
def processOpGen[T <: I : ClassTag](op: PartialFunction[I, Unit])(i: I) = {
val fallback : PartialFunction[I, Unit] = {case _ => println("Not hit")}
(op orElse fallback)(i)
}
def partialMatchGen[T <: I : ClassTag](op: T => Unit)(i: I) = {
processOpGen[T] {
case c: T => op(c) // can TypeTag be used here for matching somehow?
}(i)
}
partialMatchGen[A](a => a.hitA())(new A)
partialMatchGen[A](a => a.hitA())(new B)
}
Processing...
Reused last reload result
[info] Loading project definition from /tmp/rendererL3zBdh8HOA/project/project
[info] Loading project definition from /tmp/rendererL3zBdh8HOA/project
[info] Set current project to rendererWorker (in build file:/tmp/rendererL3zBdh8HOA/)
[info] Reapplying settings...
[info] Set current project to rendererWorker (in build file:/tmp/rendererL3zBdh8HOA/)
[info] Formatting 1 Scala source {file:/tmp/rendererL3zBdh8HOA/}rendererWorker(compile) ...
[warn] Scalariform parser error for /tmp/rendererL3zBdh8HOA/src/main/scala/test.scala: Expected token RBRACKET but got Token(XML_START_OPEN,<,335,<)
[info] Compiling 1 Scala source to /tmp/rendererL3zBdh8HOA/target/classes...
[success] Total time: 11 s, completed Oct 13, 2015 5:16:45 PM
Now running...
[info] Running Main
Hit A
Not hit
[success] Total time: 0 s, completed Oct 13, 2015 5:16:45 PM