in ratatool-scalacheck/src/main/scala/com/spotify/ratatool/scalacheck/AvroGenerator.scala [137:288]
private def avroValueOf(schema: Schema)(implicit
data: GenericData,
stringableGens: Map[Class[_], Gen[_]] = Map.empty
): Gen[Any] = {
import scala.jdk.CollectionConverters._
val conversion = for {
logicalType <- Option(schema.getLogicalType)
conversion <- Option(data.getConversionFor(logicalType))
} yield conversion
schema.getType match {
case Schema.Type.RECORD =>
val recordData = data match {
case specificData: SpecificData => dataForClass(specificData.getClass(schema))
case _ => data
}
val record = for {
fields <- Gen.sequence[List[(Int, Any)], (Int, Any)](schema.getFields.asScala.map { f =>
avroValueOf(f.schema())(recordData, stringableGens).map(v => f.pos() -> v)
})
} yield fields.foldLeft(recordData.newRecord(null, schema).asInstanceOf[IndexedRecord]) {
case (r, (idx, v)) =>
r.put(idx, v)
r
}
conversion match {
case Some(c) => record.map(r => c.fromRecord(r, schema, schema.getLogicalType))
case None => record
}
case Schema.Type.UNION =>
val types = schema.getTypes.asScala
for {
i <- Gen.choose(0, types.size - 1)
t <- avroValueOf(types(i))
} yield t
case Schema.Type.ARRAY =>
import org.scalacheck.util.Buildable._
implicit val tt: util.ArrayList[Any] => Traversable[Any] = _.asScala
val array = Gen.containerOf[util.ArrayList, Any](avroValueOf(schema.getElementType))
conversion match {
case Some(c) => array.map(a => c.fromArray(a, schema, schema.getLogicalType))
case None => array
}
case Schema.Type.ENUM =>
for {
symbol <- Gen.oneOf(schema.getEnumSymbols.asScala)
} yield conversion match {
case Some(c) =>
val enumSymbol = new GenericData.EnumSymbol(schema, symbol)
c.fromEnumSymbol(enumSymbol, schema, schema.getLogicalType)
case None =>
data.createEnum(symbol, schema)
}
case Schema.Type.MAP =>
import HashMapBuildable._
Option(schema.getProp(SpecificData.KEY_CLASS_PROP)) match {
case Some(cls) =>
Gen.buildableOf[util.HashMap[Any, Any], (Any, Any)](
(genStringable(cls), avroValueOf(schema.getValueType)).tupled
)
case None =>
val map = Gen.buildableOf[util.HashMap[CharSequence, Any], (CharSequence, Any)](
(genAvroString(schema), avroValueOf(schema.getValueType)).tupled
)
conversion match {
case Some(c) => map.map(m => c.fromMap(m, schema, schema.getLogicalType))
case None => map
}
}
case Schema.Type.FIXED =>
for {
bytes <- Gen.listOfN(schema.getFixedSize, Arbitrary.arbByte.arbitrary).map(_.toArray)
} yield conversion match {
case Some(c) =>
val fixed = new GenericData.Fixed(schema, bytes)
c.fromFixed(fixed, schema, schema.getLogicalType)
case None => data.createFixed(null, bytes, schema)
}
case Schema.Type.STRING =>
Option(schema.getProp(SpecificData.CLASS_PROP)) match {
case Some(cls) => genStringable(cls)
case None =>
val str = genAvroString(schema)
conversion match {
case Some(c) => str.map(cs => c.fromCharSequence(cs, schema, schema.getLogicalType))
case None => str
}
}
case Schema.Type.BYTES =>
val bytes = for {
n <- boundedLengthGen
bs <- Gen.listOfN(n, Arbitrary.arbByte.arbitrary)
} yield ByteBuffer.wrap(bs.toArray)
conversion match {
case Some(c) =>
val bs = schema.getLogicalType match {
case dt: LogicalTypes.Decimal =>
// we can't convert random bytes
val max = BigInt(10).pow(dt.getPrecision) - 1
Gen.choose(-max, max).map(bs => ByteBuffer.wrap(bs.toByteArray))
case _ =>
bytes
}
bs.map(b => c.asInstanceOf[Conversion[Any]].fromBytes(b, schema, schema.getLogicalType))
case None => bytes
}
case Schema.Type.INT =>
val int = Arbitrary.arbInt.arbitrary
conversion match {
case Some(c) => int.map(i => c.fromInt(i, schema, schema.getLogicalType))
case None => int
}
case Schema.Type.LONG =>
val long = Arbitrary.arbLong.arbitrary
conversion match {
case Some(c) => long.map(l => c.fromLong(l, schema, schema.getLogicalType))
case None => long
}
case Schema.Type.FLOAT =>
val float = Arbitrary.arbFloat.arbitrary
conversion match {
case Some(c) => float.map(f => c.fromFloat(f, schema, schema.getLogicalType))
case None => float
}
case Schema.Type.DOUBLE =>
val double = Arbitrary.arbDouble.arbitrary
conversion match {
case Some(c) => double.map(d => c.fromDouble(d, schema, schema.getLogicalType))
case None => double
}
case Schema.Type.BOOLEAN =>
val bool = Arbitrary.arbBool.arbitrary
conversion match {
case Some(c) => bool.map(b => c.fromBoolean(b, schema, schema.getLogicalType))
case None => bool
}
case Schema.Type.NULL => Gen.const(null)
}