These are chat archives for etorreborre/specs2

3rd
May 2016
Simon Hafner
@reactormonk
May 03 2016 19:16
Is there a way to override the must be equalTo for a specific class?
Matthew de Detrich
@mdedetrich
May 03 2016 20:58
Does anyone know how to deal with recursive data structures that are created by Gen. I have something like the following
object Generators {

  val counter = new AtomicLong(10)

  def jIntGenerator = Arbitrary.arbitrary[BigInt].map(scala.json.ast.JNumber.apply)

  def jStringGenerator = Arbitrary.arbitrary[String].map(scala.json.ast.JString.apply)

  def jBooleanGenerator = Arbitrary.arbitrary[Boolean].map(scala.json.ast.JBoolean.apply)

  def jArrayGenerator: Gen[JArray] = {
    Gen.containerOf[Vector, JValue](jValueGenerator)
      .map(scala.json.ast.JArray.apply)
  }.retryUntil{x =>
    val value = counter.getAndDecrement()
    value == 0
  }

  private def jObjectTypeGenerator: Gen[(String, JValue)] = for {
    string <- Arbitrary.arbitrary[String]
    jValue <- jValueGenerator
  } yield (string, jValue)

  def jObjectGenerator: Gen[JObject] = {
    Gen.containerOf[List, (String, JValue)](jObjectTypeGenerator)
      .map(data => scala.json.ast.JObject.apply(data.toMap))
  }.retryUntil{x =>
    val value = counter.getAndDecrement()
    value == 0
  }

  def jValueGenerator: Gen[JValue] = for {
    jInt <- jIntGenerator
    jString <- jStringGenerator
    jBoolean <- jBooleanGenerator
    jArray <- jArrayGenerator
    jObject <- jObjectGenerator
  } yield {
    val ran = Seq(jInt, jString, jBoolean, jArray, jObject)
    ran(Random.nextInt(ran.size))
  }

  implicit val arbitraryJValue = Arbitrary(jValueGenerator)

}
I know that retryUntil is meant to be side effect free, however what I am trying to do is to make sure that the jValueGenerator eventually terminates (since a JValue is recursive in nature it will call itself when creating a JObject/JArray)
The idea for the counter is to make sure that eventually the Gen will terminate. Currently it doesn’t work, but does anyone know how to simulate what I am trying to do?
Matthew de Detrich
@mdedetrich
May 03 2016 21:02
@reactormonk Thank you!
Matthew de Detrich
@mdedetrich
May 03 2016 21:19
@reactormonk Man you made my day
Simon Hafner
@reactormonk
May 03 2016 21:22
@mdedetrich :D
Matthew de Detrich
@mdedetrich
May 03 2016 21:23
Is there a guide anywhere on how this works in regards to Specs2?
Simon Hafner
@reactormonk
May 03 2016 21:23
Just use scalacheck for property testing. Or convert the Arbitrary instances to Gen
Arbitrary also has shrinking for property testing, Gen probably doesn't.
Matthew de Detrich
@mdedetrich
May 03 2016 21:24
Yeah I am more interested in the Shrink/Depth, I am somewhat familiar with what Arbitrary/Gen are
Simon Hafner
@reactormonk
May 03 2016 21:25
That's for scalacheck property testing IIRC
Eric Torreborre
@etorreborre
May 03 2016 21:51
@reactormonk re. must be equalTo. Do you mean that you want your own type of equality?
Eric Torreborre
@etorreborre
May 03 2016 21:54
@mdedetrich Shrink is a typeclass describing how to reduce a counter-example in size once it has been found and Depth/Width are parameters to control the generation of the Json tree to avoid generating too large trees (or too wide). This paper: https://gupea.ub.gu.se/bitstream/2077/22087/1/gupea_2077_22087_1.pdf presents some good paragraphs on why it is not obvious to generate recursive data structures
@reactormonk I think you can "adapt" the beEqualTo matcher with ^^^: authInfo must beTypedEqualTo(oAuthInfo.as[OAuth2Info]) ^^^ normalizeTime
Simon Hafner
@reactormonk
May 03 2016 21:57
@etorreborre and the beTypedEqualTo is your personal recommendation?
Matthew de Detrich
@mdedetrich
May 03 2016 21:58
@etorreborre Thanks, I will have a read of it
Eric Torreborre
@etorreborre
May 03 2016 21:59
@reactormonk also remember that it is pretty cheap to create new matchers for a given domain:
def be_~=(expected: OAuth2Info): Matcher[OAuth2Info] = { actual: OAuth2Info => 
  (normalizeTime(expected) == normalizeTime(actual), s"$actual does not equal $expected")
}
@reactormonk I'm not sure it works with the regular beEqualTo which accepts Any values (and is using equals under the hood)
Simon Hafner
@reactormonk
May 03 2016 22:10
@etorreborre Apparently the correct way is to go via a clock trait for mocking...
Eric Torreborre
@etorreborre
May 03 2016 22:15
@reactormonk what makes you say that?
Eric Torreborre
@etorreborre
May 03 2016 22:30
@reactormonk I see