Test arbitrary code containing Arrays with uTest

53 Views Asked by At

uTest has a hack in utest.asserts.Asserts to compare two Arrays with the ==> comparator. But how can I compare Arrays in arbitrary code?

Update with more precise question: What is the easiest way to test arbitrary code containing Arrays with uTest?

2

There are 2 best solutions below

2
On BEST ANSWER

You can supply your own ==> comparator that checks arbitrary code where Arrays have been substituted with Lists. Let your test class extend a trait like this when you have tests involving Arrays:

trait Array2List {

  // Allow comparing Arrays

  implicit class ArrowAssert(lhs: Any) {
    def ==>[V](rhs: V): Unit = {
      val (left, right) = (r(lhs), r(rhs))
      Predef.assert(
        left == right,
        s"""(Arrays converted to Lists)
           |Got     : $left
           |Expected: $right
           |""".stripMargin
      )
    }
  }

  // Recursively convert Arrays to Lists
  private def r(code: Any): Any = code match {
    case arr: Array[_]  => arr.toList // or recurse here too...
    case opt: Option[_] => opt.map(r)
    case seq: Seq[_]    => seq.map(r)
    case set: Set[_]    => set.map(r)
    case map: Map[_, _] => map.map { case (k, v) => (k, r(v)) }
    case tpl: Product   => tpl match {
      case Tuple1(a)                                                                 => Tuple1(a)
      case Tuple2(a, b)                                                              => Tuple2(r(a), r(b))
      case Tuple3(a, b, c)                                                           => Tuple3(r(a), r(b), r(c))
      case Tuple4(a, b, c, d)                                                        => Tuple4(r(a), r(b), r(c), r(d))
      case Tuple5(a, b, c, d, e)                                                     => Tuple5(r(a), r(b), r(c), r(d), r(e))
      case Tuple6(a, b, c, d, e, f)                                                  => Tuple6(r(a), r(b), r(c), r(d), r(e), r(f))
      case Tuple7(a, b, c, d, e, f, g)                                               => Tuple7(r(a), r(b), r(c), r(d), r(e), r(f), r(g))
      case Tuple8(a, b, c, d, e, f, g, h)                                            => Tuple8(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h))
      case Tuple9(a, b, c, d, e, f, g, h, i)                                         => Tuple9(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i))
      case Tuple10(a, b, c, d, e, f, g, h, i, j)                                     => Tuple10(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j))
      case Tuple11(a, b, c, d, e, f, g, h, i, j, k)                                  => Tuple11(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k))
      case Tuple12(a, b, c, d, e, f, g, h, i, j, k, l)                               => Tuple12(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l))
      case Tuple13(a, b, c, d, e, f, g, h, i, j, k, l, m)                            => Tuple13(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m))
      case Tuple14(a, b, c, d, e, f, g, h, i, j, k, l, m, n)                         => Tuple14(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n))
      case Tuple15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)                      => Tuple15(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o))
      case Tuple16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)                   => Tuple16(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p))
      case Tuple17(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)                => Tuple17(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q))
      case Tuple18(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, w)             => Tuple18(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q), r(w))
      case Tuple19(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, w, s)          => Tuple19(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q), r(w), r(s))
      case Tuple20(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, w, s, t)       => Tuple20(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q), r(w), r(s), r(t))
      case Tuple21(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, w, s, t, u)    => Tuple21(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q), r(w), r(s), r(t), r(u))
      case Tuple22(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, w, s, t, u, v) => Tuple22(r(a), r(b), r(c), r(d), r(e), r(f), r(g), r(h), r(i), r(j), r(k), r(l), r(m), r(n), r(o), r(p), r(q), r(w), r(s), r(t), r(u), r(v))
      case bigger                                                                    => bigger // Scala 3
    }
    // possibly more transformations to match the data types of your test code...
    case value          => value
  }
}
1
On

This example is one-liner that works, but involves creating a duplicate data structure.

def isMatch[A](array1 :Array[A], array2 :Array[A]) :Boolean =
    (array1.length == array2.length) && array1.zip(array2).forall { case (x,y) => x == y }

This example uses an Int to compare each element in place so is more effecient and follows Scala's functional immutable approach by prefering recursion over a for-loop

def isMatch[A](array1 :Array[A], array2 :Array[A]) :Boolean = {

    @annotation.tailrec
    def checkElement(i :Int) :Boolean = (i == array1.length) match {
        case true => true
        case false if array1(i) == array2(i) => checkElement(i+1)
        case false => false
    }
    
    array1.length == array2.length && checkElement(0)
}

And this is a classic for-loop implementation:

def isMatch[A](array1 :Array[A], array2 :Array[A]) :Boolean = {

    (array1.length == array2.length) match {
        case false => false
        case true =>

        var i :Int = 0
        
        while (i < array1.length && array1(i) == array2(i))
          i += 1
        
        // did comparison get to the end without failing?
        (i == array1.length)
    }
}