我开始为 Scala 中的 X500PrincipalBuilder 类编写单元测试。这是我的测试代码:
import org.junit.runner.RunWith
import org.scalatest.WordSpec
import org.scalatest.junit.JUnitRunner
import org.scalatest.matchers.ShouldMatchers._
import org.scalatest.prop.PropertyChecks._
import javax.security.auth.x500.X500Principal
@RunWith(classOf[JUnitRunner])
class X500PrincipalBuilderTest extends WordSpec {
"An X500 Principal Builder" when {
"given a list of attributes" should {
"properly build an X500 Principal" in {
val table = Table(
("expected", "attributes"),
("CN=Duke", List("CN" -> "Duke"))
)
forAll (table) { (expected, attributes) =>
val b = new X500PrincipalBuilder
attributes foreach { case (key, value) => b addAttribute (key, value) }
b.build should equal (new X500Principal(expected))
}
}
}
}
}
但是,当使用 Scala 2.9.2 或 2.10.0 编译此代码时,我遇到了编译器崩溃。这是 2.9.2 的初始堆栈跟踪:
[ERROR] error: java.lang.Error: no-symbol does not have owner
[INFO] at scala.tools.nsc.symtab.SymbolTable.abort(SymbolTable.scala:34)
[INFO] at scala.tools.nsc.symtab.Symbols$NoSymbol$.owner(Symbols.scala:2155)
[INFO] at scala.tools.nsc.symtab.Symbols$Symbol.logicallyEnclosingMember(Symbols.scala:1251)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.searchIn$1(LambdaLift.scala:234)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.proxy(LambdaLift.scala:243)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.scala$tools$nsc$transform$LambdaLift$LambdaLifter$$proxyRef(LambdaLift.scala:259)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.postTransform(LambdaLift.scala:389)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.transform(LambdaLift.scala:412)
... (removed some hundred stack frames)
2.10.0 的输出似乎是一些难以理解的伪代码来生成 AST:
[INFO]
[INFO] while compiling: /Users/christian/code/truelicense~v2/truelicense-core/src/test/scala/net/java/truelicense/core/util/X500PrincipalBuilderTest.scala
[INFO] during phase: global=lambdalift, atPhase=constructors
[INFO] library version: version 2.10.0
[INFO] compiler version: version 2.10.0
[INFO] reconstructed args: -deprecation -feature -classpath /Users/christian/code/truelicense~v2/truelicense-core/target/classes:/Users/christian/.m2/repository/net/java/truelicense/truelicense-obfuscate/2.3-SNAPSHOT/truelicense-obfuscate-2.3-SNAPSHOT.jar:/Users/christian/.m2/repository/commons-codec/commons-codec/1.8/commons-codec-1.8.jar:/Users/christian/.m2/repository/org/slf4j/slf4j-simple/1.7.5/slf4j-simple-1.7.5.jar:/Users/christian/.m2/repository/org/slf4j/slf4j-api/1.7.5/slf4j-api-1.7.5.jar:/Users/christian/.m2/repository/com/google/code/findbugs/annotations/2.0.1/annotations-2.0.1.jar:/Users/christian/.m2/repository/junit/junit/4.11/junit-4.11.jar:/Users/christian/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Users/christian/.m2/repository/org/mockito/mockito-core/1.9.5/mockito-core-1.9.5.jar:/Users/christian/.m2/repository/org/objenesis/objenesis/1.3/objenesis-1.3.jar:/Users/christian/.m2/repository/org/scalatest/scalatest_2.10.0/1.8/scalatest_2.10.0-1.8.jar:/Users/christian/.m2/repository/org/scala-lang/scala-library/2.10.0/scala-library-2.10.0.jar:/Users/christian/.m2/repository/org/scala-lang/scala-actors/2.10.0/scala-actors-2.10.0.jar:/Users/christian/.m2/repository/org/scala-lang/scala-reflect/2.10.0/scala-reflect-2.10.0.jar:/Users/christian/.m2/repository/org/scalacheck/scalacheck_2.10.0/1.10.0/scalacheck_2.10.0-1.10.0.jar:/Users/christian/.m2/repository/org/scala-tools/testing/test-interface/0.5/test-interface-0.5.jar:/Users/christian/code/truelicense~v2/truelicense-core/target/test-classes -d /Users/christian/code/truelicense~v2/truelicense-core/target/test-classes
[INFO]
[INFO] last tree to typer: term $outer
[INFO] symbol: value $outer (flags: <synthetic> <paramaccessor> <triedcooking> private[this])
[INFO] symbol definition: private[this] val $outer: net.java.truelicense.core.util.X500PrincipalBuilderTest
[INFO] tpe: <notype>
[INFO] symbol owners: value $outer -> anonymous class $anonfun$1 -> value <local X500PrincipalBuilderTest> -> class X500PrincipalBuilderTest -> package util
[INFO] context owners: value key -> value $anonfun -> value $anonfun -> method apply -> anonymous class $anonfun$apply$mcV$sp$2 -> method apply$mcV$sp -> anonymous class $anonfun$apply$mcV$sp$1 -> method apply$mcV$sp -> anonymous class $anonfun$1 -> value <local X500PrincipalBuilderTest> -> class X500PrincipalBuilderTest -> package util
[INFO]
[INFO] == Enclosing template or block ==
[INFO]
[INFO] DefDef( // final def apply(x$1: Tuple2): Unit
[INFO] <method> final <triedcooking>
[INFO] "apply"
[INFO] []
[INFO] // 1 parameter list
[INFO] ValDef( // x0$2: Tuple2
[INFO] <param> <synthetic> <triedcooking>
[INFO] "x0$2"
[INFO] <tpt> // tree.tpe=Tuple2
[INFO] <empty>
[INFO] )
[INFO] <tpt> // tree.tpe=Unit
[INFO] Block( // tree.tpe=Unit
[INFO] // 3 statements
[INFO] ValDef( // case val x1: Tuple2
[INFO] case <synthetic> <triedcooking>
[INFO] "x1"
[INFO] <tpt> // tree.tpe=Tuple2
[INFO] "x0$2" // x0$2: Tuple2, tree.tpe=Tuple2
[INFO] )
[INFO] LabelDef( // case def case4(): Unit, tree.tpe=Unit
[INFO] ()
[INFO] If( // tree.tpe=Unit
[INFO] Apply( // final def ne(x$1: Object): Boolean in class Object, tree.tpe=Boolean
[INFO] "x1"."ne" // final def ne(x$1: Object): Boolean in class Object, tree.tpe=(x$1: Object)Boolean
[INFO] null
[INFO] )
[INFO] Block( // tree.tpe=Unit
[INFO] // 2 statements
[INFO] ValDef( // val key: String
[INFO] <triedcooking>
[INFO] "key"
[INFO] <tpt> // tree.tpe=String
[INFO] Apply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=String
[INFO] TypeApply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=()String
[INFO] x1._1()."$asInstanceOf" // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=[T0 >: ? <: ?]()T0
[INFO] <tpt> // tree.tpe=String
[INFO] )
[INFO] Nil
[INFO] )
[INFO] )
[INFO] ValDef( // val value: String
[INFO] <triedcooking>
[INFO] "value"
[INFO] <tpt> // tree.tpe=String
[INFO] Apply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=String
[INFO] TypeApply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=()String
[INFO] x1._2()."$asInstanceOf" // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=[T0 >: ? <: ?]()T0
[INFO] <tpt> // tree.tpe=String
[INFO] )
[INFO] Nil
[INFO] )
[INFO] )
[INFO] Apply( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "matchEnd3" // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=(x: runtime.BoxedUnit)Unit
[INFO] Block( // tree.tpe=runtime.BoxedUnit
[INFO] Apply( // def addAttribute(x$1: String,x$2: String): Unit in class X500PrincipalBuilder, tree.tpe=Unit
[INFO] "b"."addAttribute" // def addAttribute(x$1: String,x$2: String): Unit in class X500PrincipalBuilder, tree.tpe=(x$1: String, x$2: String)Unit
[INFO] // 2 arguments
[INFO] "key" // val key: String, tree.tpe=String
[INFO] "value" // val value: String, tree.tpe=String
[INFO] )
[INFO] "scala"."runtime"."BoxedUnit"."UNIT" // final val UNIT: runtime.BoxedUnit in object BoxedUnit, tree.tpe=runtime.BoxedUnit
[INFO] )
[INFO] )
[INFO] )
[INFO] Apply( // case def case5(): Unit, tree.tpe=Unit
[INFO] "case5" // case def case5(): Unit, tree.tpe=()Unit
[INFO] Nil
[INFO] )
[INFO] )
[INFO] )
[INFO] LabelDef( // case def case5(): Unit, tree.tpe=Unit
[INFO] ()
[INFO] Apply( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "matchEnd3" // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=(x: runtime.BoxedUnit)Unit
[INFO] Throw( // tree.tpe=Nothing
[ERROR] Apply( // def <init>(obj: Object): MatchError in class MatchError, tree.tpe=MatchError
[ERROR] new MatchError."<init>" // def <init>(obj: Object): MatchError in class MatchError, tree.tpe=(obj: Object)MatchError
[INFO] "x1" // case val x1: Tuple2, tree.tpe=Tuple2
[INFO] )
[INFO] )
[INFO] )
[INFO] )
[INFO] LabelDef( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "x" // x: runtime.BoxedUnit, tree.tpe=runtime.BoxedUnit
[INFO] ()
[INFO] )
[INFO] )
[INFO] )
[INFO]
[INFO] == Expanded type of tree ==
[INFO]
[INFO] <notype>
[INFO]
[INFO] unhandled exception while transforming X500PrincipalBuilderTest.scala
[ERROR] error:
[INFO] while compiling: /Users/christian/code/truelicense~v2/truelicense-core/src/test/scala/net/java/truelicense/core/util/X500PrincipalBuilderTest.scala
[INFO] during phase: lambdalift
[INFO] library version: version 2.10.0
[INFO] compiler version: version 2.10.0
[INFO] reconstructed args: -deprecation -feature -classpath /Users/christian/code/truelicense~v2/truelicense-core/target/classes:/Users/christian/.m2/repository/net/java/truelicense/truelicense-obfuscate/2.3-SNAPSHOT/truelicense-obfuscate-2.3-SNAPSHOT.jar:/Users/christian/.m2/repository/commons-codec/commons-codec/1.8/commons-codec-1.8.jar:/Users/christian/.m2/repository/org/slf4j/slf4j-simple/1.7.5/slf4j-simple-1.7.5.jar:/Users/christian/.m2/repository/org/slf4j/slf4j-api/1.7.5/slf4j-api-1.7.5.jar:/Users/christian/.m2/repository/com/google/code/findbugs/annotations/2.0.1/annotations-2.0.1.jar:/Users/christian/.m2/repository/junit/junit/4.11/junit-4.11.jar:/Users/christian/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Users/christian/.m2/repository/org/mockito/mockito-core/1.9.5/mockito-core-1.9.5.jar:/Users/christian/.m2/repository/org/objenesis/objenesis/1.3/objenesis-1.3.jar:/Users/christian/.m2/repository/org/scalatest/scalatest_2.10.0/1.8/scalatest_2.10.0-1.8.jar:/Users/christian/.m2/repository/org/scala-lang/scala-library/2.10.0/scala-library-2.10.0.jar:/Users/christian/.m2/repository/org/scala-lang/scala-actors/2.10.0/scala-actors-2.10.0.jar:/Users/christian/.m2/repository/org/scala-lang/scala-reflect/2.10.0/scala-reflect-2.10.0.jar:/Users/christian/.m2/repository/org/scalacheck/scalacheck_2.10.0/1.10.0/scalacheck_2.10.0-1.10.0.jar:/Users/christian/.m2/repository/org/scala-tools/testing/test-interface/0.5/test-interface-0.5.jar:/Users/christian/code/truelicense~v2/truelicense-core/target/test-classes -d /Users/christian/code/truelicense~v2/truelicense-core/target/test-classes
[INFO]
[INFO] last tree to typer: term $outer
[INFO] symbol: value $outer (flags: <synthetic> <paramaccessor> <triedcooking> private[this])
[INFO] symbol definition: private[this] val $outer: net.java.truelicense.core.util.X500PrincipalBuilderTest
[INFO] tpe: <notype>
[INFO] symbol owners: value $outer -> anonymous class $anonfun$1 -> value <local X500PrincipalBuilderTest> -> class X500PrincipalBuilderTest -> package util
[INFO] context owners: value key -> value $anonfun -> value $anonfun -> method apply -> anonymous class $anonfun$apply$mcV$sp$2 -> method apply$mcV$sp -> anonymous class $anonfun$apply$mcV$sp$1 -> method apply$mcV$sp -> anonymous class $anonfun$1 -> value <local X500PrincipalBuilderTest> -> class X500PrincipalBuilderTest -> package util
[INFO]
[INFO] == Enclosing template or block ==
[INFO]
[INFO] DefDef( // final def apply(x$1: Tuple2): Unit
[INFO] <method> final <triedcooking>
[INFO] "apply"
[INFO] []
[INFO] // 1 parameter list
[INFO] ValDef( // x0$2: Tuple2
[INFO] <param> <synthetic> <triedcooking>
[INFO] "x0$2"
[INFO] <tpt> // tree.tpe=Tuple2
[INFO] <empty>
[INFO] )
[INFO] <tpt> // tree.tpe=Unit
[INFO] Block( // tree.tpe=Unit
[INFO] // 3 statements
[INFO] ValDef( // case val x1: Tuple2
[INFO] case <synthetic> <triedcooking>
[INFO] "x1"
[INFO] <tpt> // tree.tpe=Tuple2
[INFO] "x0$2" // x0$2: Tuple2, tree.tpe=Tuple2
[INFO] )
[INFO] LabelDef( // case def case4(): Unit, tree.tpe=Unit
[INFO] ()
[INFO] If( // tree.tpe=Unit
[INFO] Apply( // final def ne(x$1: Object): Boolean in class Object, tree.tpe=Boolean
[INFO] "x1"."ne" // final def ne(x$1: Object): Boolean in class Object, tree.tpe=(x$1: Object)Boolean
[INFO] null
[INFO] )
[INFO] Block( // tree.tpe=Unit
[INFO] // 2 statements
[INFO] ValDef( // val key: String
[INFO] <triedcooking>
[INFO] "key"
[INFO] <tpt> // tree.tpe=String
[INFO] Apply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=String
[INFO] TypeApply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=()String
[INFO] x1._1()."$asInstanceOf" // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=[T0 >: ? <: ?]()T0
[INFO] <tpt> // tree.tpe=String
[INFO] )
[INFO] Nil
[INFO] )
[INFO] )
[INFO] ValDef( // val value: String
[INFO] <triedcooking>
[INFO] "value"
[INFO] <tpt> // tree.tpe=String
[INFO] Apply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=String
[INFO] TypeApply( // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=()String
[INFO] x1._2()."$asInstanceOf" // final def $asInstanceOf[T0 >: ? <: ?](): T0 in class Object, tree.tpe=[T0 >: ? <: ?]()T0
[INFO] <tpt> // tree.tpe=String
[INFO] )
[INFO] Nil
[INFO] )
[INFO] )
[INFO] Apply( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "matchEnd3" // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=(x: runtime.BoxedUnit)Unit
[INFO] Block( // tree.tpe=runtime.BoxedUnit
[INFO] Apply( // def addAttribute(x$1: String,x$2: String): Unit in class X500PrincipalBuilder, tree.tpe=Unit
[INFO] "b"."addAttribute" // def addAttribute(x$1: String,x$2: String): Unit in class X500PrincipalBuilder, tree.tpe=(x$1: String, x$2: String)Unit
[INFO] // 2 arguments
[INFO] "key" // val key: String, tree.tpe=String
[INFO] "value" // val value: String, tree.tpe=String
[INFO] )
[INFO] "scala"."runtime"."BoxedUnit"."UNIT" // final val UNIT: runtime.BoxedUnit in object BoxedUnit, tree.tpe=runtime.BoxedUnit
[INFO] )
[INFO] )
[INFO] )
[INFO] Apply( // case def case5(): Unit, tree.tpe=Unit
[INFO] "case5" // case def case5(): Unit, tree.tpe=()Unit
[INFO] Nil
[INFO] )
[INFO] )
[INFO] )
[INFO] LabelDef( // case def case5(): Unit, tree.tpe=Unit
[INFO] ()
[INFO] Apply( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "matchEnd3" // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=(x: runtime.BoxedUnit)Unit
[INFO] Throw( // tree.tpe=Nothing
[ERROR] Apply( // def <init>(obj: Object): MatchError in class MatchError, tree.tpe=MatchError
[ERROR] new MatchError."<init>" // def <init>(obj: Object): MatchError in class MatchError, tree.tpe=(obj: Object)MatchError
[INFO] "x1" // case val x1: Tuple2, tree.tpe=Tuple2
[INFO] )
[INFO] )
[INFO] )
[INFO] )
[INFO] LabelDef( // case def matchEnd3(x: runtime.BoxedUnit): Unit, tree.tpe=Unit
[INFO] "x" // x: runtime.BoxedUnit, tree.tpe=runtime.BoxedUnit
[INFO] ()
[INFO] )
[INFO] )
[INFO] )
[INFO]
[INFO] == Expanded type of tree ==
[INFO]
[INFO] <notype>
[INFO]
[INFO] uncaught exception during compilation: java.lang.IllegalArgumentException
[ERROR] error: java.lang.IllegalArgumentException: Could not find proxy for case val x1: Tuple2 in List(value x1, method apply, anonymous class $anonfun$apply$1, method apply, anonymous class $anonfun$apply$mcV$sp$3, method apply$mcV$sp, anonymous class $anonfun$apply$mcV$sp$2, method apply$mcV$sp, anonymous class $anonfun$apply$mcV$sp$1, method apply$mcV$sp, anonymous class $anonfun$1, value <local X500PrincipalBuilderTest>, class X500PrincipalBuilderTest, package util, package core, package truelicense, package java, package net, package <root>) (currentOwner= value key )
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.scala$tools$nsc$transform$LambdaLift$LambdaLifter$$searchIn$1(LambdaLift.scala:303)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.scala$tools$nsc$transform$LambdaLift$LambdaLifter$$searchIn$1(LambdaLift.scala:308)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.scala$tools$nsc$transform$LambdaLift$LambdaLifter$$searchIn$1(LambdaLift.scala:308)
[INFO] at scala.tools.nsc.transform.LambdaLift$LambdaLifter.scala$tools$nsc$transform$LambdaLift$LambdaLifter$$searchIn$1(LambdaLift.scala:308)
... (removed some hundred stack frames)
WTF 这意味着什么,我该如何解决?