From 5bfe51a5100d91d14940647e88027656e0ff426a Mon Sep 17 00:00:00 2001 From: Michel Davit Date: Tue, 28 Nov 2023 15:40:50 +0100 Subject: [PATCH 1/3] Add buildable for java.util.HashMap --- .../util/BuildableVersionSpecific.scala | 20 ++++++++++++++++++- .../util/BuildableVersionSpecific.scala | 18 ++++++++++++++++- .../scala/org/scalacheck/util/Buildable.scala | 8 +++++++- .../util/BuildableSpecification.scala | 12 +++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala index 43ffcb9a6..5c98c45f9 100644 --- a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala @@ -9,13 +9,19 @@ package org.scalacheck.util -import java.util.ArrayList +import java.util.{ArrayList, HashMap} import collection.{Map => _, _} import generic.CanBuildFrom import scala.collection.mutable.Builder private[util] trait BuildableVersionSpecific { + import scala.collection.JavaConverters._ + import scala.language.implicitConversions + + implicit def wrapArrayList[T](xs: ArrayList[T]): Traversable[T] = xs.asScala + implicit def wrapHashMap[K, V](xs: HashMap[K, V]): Traversable[(K, V)] = xs.asScala + implicit def buildableCanBuildFrom[T, F, C](implicit c: CanBuildFrom[F, T, C]): Buildable[T, C] = new Buildable[T, C] { def builder = c.apply @@ -32,6 +38,18 @@ private[util] class ArrayListBuilder[T] extends Builder[T, ArrayList[T]] { def result(): ArrayList[T] = al } +private[util] class HashMapBuilder[K, V] extends Builder[(K, V), HashMap[K, V]] { + private val hm = new HashMap[K, V] + + def +=(x: (K, V)): this.type = { + val (k, v) = x + hm.put(k, v) + this + } + def clear(): Unit = hm.clear() + def result(): HashMap[K, V] = hm +} + /** CanBuildFrom instances implementing Serializable, so that the objects capturing those can be serializable too. */ object SerializableCanBuildFroms { diff --git a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala index 1e95b7e53..09f43c286 100644 --- a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala @@ -9,11 +9,16 @@ package org.scalacheck.util -import java.util.ArrayList +import java.util.{ArrayList, HashMap} import scala.collection.mutable.Builder import scala.collection.{Map => _, _} private[util] trait BuildableVersionSpecific { + import scala.jdk.CollectionConverters._ + + implicit def wrapArrayList[T](xs: ArrayList[T]): Iterable[T] = xs.asScala + implicit def wrapHashMap[K, V](xs: HashMap[K, V]): Iterable[(K, V)] = xs.asScala + implicit def buildableFactory[T, C](implicit f: Factory[T, C]): Buildable[T, C] = new Buildable[T, C] { def builder = f.newBuilder @@ -30,6 +35,17 @@ private[util] class ArrayListBuilder[T] extends Builder[T, ArrayList[T]] { def result(): ArrayList[T] = al } +private[util] class HashMapBuilder[K, V] extends Builder[(K, V), HashMap[K, V]] { + private val hm = new HashMap[K, V] + def addOne(x: (K, V)): this.type = { + val (k, v) = x + hm.put(k, v) + this + } + def clear(): Unit = hm.clear() + def result(): HashMap[K, V] = hm +} + /** Factory instances implementing Serializable, so that the objects capturing those can be serializable too. */ // Named `...CanBuildFroms` for 2.12 source compatibility (`import SerializableCanBuildFroms._`) diff --git a/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala b/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala index e78b7ed70..fee467e78 100644 --- a/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala +++ b/core/shared/src/main/scala/org/scalacheck/util/Buildable.scala @@ -22,12 +22,18 @@ trait Buildable[T, C] extends Serializable { } object Buildable extends BuildableVersionSpecific { - import java.util.ArrayList + import java.util.{ArrayList, HashMap} + implicit def buildableArrayList[T]: Buildable[T, ArrayList[T]] = new Buildable[T, ArrayList[T]] { def builder = new ArrayListBuilder[T] } + implicit def buildableHashMap[K, V]: Buildable[(K, V), HashMap[K, V]] = + new Buildable[(K, V), HashMap[K, V]] { + def builder = new HashMapBuilder[K, V] + } + def buildableSeq[T]: Buildable[T, Seq[T]] = new Buildable[T, Seq[T]] { def builder: mutable.Builder[T, Seq[T]] = diff --git a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala index 98a599ff9..19bfde440 100644 --- a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala +++ b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala @@ -12,6 +12,7 @@ package util import scala.collection._ +import Buildable._ import ScalaVersionSpecific._ object BuildableSpecification { @@ -47,4 +48,15 @@ object BuildableSpecification { implicit val iterableGen: Gen[immutable.Iterable[String]] = container[immutable.Iterable] implicit val trieIteratorGen: Gen[immutable.Queue[String]] = container[immutable.Queue] + + implicit val arrayListGen: Gen[java.util.ArrayList[String]] = container[java.util.ArrayList] + + def buildable[C[_, _]](implicit + evb: Buildable[(String, Long), C[String, Long]], + evt: C[String, Long] => Traversable[(String, Long)] + ) = Gen.buildableOf[C[String, Long], (String, Long)](for (str <- Gen.alphaStr; lng <- Gen.long) yield (str, lng)) + + implicit val mapGen: Gen[Map[String, Long]] = buildable[Map] + + implicit val hashMapGen: Gen[java.util.HashMap[String, Long]] = buildable[java.util.HashMap] } From 94532f370e88302bdca17201325cf5a9b8e811f7 Mon Sep 17 00:00:00 2001 From: Michel Davit Date: Mon, 25 Mar 2024 17:10:48 +0100 Subject: [PATCH 2/3] Remove provided implicit evidence --- .../util/BuildableVersionSpecific.scala | 6 ---- .../util/BuildableVersionSpecific.scala | 4 --- .../util/BuildableSpecification.scala | 21 ++++++++---- doc/UserGuide.md | 33 +++++++++++-------- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala index 5c98c45f9..7f67cf236 100644 --- a/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.12-/org/scalacheck/util/BuildableVersionSpecific.scala @@ -16,12 +16,6 @@ import generic.CanBuildFrom import scala.collection.mutable.Builder private[util] trait BuildableVersionSpecific { - import scala.collection.JavaConverters._ - import scala.language.implicitConversions - - implicit def wrapArrayList[T](xs: ArrayList[T]): Traversable[T] = xs.asScala - implicit def wrapHashMap[K, V](xs: HashMap[K, V]): Traversable[(K, V)] = xs.asScala - implicit def buildableCanBuildFrom[T, F, C](implicit c: CanBuildFrom[F, T, C]): Buildable[T, C] = new Buildable[T, C] { def builder = c.apply diff --git a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala index 09f43c286..ea694ecf9 100644 --- a/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala +++ b/core/shared/src/main/scala-2.13+/org/scalacheck/util/BuildableVersionSpecific.scala @@ -14,10 +14,6 @@ import scala.collection.mutable.Builder import scala.collection.{Map => _, _} private[util] trait BuildableVersionSpecific { - import scala.jdk.CollectionConverters._ - - implicit def wrapArrayList[T](xs: ArrayList[T]): Iterable[T] = xs.asScala - implicit def wrapHashMap[K, V](xs: HashMap[K, V]): Iterable[(K, V)] = xs.asScala implicit def buildableFactory[T, C](implicit f: Factory[T, C]): Buildable[T, C] = new Buildable[T, C] { diff --git a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala index 19bfde440..760f6450a 100644 --- a/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala +++ b/core/shared/src/test/scala/org/scalacheck/util/BuildableSpecification.scala @@ -21,6 +21,11 @@ object BuildableSpecification { evt: C[String] => Traversable[String] ) = Gen.containerOf[C, String](Gen.alphaStr) + def buildable[C[_, _]](implicit + evb: Buildable[(String, Long), C[String, Long]], + evt: C[String, Long] => Traversable[(String, Long)] + ) = Gen.buildableOf[C[String, Long], (String, Long)](Gen.zip(Gen.alphaStr, Gen.long)) + implicit val listGen: Gen[List[String]] = container[List] implicit val streamGen: Gen[Stream[String]] = container[Stream] @@ -49,14 +54,16 @@ object BuildableSpecification { implicit val trieIteratorGen: Gen[immutable.Queue[String]] = container[immutable.Queue] - implicit val arrayListGen: Gen[java.util.ArrayList[String]] = container[java.util.ArrayList] + implicit val mapGen: Gen[Map[String, Long]] = buildable[Map] + + Gen.buildableOf[Map[String, Int], (String, Int)](Gen.zip(Gen.alphaStr, Gen.choose(0, 100))) - def buildable[C[_, _]](implicit - evb: Buildable[(String, Long), C[String, Long]], - evt: C[String, Long] => Traversable[(String, Long)] - ) = Gen.buildableOf[C[String, Long], (String, Long)](for (str <- Gen.alphaStr; lng <- Gen.long) yield (str, lng)) + // java containers + { + import scala.collection.convert.ImplicitConversionsToScala._ - implicit val mapGen: Gen[Map[String, Long]] = buildable[Map] + implicit val arrayListGen: Gen[java.util.ArrayList[String]] = container[java.util.ArrayList] - implicit val hashMapGen: Gen[java.util.HashMap[String, Long]] = buildable[java.util.HashMap] + implicit val hashMapGen: Gen[java.util.HashMap[String, Long]] = buildable[java.util.HashMap] + } } diff --git a/doc/UserGuide.md b/doc/UserGuide.md index 10289e9b3..95e45d784 100644 --- a/doc/UserGuide.md +++ b/doc/UserGuide.md @@ -468,32 +468,39 @@ value ultimately reported as failing might not satisfy the condition given to case that does. To avoid confusion, the corresponding shrink for the type can use `suchThat` method too. -#### Generating Containers +#### Generating Buildables and Containers -There is a special generator, `Gen.containerOf`, that generates containers such -as lists and arrays. They take another generator as argument, that is -responsible for generating the individual items. You can use it in the -following way: +There are special generators +* `Gen.buildableOf`, that generates any buildable collection +* `Gen.containerOf`, a convenience method for single parameter buildable like lists and arrays + +They take another generator as argument, that is responsible for generating the individual items. +You can use it in the following way: ```scala -val genIntList = Gen.containerOf[List,Int](Gen.oneOf(1, 3, 5)) +implicit genInt: Gen[Int] = Gen.oneOf(1, 3, 5) +val genIntList = Gen.containerOf[List, Int] -val genStringLazyList = Gen.containerOf[LazyList,String](Gen.alphaStr) +val genStringLazyList = Gen.containerOf[LazyList, String](Gen.alphaStr) +val genBoolArray = Gen.containerOf[Array, Boolean](Gen.const(true)) -val genBoolArray = Gen.containerOf[Array,Boolean](true) +implicit getTuple: Gen[(String, Int)] = Gen.zip(Gen.alphaStr, Gen.choose(0, 100)) +val genMap = Gen.buildableOf[Map[String, Int], (String, Int)] ``` By default, ScalaCheck supports generation of `List`, `Stream` (Scala 2.10 - -2.12, deprecated in 2.13), `LazyList` (Scala 2.13), `Set`, `Array`, and -`ArrayList` (from `java.util`). You can add support for additional containers -by adding implicit `Buildable` instances. See `Buildable.scala` for examples. +2.12, deprecated in 2.13), `LazyList` (Scala 2.13), `Set`, `Array` and `Map`. +Additionally, ScalaCheck can generate `java.util.ArrayList` and `java.util.HashMap` when an implicit +`Traversable` conversion evidence is in scope. + +You can add support for additional containers by adding implicit `Buildable` instances. +See `Buildable.scala` for examples. There is also `Gen.nonEmptyContainerOf` for generating non-empty containers, and `Gen.containerOfN` for generating containers of a given size. To generate a container by picking an arbitrary number of elements use -`Gen.someOf`, or by picking one or more elements with -`Gen.atLeastOne`. +`Gen.someOf`, or by picking one or more elements with `Gen.atLeastOne`. ```scala val zeroOrMoreDigits = Gen.someOf(1 to 9) From 4fc914c5bae8d5358e343d3168be3a7a1a6077a9 Mon Sep 17 00:00:00 2001 From: Michel Davit Date: Wed, 22 May 2024 09:34:34 +0200 Subject: [PATCH 3/3] Update user guide --- doc/UserGuide.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/UserGuide.md b/doc/UserGuide.md index 95e45d784..40a399d0d 100644 --- a/doc/UserGuide.md +++ b/doc/UserGuide.md @@ -478,14 +478,13 @@ They take another generator as argument, that is responsible for generating the You can use it in the following way: ```scala -implicit genInt: Gen[Int] = Gen.oneOf(1, 3, 5) -val genIntList = Gen.containerOf[List, Int] - +val genInt: Gen[Int] = Gen.oneOf(1, 3, 5) +val genIntList = Gen.containerOf[List, Int](genInt) val genStringLazyList = Gen.containerOf[LazyList, String](Gen.alphaStr) val genBoolArray = Gen.containerOf[Array, Boolean](Gen.const(true)) -implicit getTuple: Gen[(String, Int)] = Gen.zip(Gen.alphaStr, Gen.choose(0, 100)) -val genMap = Gen.buildableOf[Map[String, Int], (String, Int)] +val genTuple: Gen[(String, Int)] = Gen.zip(Gen.alphaStr, Gen.choose(0, 100)) +val genMap = Gen.buildableOf[Map[String, Int], (String, Int)](genTuple) ``` By default, ScalaCheck supports generation of `List`, `Stream` (Scala 2.10 -