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")
}
Related
I am calling Scala 3's compiler as a library, which gives you CompilationUnit per source after compilation. This has tpdTree, which by the sound of it should contain type information.
I'm trying to walk the tree to get any type symbol as:
atPhase(Phases.typerPhase.next) {
// traverse unit.tpdTree...
}
Where tree walking looks like:
class ValExtractor(tpes: Set[String]) extends tpd.TreeTraverser:
def isAcceptableType(tpe: Types.Type)(using ctx: Context): Boolean =
tpe.baseClasses.exists { sym =>
tpes.contains(sym.fullName.toString)
}
override def traverse(tree: tpd.Tree)(using ctx: Context): Unit =
tree match
case tpd.ValDef(name, tpt, _) if isAcceptableType(tpt.tpe) =>
println("do something")
case t: tpd.Template => this((), t.body)
case t: tpd.PackageDef => this((), t.stats)
case t: tpd.TypeDef => this((), t.rhs)
case _ => ()
end ValExtractor
I get
[info] assertion failed: denotation class Int invalid in run 1. ValidFor: Period(1..55, run = 2)
[info] scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.updateValidity(Denotations.scala:719)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.bringForward(Denotations.scala:744)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.toNewRun$1(Denotations.scala:803)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.current(Denotations.scala:877)
[info] dotty.tools.dotc.core.Symbols$Symbol.recomputeDenot(Symbols.scala:122)
[info] dotty.tools.dotc.core.Symbols$Symbol.computeDenot(Symbols.scala:116)
[info] dotty.tools.dotc.core.Symbols$Symbol.denot(Symbols.scala:109)
[info] dotty.tools.dotc.core.Symbols$.toDenot(Symbols.scala:502)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.updateValidity(Denotations.scala:718)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.bringForward(Denotations.scala:744)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.toNewRun$1(Denotations.scala:803)
[info] dotty.tools.dotc.core.Denotations$SingleDenotation.current(Denotations.scala:877)
[info] dotty.tools.dotc.core.Types$NamedType.computeDenot(Types.scala:2253)
[info] dotty.tools.dotc.core.Types$NamedType.denot(Types.scala:2213)
[info] dotty.tools.dotc.core.Types$NamedType.info(Types.scala:2201)
[info] dotty.tools.dotc.core.Types$TypeRef.underlying(Types.scala:2693)
[info] dotty.tools.dotc.core.Types$Type.baseClasses(Types.scala:600)
What am I doing wrong?
Resolution
In my case I had (using ctx: Context) in my method, but apparently that did not match the run context. Passing it explicitly as follows fixed it:
atPhase(Phases.typerPhase.next) {
(new ValExtractor(valTypes.toSet)).getVals(unit.tpdTree)
}(using run.runContext)
You need to run the query in a context where the run value is the same as when the definitions were created (or is a later run, but an earlier run is not valid).
The error message you are seeing is that you ask about a denotation of a symbol at run 1, which is conceptually before it was created at run 2.
See https://www.youtube.com/watch?v=WxyyJyB_Ssc for a video where the run/phase concept is explained.
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.
Continuing my travel through exercises from Sedgwick and Wayne's Algorithms I came across one in which I have to implement a RandomBag. Originally RandomBag is supposed to implement Iterable (in java) and its Iterator had to serve items in random order.
This is the companion object of my ImmutableRandomBag:
object ImmutableRandomBag{
case class Node[Item](item: Item, next: Option[Node[Item]])
def apply[Item](maybeNode: Option[Node[Item]], size: Int): ImmutableRandomBag[Item] = new ImmutableRandomBag(maybeNode, size)
}
And this is beginning of the class itself:
class ImmutableRandomBag[Item](maybeNode: Option[Node[Item]], size: Int) extends Iterable[Item]{
override def isEmpty: Boolean = size == 0
def add(item: Item) = {
ImmutableRandomBag(Some(Node(item, maybeNode)), size +1)
}
...
}
My understanding was that the val size should have overridden the def size from Iterable trait. When testing the add method I am getting the IndexOutOfBounException:
class RandomBagSpec extends BaseSpec {
trait RandomBag{
val begin = new ImmutableRandomBag[Connection](None, 0)
}
...
"Adding an item to empty RandomBag" should "return another bag with size 1" in new RandomBag {
val bag = begin.add(Connection(0,1))
bag.size should equal(1)
}
}
While debugging size is correctly evaluated in the constructor parameter, so I am not sure where the IndexOutOfBoundException comes from, but I get it whenever I call the add method. Maybe the problem sprouts from the following. In ImmutableRandomBag there's also Iterator implementation:
...
override def iterator: Iterator[Item] = new RandomIterator[Item](maybeNode)
private class RandomIterator[Item](first: Option[Node[Item]]) extends Iterator[Item]{
first match {
case Some(node) => random(node)
case None =>
}
var current: Int = 0
var container: Vector[Item] = Vector()
override def hasNext: Boolean = current < ImmutableRandomBag.this.size
override def next(): Item = {
val item = container(current)
current += 1
item
}
def random(first: Node[Item]) = {
#tailrec
def randomHelper(next: Option[Node[Item]], acc: List[Item]):List[Item]= next match {
case None => acc
case Some(node) => randomHelper(node.next, node.item::acc)
}
val items = randomHelper(Some(first), List[Item]())
container = Random.shuffle(items).toVector
}
}
}
And I have a different test in the same spec for it:
...
"Random Bag's iterator" should "contain all items passed to parent iterable" in new RandomBag{
val connections = List(Connection(0,1), Connection(1,0), Connection(1,1))
var localRB = begin
for(c <- connections) localRB = localRB.add(c)
assert(localRB.iterator.forall(conn=> connections.contains(conn)) == true)
}
...
I also get an IndexOutOfBoundException with the following stack:
[info] RandomBagSpec:
[info] Random Bag's iterator
[info] - should contain all items passed to parent iterable *** FAILED ***
[info] java.lang.IndexOutOfBoundsException: 0
[info] at scala.collection.immutable.Vector.checkRangeConvert(Vector.scala:123)
[info] at scala.collection.immutable.Vector.apply(Vector.scala:114)
[info] at ca.vgorcinschi.algorithms1_3_34.ImmutableRandomBag$RandomIterator.next(ImmutableRandomBag.scala:31)
[info] at scala.collection.Iterator.forall(Iterator.scala:956)
[info] at scala.collection.Iterator.forall$(Iterator.scala:954)
[info] at ca.vgorcinschi.algorithms1_3_34.ImmutableRandomBag$RandomIterator.forall(ImmutableRandomBag.scala:18)
[info] at ca.vgorcinschi.algorithms1_5_19.RandomBagSpec$$anon$1.<init>(RandomBagSpec.scala:16)
[info] at ca.vgorcinschi.algorithms1_5_19.RandomBagSpec.$anonfun$new$1(RandomBagSpec.scala:12)
[info] at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info] at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
The issue seems to come from calling Iterator's next method and indeed the container Vector doesn't contain any elements:
but why is next being called before random?
val size should have overridden the def size from Iterable trait
A val would have, but you don't have one; you just have a constructor parameter in a non-case class. Effectively it's a private val and can't override anything.
but why is next being called before random?
It isn't; in RandomIterator's constructor, random is called (as part of first match ...) before the initializer container = Vector(). next is called only after constructor.
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
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