diff options
21 files changed, 426 insertions, 54 deletions
@@ -20,7 +20,7 @@ We're still using Jira for issue reporting, so please [report any issues](https: # Get in touch! If you need some help with your PR at any time, please feel free to @-mention anyone from the list below, and we will do our best to help you out: - | username | talk to me about... | +| | username | talk to me about... | --------------------------------------------------------------------------------------------------|----------------------------------------------------------------|---------------------------------------------------| <img src="https://avatars.githubusercontent.com/adriaanm" height="50px" title="Adriaan Moors"/> | [`@adriaanm`](https://github.com/adriaanm) | type checker, pattern matcher, infrastructure, language spec | <img src="https://avatars.githubusercontent.com/SethTisue" height="50px" title="Seth Tisue"/> | [`@SethTisue`](https://github.com/SethTisue) | build, developer docs, community build, Jenkins, library, the welcome-to-Scala experience | @@ -104,10 +104,13 @@ Core commands: - `partest` runs partest tests (accepts options, try `partest --help`) - `publishLocal` publishes a distribution locally (can be used as `scalaVersion` in other sbt projects) - - Optionally `set baseVersionSuffix := "abcd123-SNAPSHOT"` + - Optionally `set baseVersionSuffix := "-bin-abcd123-SNAPSHOT"` where `abcd123` is the git hash of the revision being published. You can also - use something custom like `"mypatch"`. This changes the version number from - `2.12.0-SNAPSHOT` to something more stable (`2.12.0-abcd123-SNAPSHOT`). + use something custom like `"-bin-mypatch"`. This changes the version number from + `2.12.2-SNAPSHOT` to something more stable (`2.12.2-bin-abcd123-SNAPSHOT`). + - Note that the `-bin` string marks the version binary compatible. Using it in + sbt will cause the `scalaBinaryVersion` to be `2.12`. If the version is not + binary compatible, we recommend using `-pre`, e.g., `2.13.0-pre-abcd123-SNAPSHOT`. - Optionally `set publishArtifact in (Compile, packageDoc) in ThisBuild := false` to skip generating / publishing API docs (speeds up the process). @@ -199,8 +202,9 @@ CI performs a full bootstrap. The first task, `validate-publish-core`, publishes a build of your commit to the temporary repository https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots. Note that this build is not yet bootstrapped, its bytecode is built using the -current `starr`. The version number is `2.12.0-abcd123-SNAPSHOT` where `abcd123` -is the commit hash. +current `starr`. The version number is `2.12.2-bin-abcd123-SNAPSHOT` where `abcd123` +is the commit hash. For binary incompatible builds, the version number is +`2.13.0-pre-abcd123-SNAPSHOT`. You can use Scala builds in the validation repository locally by adding a resolver and specifying the corresponding `scalaVersion`: @@ -208,7 +212,7 @@ and specifying the corresponding `scalaVersion`: ``` $ sbt > set resolvers += "pr" at "https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots/" -> set scalaVersion := "2.12.0-abcd123-SNAPSHOT" +> set scalaVersion := "2.12.2-bin-abcd123-SNAPSHOT" > console ``` @@ -228,10 +232,8 @@ The CI also publishes nightly API docs: - [2.11.x](http://www.scala-lang.org/files/archive/nightly/2.11.x/api/?C=M;O=D) - [symlink to the latest](http://www.scala-lang.org/files/archive/nightly/2.11.x/api/2.11.x/) -Note that we currently don't publish nightly (or SNAPSHOT) builds in maven or ivy -format to any repository. You can track progress on this front at -[scala-jenkins-infra#133](https://github.com/scala/scala-jenkins-infra/issues/133) -and [scala-dev#68](https://github.com/scala/scala-dev/issues/68). +Using a nightly build in sbt is explained in +[this answer on Stack Overflow](http://stackoverflow.com/questions/40622878) ## Scala CI Internals diff --git a/scripts/jobs/integrate/bootstrap b/scripts/jobs/integrate/bootstrap index a071f3c45f..65c8ef5551 100755 --- a/scripts/jobs/integrate/bootstrap +++ b/scripts/jobs/integrate/bootstrap @@ -3,7 +3,7 @@ # Script Overview # - determine scala version # - determine module versions -# - build minimal core (aka locker) of Scala, use the determined version number, publish to scala-release-temp +# - build minimal core (aka locker) of Scala, use the determined version number, publish to scala-integration # - build those modules where a binary compatible version doesn't exist, publish to scala-integration # - build Scala using the previously built core and bootstrap modules, publish to scala-integration # - for releases @@ -98,15 +98,10 @@ mkdir -p $baseDir/ivy2 rm -rf $baseDir/resolutionScratch_ mkdir -p $baseDir/resolutionScratch_ -# repo for the starr and locker builds -releaseTempRepoUrl=${releaseTempRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-release-temp/"} -# repo for the modules and the quick build +# repo to publish builds integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"} -# the `releaseTempRepoUrl` needs to be in the repositories file to get starr when building quick and the modules. -# `integrationRepoUrl` is there to find modules when building quick and other modules (e.g., partest requires xml). -# the file is re-generated for running the stability test, this time with only `integrationRepoUrl`. -generateRepositoriesConfig $releaseTempRepoUrl $integrationRepoUrl +generateRepositoriesConfig $integrationRepoUrl # ARGH trying to get this to work on multiple versions of sbt-extras... # the old version (on jenkins, and I don't want to upgrade for risk of breaking other builds) honors -sbt-dir @@ -413,7 +408,7 @@ bootstrap() { git clone --reference $WORKSPACE/.git $WORKSPACE/.git $STARR_DIR cd $STARR_DIR git co $STARR_REF - $SBT_CMD -no-colors $sbtArgs --warn "setupBootstrapStarr $releaseTempRepoUrl $STARR_VER" $clean publish >> $baseDir/logs/builds 2>&1 + $SBT_CMD -no-colors $sbtArgs --warn "setupBootstrapStarr $integrationRepoUrl $STARR_VER" $clean publish >> $baseDir/logs/builds 2>&1 ) fi @@ -427,7 +422,7 @@ bootstrap() { # publish more than just core: partest needs scalap # in sabbus lingo, the resulting Scala build will be used as starr to build the released Scala compiler if [ ! -z "$STARR_VER" ]; then SET_STARR=-Dstarr.version=$STARR_VER; fi - $SBT_CMD -no-colors $sbtArgs $SET_STARR --warn "setupBootstrapLocker $releaseTempRepoUrl $SCALA_VER" $clean publish >> $baseDir/logs/builds 2>&1 + $SBT_CMD -no-colors $sbtArgs $SET_STARR --warn "setupBootstrapLocker $integrationRepoUrl $SCALA_VER" $clean publish >> $baseDir/logs/builds 2>&1 echo "### Building modules using locker" @@ -530,13 +525,9 @@ determineScalaVersion deriveModuleVersions removeExistingBuilds $integrationRepoUrl -removeExistingBuilds $releaseTempRepoUrl bootstrap -# for stability testing and sonatype publishing, use artifacts in `integrationRepoUrl` -generateRepositoriesConfig $integrationRepoUrl - if [ "$testStability" == "yes" ] then testStability fi diff --git a/spec/04-basic-declarations-and-definitions.md b/spec/04-basic-declarations-and-definitions.md index 53b34dedc5..c4d3425fff 100644 --- a/spec/04-basic-declarations-and-definitions.md +++ b/spec/04-basic-declarations-and-definitions.md @@ -669,6 +669,15 @@ def f(a: Int = 0)(b: Int = a + 1) = b // OK f(10)() // returns 11 (not 1) ``` +If an [implicit argument](07-implicits.html#implicit-parameters) +is not found by implicit search, it may be supplied using a default argument. + +```scala +implicit val i: Int = 2 +def f(implicit x: Int, s: String = "hi") = s * x +f // "hihi" +``` + ### By-Name Parameters ```ebnf diff --git a/spec/06-expressions.md b/spec/06-expressions.md index 48cff1725a..581170c5f9 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -320,7 +320,7 @@ would not typecheck. ### Named and Default Arguments -If an application might uses named arguments $p = e$ or default +If an application is to use named arguments $p = e$ or default arguments, the following conditions must hold. - For every named argument $p_i = e_i$ which appears left of a positional argument @@ -330,7 +330,7 @@ arguments, the following conditions must hold. argument defines a parameter which is already specified by a positional argument. - Every formal parameter $p_j:T_j$ which is not specified by either a positional - or a named argument has a default argument. + or named argument has a default argument. If the application uses named or default arguments the following transformation is applied to convert it into diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 76d042ce3b..37dea477c6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -1264,14 +1264,22 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label, pos: Position) { /* True if the equality comparison is between values that require the use of the rich equality - * comparator (scala.runtime.Comparator.equals). This is the case when either side of the + * comparator (scala.runtime.BoxesRunTime.equals). This is the case when either side of the * comparison might have a run-time type subtype of java.lang.Number or java.lang.Character. - * When it is statically known that both sides are equal and subtypes of Number of Character, - * not using the rich equality is possible (their own equals method will do ok.) + * + * When it is statically known that both sides are equal and subtypes of Number or Character, + * not using the rich equality is possible (their own equals method will do ok), except for + * java.lang.Float and java.lang.Double: their `equals` have different behavior around `NaN` + * and `-0.0`, see Javadoc (scala-dev#329). */ val mustUseAnyComparator: Boolean = { - val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe) - !areSameFinals && platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol) + platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol) && { + val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe) && { + val sym = l.tpe.typeSymbol + sym != BoxedFloatClass && sym != BoxedDoubleClass + } + !areSameFinals + } } if (mustUseAnyComparator) { diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index f1f5f37c36..224de97734 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -77,10 +77,10 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr if (reader != null) { reader.read match { case 'a' | 'A' => - new Throwable().printStackTrace() + new Throwable().printStackTrace(writer) System.exit(1) case 's' | 'S' => - new Throwable().printStackTrace() + new Throwable().printStackTrace(writer) writer.println() writer.flush() case _ => diff --git a/src/library/scala/collection/immutable/Map.scala b/src/library/scala/collection/immutable/Map.scala index cbdf7b39f5..4107b6414d 100644 --- a/src/library/scala/collection/immutable/Map.scala +++ b/src/library/scala/collection/immutable/Map.scala @@ -198,7 +198,7 @@ object Map extends ImmutableMapFactory[Map] { else if (key == key2) new Map4(key1, value1, key2, value, key3, value3, key4, value4) else if (key == key3) new Map4(key1, value1, key2, value2, key3, value, key4, value4) else if (key == key4) new Map4(key1, value1, key2, value2, key3, value3, key4, value) - else new HashMap + ((key1, value1), (key2, value2), (key3, value3), (key4, value4), (key, value)) + else (new HashMap).updated(key1,value1).updated(key2, value2).updated(key3, value3).updated(key4, value4).updated(key, value) def + [V1 >: V](kv: (K, V1)): Map[K, V1] = updated(kv._1, kv._2) def - (key: K): Map[K, V] = if (key == key1) new Map3(key2, value2, key3, value3, key4, value4) diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index 047ea736bd..0f16f97cb0 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -193,7 +193,7 @@ object Set extends ImmutableSetFactory[Set] { elem == elem1 || elem == elem2 || elem == elem3 || elem == elem4 def + (elem: A): Set[A] = if (contains(elem)) this - else new HashSet[A] + (elem1, elem2, elem3, elem4, elem) + else new HashSet[A] + elem1 + elem2 + elem3 + elem4 + elem def - (elem: A): Set[A] = if (elem == elem1) new Set3(elem2, elem3, elem4) else if (elem == elem2) new Set3(elem1, elem3, elem4) diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala index 11ff1f0893..de61ebb796 100644 --- a/src/library/scala/collection/mutable/HashMap.scala +++ b/src/library/scala/collection/mutable/HashMap.scala @@ -73,10 +73,18 @@ extends AbstractMap[A, B] } override def getOrElseUpdate(key: A, defaultValue: => B): B = { - val i = index(elemHashCode(key)) + val hash = elemHashCode(key) + val i = index(hash) val entry = findEntry(key, i) if (entry != null) entry.value - else addEntry(createNewEntry(key, defaultValue), i) + else { + val table0 = table + val default = defaultValue + // Avoid recomputing index if the `defaultValue()` hasn't triggered + // a table resize. + val newEntryIndex = if (table0 eq table) i else index(hash) + addEntry(createNewEntry(key, default), newEntryIndex) + } } /* inlined HashTable.findEntry0 to preserve its visibility */ diff --git a/src/reflect/scala/reflect/internal/Depth.scala b/src/reflect/scala/reflect/internal/Depth.scala index a330e0accb..5e7202f8bf 100644 --- a/src/reflect/scala/reflect/internal/Depth.scala +++ b/src/reflect/scala/reflect/internal/Depth.scala @@ -5,7 +5,7 @@ package internal import Depth._ final class Depth private (val depth: Int) extends AnyVal with Ordered[Depth] { - def max(that: Depth): Depth = if (this < that) that else this + def max(that: Depth): Depth = if (this.depth < that.depth) that else this def decr(n: Int): Depth = if (isAnyDepth) this else Depth(depth - n) def incr(n: Int): Depth = if (isAnyDepth) this else Depth(depth + n) def decr: Depth = decr(1) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 1aef30819a..933afbea2b 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -274,6 +274,7 @@ abstract class TreeInfo { def mayBeVarGetter(sym: Symbol): Boolean = sym.info match { case NullaryMethodType(_) => sym.owner.isClass && !sym.isStable case PolyType(_, NullaryMethodType(_)) => sym.owner.isClass && !sym.isStable + case PolyType(_, mt @ MethodType(_, _))=> mt.isImplicit && sym.owner.isClass && !sym.isStable case mt @ MethodType(_, _) => mt.isImplicit && sym.owner.isClass && !sym.isStable case _ => false } diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 9635f320fe..8be4d159f1 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -768,7 +768,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) result } - private object paste extends Pasted(prompt) { + private object paste extends Pasted(replProps.promptText) { def interpret(line: String) = intp interpret line def echo(message: String) = ILoop.this echo message diff --git a/src/repl/scala/tools/nsc/interpreter/Pasted.scala b/src/repl/scala/tools/nsc/interpreter/Pasted.scala index 3a7eda1b77..7ab5e5bb42 100644 --- a/src/repl/scala/tools/nsc/interpreter/Pasted.scala +++ b/src/repl/scala/tools/nsc/interpreter/Pasted.scala @@ -38,10 +38,9 @@ abstract class Pasted(prompt: String) { def matchesContinue(line: String) = matchesString(line, ContinueString) def running = isRunning - private def matchesString(line: String, target: String): Boolean = ( - (line startsWith target) || - (line.nonEmpty && spacey(line.head) && matchesString(line.tail, target)) - ) + private def matchesString(line: String, target: String): Boolean = + line.startsWith(target) || (line.nonEmpty && spacey(line.head) && matchesString(line.tail, target)) + private def stripString(line: String, target: String) = line indexOf target match { case -1 => line case idx => line drop (idx + target.length) diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala index 94844dcae2..36e2518993 100644 --- a/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala @@ -23,12 +23,14 @@ class ListBenchmark { var values: List[Content] = _ var mid: Content = _ var last: Content = _ + var replacement: Content = _ @Setup(Level.Trial) def initKeys(): Unit = { values = List.tabulate(size)(v => Content(v)) mid = Content(size / 2) last = Content(Math.max(0,size -1)) + replacement = Content(size * 2 + 1) } @Benchmark def filter_includeAll: Any = { @@ -55,18 +57,14 @@ class ListBenchmark { values.filter(v => v.value == last.value) } - @Setup(Level.Trial) def initKeys(): Unit = { - values = List.tabulate(size)(n => if (n == size / 2) "mid" else "") - } - @Benchmark def mapConserve_identity: Any = { values.mapConserve(x => x) } @Benchmark def mapConserve_modifyAll: Any = { - values.mapConserve(x => "replace") + values.mapConserve(x => replacement) } @Benchmark def mapConserve_modifyMid: Any = { - values.mapConserve(x => if (x == "mid") "replace" else x) + values.mapConserve(x => if (x == mid) replacement else x) } } diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/MapBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/MapBenchmark.scala new file mode 100644 index 0000000000..a0358d6a1a --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/MapBenchmark.scala @@ -0,0 +1,29 @@ +package scala.collection.immutable + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra._ + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class MapBenchmark { + + var base: Map[String,String] = _ + + + @Setup(Level.Trial) def initKeys(): Unit = { + base = Map("a" -> "a", "b" -> "b", "c" -> "c", "d" -> "d") + } + + // immutable map is implemented as EmptyMap -> Map1 -> Map2 -> Map3 -> Map4 -> Hashmap + // add an extra entry to Map4 causes a lot of work, benchmark the transition + @Benchmark def map4AddElement(bh: Blackhole): Unit = { + bh.consume(base.updated("e", "e")) + } +} diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/SetBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/SetBenchmark.scala new file mode 100644 index 0000000000..9330626691 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/SetBenchmark.scala @@ -0,0 +1,29 @@ +package scala.collection.immutable + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra._ + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class SetBenchmark { + + var base: Set[String] = _ + + + @Setup(Level.Trial) def initKeys(): Unit = { + base = Set("a", "b", "c", "d") + } + + // immutable map is implemented as EmptySet -> Set1 -> Set2 -> Set3 -> Set4 -> HashSet + // add an extra entry to Set4 causes a lot of work, benchmark the transition + @Benchmark def set4AddElement(bh: Blackhole): Unit = { + bh.consume(base + "e") + } +} diff --git a/test/files/pos/t4237.scala b/test/files/pos/t4237.scala index fcf6eb8bf1..3f605607b2 100644 --- a/test/files/pos/t4237.scala +++ b/test/files/pos/t4237.scala @@ -2,5 +2,16 @@ class A { (new { def field = 0; def field_=(i: Int) = () }).field = 5 // compiles as expected (new { def field(implicit i: Int) = 0; def field_=(i: Int) = () }).field = 5 // compiles even with implicit params on getter (new { def field = 0; def field_=[T](i: Int) = () }).field = 5 // compiles with type param on setter - (new { def field[T] = 0; def field_=(i: Int) = () }).field = 5 // DOESN'T COMPILE -}
\ No newline at end of file + (new { def field[T] = 0; def field_=(i: Int) = () }).field = 5 // DIDN'T COMPILE + + class Imp + implicit val imp: Imp = new Imp + implicit val implicitList: List[Int] = null + + // compiles even with implicit params on setter + (new { def field(implicit i: Int) = 0; def field_=(i: Int)(implicit j: Imp) = () }).field = 5 + (new { def field(implicit i: Int) = 0; def field_=[T <: Imp](i: Int)(implicit j: T) = () }).field = 5 + // was reassignment to val + (new { def field[T](implicit ts: List[T]) = 0; def field_=[T](i: Int)(implicit ts: List[T]) = () }).field = 5 + (new { def field[T](implicit ts: List[T]) = 0; def field_=[T](i: T)(implicit ts: List[T]) = () }).field = 5 +} diff --git a/test/files/run/number-parsing.scala b/test/files/run/number-parsing.scala index ad1481063e..5627ee9006 100644 --- a/test/files/run/number-parsing.scala +++ b/test/files/run/number-parsing.scala @@ -3,8 +3,8 @@ object Test { val MinusZero = Float.box(-0.0f) val PlusZero = Float.box(0.0f) - assert(PlusZero match { case MinusZero => false ; case _ => true }) - assert(MinusZero match { case PlusZero => false ; case _ => true }) + assert(PlusZero match { case MinusZero => true ; case _ => false }) + assert(MinusZero match { case PlusZero => true ; case _ => false }) assert((MinusZero: scala.Float) == (PlusZero: scala.Float)) assert(!(MinusZero equals PlusZero)) diff --git a/test/files/run/sd329.scala b/test/files/run/sd329.scala new file mode 100644 index 0000000000..c934e2c986 --- /dev/null +++ b/test/files/run/sd329.scala @@ -0,0 +1,76 @@ +object Test extends App { + def d1: Double = 0.0 + def d2: Double = -0.0 + def d3: Double = Double.NaN + def d4: Double = Double.NaN + assert(d1 == d2) + assert(d3 != d4) + + def d1B: java.lang.Double = d1 + def d2B: java.lang.Double = d2 + def d3B: java.lang.Double = d3 + def d4B: java.lang.Double = d4 + assert(d1B == d2B) + assert(d1 == d1B) + assert(d1B == d1) + assert(d3B != d4B) + assert(d3 != d4B) + assert(d3B != d4) + + assert(!d1B.equals(d2B)) // ! see javadoc + assert( d3B.equals(d4B)) // ! see javadoc + + def d1A: Any = d1 + def d2A: Any = d2 + def d3A: Any = d3 + def d4A: Any = d4 + assert(d1A == d2A) + assert(d1 == d1A) + assert(d1A == d1) + assert(d1B == d1A) + assert(d1A == d1B) + + assert(d3A != d4A) + assert(d3 != d4A) + assert(d3A != d4) + assert(d3B != d4A) + assert(d3A != d4B) + + + def f1: Float = 0.0f + def f2: Float = -0.0f + def f3: Float = Float.NaN + def f4: Float = Float.NaN + assert(f1 == f2) + assert(f3 != f4) + + def f1B: java.lang.Float = f1 + def f2B: java.lang.Float = f2 + def f3B: java.lang.Float = f3 + def f4B: java.lang.Float = f4 + assert(f1B == f2B) + assert(f1 == f1B) + assert(f1B == f1) + assert(f3B != f4B) + assert(f3 != f4B) + assert(f3B != f4) + + assert(!f1B.equals(f2B)) // ! see javadoc + assert( f3B.equals(f4B)) // ! see javadoc + + def f1A: Any = f1 + def f2A: Any = f2 + def f3A: Any = f3 + def f4A: Any = f4 + assert(f1A == f2A) + assert(f1 == f1A) + assert(f1A == f1) + assert(f1B == f1A) + assert(f1A == f1B) + + assert(f3A != f4A) + assert(f3 != f4A) + assert(f3A != f4) + assert(f3B != f4A) + assert(f3A != f4B) +} diff --git a/test/junit/scala/collection/mutable/HashMapTest.scala b/test/junit/scala/collection/mutable/HashMapTest.scala new file mode 100644 index 0000000000..cc1979a920 --- /dev/null +++ b/test/junit/scala/collection/mutable/HashMapTest.scala @@ -0,0 +1,38 @@ +package scala.collection +package mutable + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class HashMapTest { + + @Test + def getOrElseUpdate_mutationInCallback() { + val hm = new mutable.HashMap[String, String]() + // add enough elements to resize the hash table in the callback + def add() = 1 to 100000 foreach (i => hm(i.toString) = "callback") + hm.getOrElseUpdate("0", { + add() + "" + }) + assertEquals(Some(""), hm.get("0")) + } + + @Test + def getOrElseUpdate_evalOnce(): Unit = { + var i = 0 + val hm = new mutable.HashMap[Int, Int]() + hm.getOrElseUpdate(0, {i += 1; i}) + assertEquals(1, hm(0)) + } + + @Test + def getOrElseUpdate_noEval(): Unit = { + val hm = new mutable.HashMap[Int, Int]() + hm.put(0, 0) + hm.getOrElseUpdate(0, throw new AssertionError()) + } +} diff --git a/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala b/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala new file mode 100644 index 0000000000..f24e11c9e2 --- /dev/null +++ b/test/junit/scala/tools/nsc/reporters/ConsoleReporterTest.scala @@ -0,0 +1,173 @@ +package scala +package tools.nsc +package reporters + +import java.io.{ByteArrayOutputStream, StringReader, BufferedReader, PrintStream, PrintWriter} +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.reflect.internal.util._ + + +@RunWith(classOf[JUnit4]) +class ConsoleReporterTest { + val source = "Test_ConsoleReporter" + val batchFile = new BatchSourceFile(source, "For testing".toList) + val posWithSource = new OffsetPosition(batchFile, 4) + val content = posWithSource.lineContent + val writerOut = new ByteArrayOutputStream() + val echoWriterOut = new ByteArrayOutputStream() + + + def createConsoleReporter(inputForReader: String, errOut: ByteArrayOutputStream, echoOut: ByteArrayOutputStream = null): ConsoleReporter = { + val reader = new BufferedReader(new StringReader(inputForReader)) + + /** Create reporter with the same writer and echoWriter if echoOut is null */ + echoOut match { + case null => new ConsoleReporter(new Settings(), reader, new PrintWriter(errOut)) + case _ => new ConsoleReporter(new Settings(), reader, new PrintWriter(errOut), new PrintWriter(echoWriterOut)) + } + } + + + def testHelper(pos: Position = NoPosition, msg: String, severity: String = "")(test: Position => Unit) = { + test(pos) + if (msg.isEmpty && severity.isEmpty) assertTrue(writerOut.toString.isEmpty) + else { + if (!pos.isDefined) assertEquals(severity + msg, writerOut.toString.lines.next) + else { + val it = writerOut.toString.lines + assertEquals(source + ":1: " + severity + msg, it.next) + assertEquals(content, it.next) + assertEquals(" ^", it.next) + } + } + writerOut.reset + } + + + @Test + def printMessageTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut) + testHelper(msg = "Hello World!")(_ => reporter.printMessage("Hello World!")) + testHelper(msg = "Testing with NoPosition")(reporter.printMessage(_, "Testing with NoPosition")) + testHelper(posWithSource, "Testing with Defined Position")(reporter.printMessage(_, "Testing with Defined Position")) + } + + + @Test + def echoTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut, echoWriterOut) + reporter.echo("Hello World!") + assertEquals("Hello World!", echoWriterOut.toString.lines.next) + + /** Check with constructor which has the same writer and echoWriter */ + val reporter2 = createConsoleReporter("r", writerOut) + testHelper(msg = "Hello World!")(_ => reporter2.echo("Hello World!")) + } + + + @Test + def printTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut) + testHelper(msg = "test")(reporter.print(_, "test", reporter.INFO)) + testHelper(msg = "test", severity = "warning: ")(reporter.print(_, "test", reporter.WARNING)) + testHelper(msg = "test", severity = "error: ")(reporter.print(_, "test", reporter.ERROR)) + testHelper(posWithSource, msg = "test")(reporter.print(_, "test", reporter.INFO)) + testHelper(posWithSource, msg = "test", severity = "warning: ")(reporter.print(_, "test", reporter.WARNING)) + testHelper(posWithSource, msg = "test", severity = "error: ")(reporter.print(_, "test", reporter.ERROR)) + } + + + @Test + def printColumnMarkerTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut) + testHelper(msg = "")(reporter.printColumnMarker(_)) + + reporter.printColumnMarker(posWithSource) + assertEquals(" ^", writerOut.toString.lines.next) + writerOut.reset + } + + + @Test + def displayTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut) + + /** Change maxerrs and maxwarns from default */ + reporter.settings.maxerrs.value = 1 + reporter.settings.maxwarns.value = 1 + + testHelper(msg = "Testing display")(reporter.display(_, "Testing display", reporter.INFO)) + testHelper(msg = "Testing display", severity = "warning: ")(reporter.display(_, "Testing display", reporter.WARNING)) + testHelper(msg = "Testing display", severity = "error: ")(reporter.display(_, "Testing display", reporter.ERROR)) + testHelper(posWithSource, msg = "Testing display")(reporter.display(_, "Testing display", reporter.INFO)) + testHelper(posWithSource, msg = "Testing display", severity = "warning: ")(reporter.display(_, "Testing display", reporter.WARNING)) + testHelper(posWithSource, msg = "Testing display", severity = "error: ")(reporter.display(_, "Testing display", reporter.ERROR)) + + reporter.resetCount(reporter.ERROR) + reporter.resetCount(reporter.WARNING) + + reporter.ERROR.count += 1 + testHelper(posWithSource, msg = "Testing display for maxerrs to pass", severity = "error: ")(reporter.display(_, "Testing display for maxerrs to pass", reporter.ERROR)) + reporter.ERROR.count += 1 + testHelper(msg = "")(reporter.display(_, "Testing display for maxerrs to fail", reporter.ERROR)) + + reporter.WARNING.count += 1 + testHelper(posWithSource, msg = "Testing display for maxwarns to pass", severity = "warning: ")(reporter.display(_, "Testing display for maxwarns to pass", reporter.WARNING)) + reporter.WARNING.count += 1 + testHelper(msg = "")(reporter.display(_, "Testing display for maxwarns to fail", reporter.WARNING)) + } + + + @Test + def finishTest(): Unit = { + val reporter = createConsoleReporter("r", writerOut) + + reporter.resetCount(reporter.ERROR) + reporter.resetCount(reporter.WARNING) + testHelper(msg = "")(_ => reporter.finish()) + + reporter.ERROR.count = 10 + reporter.WARNING.count = 3 + reporter.finish() + val it = writerOut.toString.lines + assertEquals("three warnings found", it.next) + assertEquals("10 errors found", it.next) + writerOut.reset + } + + + @Test + def displayPromptTest(): Unit = { + val output = "a)bort, s)tack, r)esume: " + + /** Check for stack trace */ + val reporter = createConsoleReporter("s", writerOut, echoWriterOut) + reporter.displayPrompt() + val it = writerOut.toString.lines + assertTrue(it.next.isEmpty) + assertEquals(output + "java.lang.Throwable", it.next) + assertTrue(it.hasNext) + + /** Check for no stack trace */ + val writerOut2 = new ByteArrayOutputStream() + val reporter2 = createConsoleReporter("w", writerOut2) + reporter2.displayPrompt() + val it2 = writerOut2.toString.lines + assertTrue(it2.next.isEmpty) + assertEquals(output, it2.next) + assertFalse(it2.hasNext) + + /** Check for no stack trace */ + val writerOut3 = new ByteArrayOutputStream() + val reporter3 = createConsoleReporter("r", writerOut3) + reporter3.displayPrompt() + val it3 = writerOut3.toString.lines + assertTrue(it3.next.isEmpty) + assertEquals(output, it3.next) + assertFalse(it3.hasNext) + } +} |