diff --git a/.travis.yml b/.travis.yml index 1fa75959c..9f68a31a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ language: scala scala: - 2.10.4 - - 2.9.3 + - 2.11.2 diff --git a/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala b/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala index 54e70e1c0..1f382faa0 100644 --- a/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala +++ b/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala @@ -24,6 +24,7 @@ import org.apache.avro.generic.{ GenericDatumReader, GenericDatumWriter, Generic import org.apache.avro.Schema import org.apache.avro.io.{ DecoderFactory, DatumReader, EncoderFactory, DatumWriter } import Injection.utf8 +import scala.reflect._ /** * Factory providing various avro injections. @@ -37,8 +38,8 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def apply[T <: SpecificRecordBase: Manifest]: Injection[T, Array[Byte]] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + def apply[T <: SpecificRecordBase: ClassTag]: Injection[T, Array[Byte]] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] new SpecificAvroCodec[T](klass) } @@ -49,8 +50,8 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def withCompression[T <: SpecificRecordBase: Manifest](codecFactory: CodecFactory): Injection[T, Array[Byte]] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + def withCompression[T <: SpecificRecordBase: ClassTag](codecFactory: CodecFactory): Injection[T, Array[Byte]] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] new SpecificAvroCodec[T](klass, Some(codecFactory)) } @@ -60,7 +61,7 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def withBzip2Compression[T <: SpecificRecordBase: Manifest]: Injection[T, Array[Byte]] = + def withBzip2Compression[T <: SpecificRecordBase: ClassTag]: Injection[T, Array[Byte]] = withCompression(CodecFactory.bzip2Codec()) /** @@ -71,7 +72,7 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def withDeflateCompression[T <: SpecificRecordBase: Manifest](compressionLevel: Int): Injection[T, Array[Byte]] = { + def withDeflateCompression[T <: SpecificRecordBase: ClassTag](compressionLevel: Int): Injection[T, Array[Byte]] = { require(1 <= compressionLevel && compressionLevel <= 9, "Compression level should be between 1 and 9, inclusive") withCompression(CodecFactory.deflateCodec(compressionLevel)) } @@ -84,7 +85,7 @@ object SpecificAvroCodecs { */ // Allows to create deflate-compressing Injection's without requiring parentheses similar to `apply`, // `withSnappyCompression`, etc. to achieve API consistency. - def withDeflateCompression[T <: SpecificRecordBase: Manifest]: Injection[T, Array[Byte]] = withDeflateCompression(5) + def withDeflateCompression[T <: SpecificRecordBase: ClassTag]: Injection[T, Array[Byte]] = withDeflateCompression(5) /** * Returns Injection capable of serializing and deserializing a compiled Avro record using SpecificDatumWriter and @@ -92,7 +93,7 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def withSnappyCompression[T <: SpecificRecordBase: Manifest]: Injection[T, Array[Byte]] = + def withSnappyCompression[T <: SpecificRecordBase: ClassTag]: Injection[T, Array[Byte]] = withCompression(CodecFactory.snappyCodec()) /** @@ -100,8 +101,8 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def toBinary[T <: SpecificRecordBase: Manifest]: Injection[T, Array[Byte]] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + def toBinary[T <: SpecificRecordBase: ClassTag]: Injection[T, Array[Byte]] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] val writer = new SpecificDatumWriter[T](klass) val reader = new SpecificDatumReader[T](klass) new BinaryAvroCodec[T](writer, reader) @@ -113,8 +114,8 @@ object SpecificAvroCodecs { * @tparam T compiled Avro record * @return Injection */ - def toJson[T <: SpecificRecordBase: Manifest](schema: Schema): Injection[T, String] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + def toJson[T <: SpecificRecordBase: ClassTag](schema: Schema): Injection[T, String] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] val writer = new SpecificDatumWriter[T](klass) val reader = new SpecificDatumReader[T](klass) new JsonAvroCodec[T](schema, writer, reader) @@ -139,7 +140,7 @@ object GenericAvroCodecs { * @tparam T generic record * @return Injection */ - def withCompression[T <: GenericRecord: Manifest](schema: Schema, codecFactory: CodecFactory): Injection[T, Array[Byte]] = + def withCompression[T <: GenericRecord: ClassTag](schema: Schema, codecFactory: CodecFactory): Injection[T, Array[Byte]] = new GenericAvroCodec[T](schema, Some(codecFactory)) /** @@ -148,7 +149,7 @@ object GenericAvroCodecs { * @tparam T generic record * @return Injection */ - def withBzip2Compression[T <: GenericRecord: Manifest](schema: Schema): Injection[T, Array[Byte]] = + def withBzip2Compression[T <: GenericRecord: ClassTag](schema: Schema): Injection[T, Array[Byte]] = withCompression(schema, CodecFactory.bzip2Codec()) /** @@ -159,7 +160,7 @@ object GenericAvroCodecs { * @tparam T generic record * @return Injection */ - def withDeflateCompression[T <: GenericRecord: Manifest](schema: Schema, compressionLevel: Int = 5): Injection[T, Array[Byte]] = { + def withDeflateCompression[T <: GenericRecord: ClassTag](schema: Schema, compressionLevel: Int = 5): Injection[T, Array[Byte]] = { require(1 <= compressionLevel && compressionLevel <= 9, "Compression level should be between 1 and 9, inclusive") withCompression(schema, CodecFactory.deflateCodec(compressionLevel)) } @@ -170,7 +171,7 @@ object GenericAvroCodecs { * @tparam T generic record * @return Injection */ - def withSnappyCompression[T <: GenericRecord: Manifest](schema: Schema): Injection[T, Array[Byte]] = + def withSnappyCompression[T <: GenericRecord: ClassTag](schema: Schema): Injection[T, Array[Byte]] = withCompression(schema, CodecFactory.snappyCodec()) /** diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecLaws.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecLaws.scala index da3f3d57a..1985abb6c 100644 --- a/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecLaws.scala +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecLaws.scala @@ -15,7 +15,9 @@ package com.twitter.bijection.avro import com.twitter.bijection.{ BaseProperties, Injection } -import org.scalacheck.Properties +import org.scalatest.PropSpec +import org.scalatest.prop.PropertyChecks + import org.apache.avro.generic.{ GenericRecord, GenericData } import org.apache.avro.Schema @@ -23,7 +25,7 @@ import org.apache.avro.Schema * @author Muhammad Ashraf * @since 7/5/13 */ -object GenericAvroCodecLaws extends Properties("GenericAvroCodecs") with BaseProperties { +class GenericAvroCodecLaws extends PropSpec with PropertyChecks with BaseProperties { val testSchema = new Schema.Parser().parse("""{ "type":"record", "name":"FiscalRecord", @@ -71,13 +73,16 @@ object GenericAvroCodecLaws extends Properties("GenericAvroCodecs") with BasePro isLooseInjection[GenericRecord, String] } - property("round trips Generic Record -> Array[Byte]") = + property("round trips Generic Record -> Array[Byte]") { roundTripsGenericRecord(GenericAvroCodecs[GenericRecord](testSchema)) + } - property("round trips Generic Record -> Array[Byte] using Binary Encoder/Decoder") = + property("round trips Generic Record -> Array[Byte] using Binary Encoder/Decoder") { roundTripsGenericRecord(GenericAvroCodecs.toBinary[GenericRecord](testSchema)) + } - property("round trips Generic Record -> String using Json Encoder/Decoder") = + property("round trips Generic Record -> String using Json Encoder/Decoder") { roundTripsGenericRecordToJson(GenericAvroCodecs.toJson[GenericRecord](testSchema)) + } } diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecsSpecification.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecsSpecification.scala index 23257b492..4740e86e3 100644 --- a/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecsSpecification.scala +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/GenericAvroCodecsSpecification.scala @@ -14,7 +14,7 @@ limitations under the License. */ package com.twitter.bijection.avro -import org.specs.Specification +import org.scalatest._ import com.twitter.bijection.{ Injection, BaseProperties } import org.apache.avro.Schema import avro.FiscalRecord @@ -24,7 +24,7 @@ import org.apache.avro.generic.{ GenericData, GenericRecord } * @author Muhammad Ashraf * @since 7/6/13 */ -object GenericAvroCodecsSpecification extends Specification with BaseProperties { +object GenericAvroCodecsSpecification extends WordSpec with Matchers with BaseProperties { val testSchema = new Schema.Parser().parse("""{ "type":"record", "name":"FiscalRecord", @@ -58,7 +58,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip generic record using Generic Injection with Bzip2 compression" in { @@ -66,7 +66,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip generic record using Generic Injection with Deflate compression (default compression level)" in { @@ -74,7 +74,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip generic record using Generic Injection with Deflate compression (custom compression level)" in { @@ -82,15 +82,15 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Cannot create Generic Injection with Deflate compression if compression level is set too low" in { - GenericAvroCodecs.withDeflateCompression[FiscalRecord](testSchema, 0) must throwA[IllegalArgumentException] + an[IllegalArgumentException] should be thrownBy GenericAvroCodecs.withDeflateCompression[FiscalRecord](testSchema, 0) } "Cannot create Generic Injection with Deflate compression if compression level is set too high" in { - GenericAvroCodecs.withDeflateCompression[FiscalRecord](testSchema, 10) must throwA[IllegalArgumentException] + an[IllegalArgumentException] should be thrownBy GenericAvroCodecs.withDeflateCompression[FiscalRecord](testSchema, 10) } "Round trip generic record using Generic Injection with Snappy compression" in { @@ -98,7 +98,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip generic record using Binary Injection" in { @@ -106,7 +106,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[GenericRecord, Array[Byte]](testRecord) val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip generic record using Json Injection" in { @@ -114,7 +114,7 @@ object GenericAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) val jsonString = Injection[GenericRecord, String](testRecord) val attempt = Injection.invert[GenericRecord, String](jsonString) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } } diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecLaws.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecLaws.scala index a1f856672..1dbbcea61 100644 --- a/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecLaws.scala +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecLaws.scala @@ -1,6 +1,7 @@ package com.twitter.bijection.avro -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks import com.twitter.bijection.{ Injection, BaseProperties } import org.apache.avro.Schema import avro.FiscalRecord @@ -9,7 +10,7 @@ import avro.FiscalRecord * @author Muhammad Ashraf * @since 10/5/13 */ -object SpecificAvroCodecLaws extends Properties("SpecificAvroCodecs") with BaseProperties { +class SpecificAvroCodecLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { val testSchema = new Schema.Parser().parse("""{ "type":"record", "name":"FiscalRecord", @@ -56,14 +57,17 @@ object SpecificAvroCodecLaws extends Properties("SpecificAvroCodecs") with BaseP isLooseInjection[FiscalRecord, String] } - property("round trips Specific Record -> Array[Byte]") = + property("round trips Specific Record -> Array[Byte]") { roundTripsSpecificRecord(SpecificAvroCodecs[FiscalRecord]) + } - property("round trips Specific Record -> Array[Byte] using Binary Encoder/Decoder") = + property("round trips Specific Record -> Array[Byte] using Binary Encoder/Decoder") { roundTripsSpecificRecord(SpecificAvroCodecs.toBinary[FiscalRecord]) + } - property("round trips Specific Record -> String using Json Encoder/Decoder") = + property("round trips Specific Record -> String using Json Encoder/Decoder") { roundTripsSpecificRecordToJson(SpecificAvroCodecs.toJson[FiscalRecord](testSchema)) + } } diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecsSpecification.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecsSpecification.scala index 18b01622e..6b027b568 100644 --- a/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecsSpecification.scala +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/SpecificAvroCodecsSpecification.scala @@ -1,6 +1,6 @@ package com.twitter.bijection.avro -import org.specs.Specification +import org.scalatest._ import com.twitter.bijection.{ Injection, BaseProperties } import org.apache.avro.Schema import avro.FiscalRecord @@ -9,7 +9,7 @@ import avro.FiscalRecord * @author Muhammad Ashraf * @since 10/5/13 */ -object SpecificAvroCodecsSpecification extends Specification with BaseProperties { +object SpecificAvroCodecsSpecification extends WordSpec with Matchers with BaseProperties { val testSchema = new Schema.Parser().parse("""{ "type":"record", "name":"FiscalRecord", @@ -43,7 +43,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip specific record using Specific Injection with Bzip2 compression" in { @@ -51,7 +51,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip specific record using Specific Injection with Deflate compression (default compression level)" in { @@ -59,7 +59,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip specific record using Specific Injection with Deflate compression (custom compression level)" in { @@ -67,15 +67,15 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Cannot create Specific Injection with Deflate compression if compression level is set too low" in { - SpecificAvroCodecs.withDeflateCompression[FiscalRecord](0) must throwA[IllegalArgumentException] + an[IllegalArgumentException] should be thrownBy SpecificAvroCodecs.withDeflateCompression[FiscalRecord](0) } "Cannot create Specific Injection with Deflate compression if compression level is set too high" in { - SpecificAvroCodecs.withDeflateCompression[FiscalRecord](10) must throwA[IllegalArgumentException] + an[IllegalArgumentException] should be thrownBy SpecificAvroCodecs.withDeflateCompression[FiscalRecord](10) } "Round trip specific record using Specific Injection with Snappy compression" in { @@ -83,7 +83,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip specific record using Binary Injection" in { @@ -91,7 +91,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } "Round trip specific record using Json Injection" in { @@ -99,7 +99,7 @@ object SpecificAvroCodecsSpecification extends Specification with BaseProperties val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) val jsonString = Injection[FiscalRecord, String](testRecord) val attempt = Injection.invert[FiscalRecord, String](jsonString) - attempt.get must_== testRecord + assert(attempt.get == testRecord) } } diff --git a/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala b/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala index 74a82ddd9..098f5e642 100644 --- a/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala +++ b/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala @@ -17,8 +17,10 @@ package com.twitter.bijection.clojure import com.twitter.bijection.{ BaseProperties, Bijection } -import org.scalacheck.{ Arbitrary, Properties } +import org.scalacheck.{ Arbitrary } +import org.scalatest.PropSpec +import org.scalatest.prop.PropertyChecks -object ClojureBijectionLaws extends Properties("ClojureBijections") with BaseProperties { +class ClojureBijectionLaws extends PropSpec with PropertyChecks with BaseProperties { // TODO: Fill in tests. } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala b/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala index 0c9f94c13..9c14da7fb 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala @@ -18,6 +18,7 @@ package com.twitter.bijection import java.io.Serializable import scala.annotation.implicitNotFound +import scala.reflect.ClassTag /** * A Bijection[A, B] is a pair of functions that transform an element between @@ -152,8 +153,8 @@ object Bijection extends CollectionBijections implicit def swap[T, U]: Bijection[(T, U), (U, T)] = SwapBijection[T, U] - def subclass[A, B <: A](afn: A => B)(implicit mf: ClassManifest[B]): Bijection[A, B] = - new SubclassBijection[A, B](mf.erasure.asInstanceOf[Class[B]]) { + def subclass[A, B <: A](afn: A => B)(implicit ct: ClassTag[B]): Bijection[A, B] = + new SubclassBijection[A, B](ct.runtimeClass.asInstanceOf[Class[B]]) { def applyfn(a: A) = afn(a) } } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala b/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala index aa52b36e2..738e55ff5 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala @@ -41,6 +41,7 @@ trait Bufferable[T] extends Serializable { get(from) match { case Success(tup @ _) => tup case Failure(InversionFailure(_, t)) => throw t + case Failure(t) => throw t } } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/ClassInjection.scala b/bijection-core/src/main/scala/com/twitter/bijection/ClassInjection.scala index 0038489a3..5182f90c2 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/ClassInjection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/ClassInjection.scala @@ -17,6 +17,7 @@ limitations under the License. package com.twitter.bijection import com.twitter.bijection.Inversion.attempt +import scala.reflect.ClassTag /** * Injection between Class objects and string. @@ -33,8 +34,8 @@ class ClassInjection[T] extends AbstractInjection[Class[T], String] { * the inner items will not be correct. This is intended for experts. */ object CastInjection { - def of[A, B >: A](implicit cmf: ClassManifest[A]): Injection[A, B] = new AbstractInjection[A, B] { - private val cls = cmf.erasure.asInstanceOf[Class[A]] + def of[A, B >: A](implicit cmf: ClassTag[A]): Injection[A, B] = new AbstractInjection[A, B] { + private val cls = cmf.runtimeClass.asInstanceOf[Class[A]] def apply(a: A) = a def invert(b: B) = attempt(b)(cls.cast(_)) } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/CollectionBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/CollectionBijections.scala index bb4d667a5..472773e61 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/CollectionBijections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/CollectionBijections.scala @@ -30,6 +30,7 @@ import java.util.concurrent.{ ConcurrentMap => JConcurrentMap } import scala.collection.JavaConverters._ import scala.collection.mutable import collection.generic.CanBuildFrom +import scala.reflect.ClassTag trait CollectionBijections extends BinaryBijections { import Conversion.asMethod @@ -184,7 +185,7 @@ trait CollectionBijections extends BinaryBijections { /** * This doesn't actually copy the Array, only wraps/unwraps with WrappedArray */ - implicit def array2Traversable[T: ClassManifest]: Bijection[Array[T], Traversable[T]] = + implicit def array2Traversable[T: ClassTag]: Bijection[Array[T], Traversable[T]] = new AbstractBijection[Array[T], Traversable[T]] { override def apply(a: Array[T]) = a.toTraversable override def invert(t: Traversable[T]) = t.toArray @@ -193,7 +194,7 @@ trait CollectionBijections extends BinaryBijections { /** * This doesn't actually copy the Array, only wraps/unwraps with WrappedArray */ - implicit def array2Seq[T: ClassManifest]: Bijection[Array[T], Seq[T]] = + implicit def array2Seq[T: ClassTag]: Bijection[Array[T], Seq[T]] = new AbstractBijection[Array[T], Seq[T]] { override def apply(a: Array[T]) = a.toSeq override def invert(t: Seq[T]) = t.toArray diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala b/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala index c776c46ae..163c238c5 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala @@ -20,6 +20,7 @@ import java.io.Serializable import scala.annotation.implicitNotFound import scala.util.{ Failure, Success, Try } import com.twitter.bijection.Inversion.attempt +import scala.reflect.ClassTag /** * An Injection[A, B] is a function from A to B, and from some B back to A. @@ -173,7 +174,7 @@ object Injection extends CollectionInjections * due to the above. Only use this in instances where A has no type parameters, or you can prove * that the cast from B to A succeeding is enough to prove correctness. */ - def subclass[A, B >: A](implicit cmf: ClassManifest[A]): Injection[A, B] = CastInjection.of[A, B] + def subclass[A, B >: A](implicit cmf: ClassTag[A]): Injection[A, B] = CastInjection.of[A, B] /** * Get a partial from B => D from injections and a function from A => C diff --git a/bijection-core/src/main/scala/com/twitter/bijection/JavaSerializationInjection.scala b/bijection-core/src/main/scala/com/twitter/bijection/JavaSerializationInjection.scala index 3028cd23a..a46be05b5 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/JavaSerializationInjection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/JavaSerializationInjection.scala @@ -20,13 +20,14 @@ import java.io._ import scala.util.{ Failure, Try } import scala.util.control.Exception.allCatch import com.twitter.bijection.Inversion.attempt +import scala.reflect.ClassTag object JavaSerializationInjection extends Serializable { /** * Construct a new JavaSerializationInjection instance */ - def apply[T <: Serializable](implicit cmf: ClassManifest[T]): JavaSerializationInjection[T] = { - val cls = cmf.erasure.asInstanceOf[Class[T]] + def apply[T <: Serializable](implicit ct: ClassTag[T]): JavaSerializationInjection[T] = { + val cls = ct.runtimeClass.asInstanceOf[Class[T]] new JavaSerializationInjection[T](cls) } } diff --git a/bijection-core/src/test/java/com/twitter/bijection/TestBijectionInJava.java b/bijection-core/src/test/java/com/twitter/bijection/TestBijectionInJava.java index 024689644..c64e43c2d 100644 --- a/bijection-core/src/test/java/com/twitter/bijection/TestBijectionInJava.java +++ b/bijection-core/src/test/java/com/twitter/bijection/TestBijectionInJava.java @@ -1,6 +1,7 @@ package com.twitter.bijection; import org.junit.Test; +import org.scalatest.junit.JUnitSuite; import scala.util.Failure; import scala.util.Success; import scala.util.Try; @@ -10,7 +11,7 @@ * Bijection is as useful in Java as in Scala, so these tests ensure correct * functionality while providing an example of use and implementation from Java. */ -public class TestBijectionInJava { +public class TestBijectionInJava extends JUnitSuite { @Test public void testBasicBijection() { Bijection s2l = new StringToLong(); diff --git a/bijection-core/src/test/scala/com/twitter/bijection/AsSyntax.scala b/bijection-core/src/test/scala/com/twitter/bijection/AsSyntax.scala index a4851bdbe..89f9a9927 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/AsSyntax.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/AsSyntax.scala @@ -16,25 +16,23 @@ limitations under the License. package com.twitter.bijection -import org.specs._ +import org.scalatest._ import scala.util.Success -class AsSyntax extends Specification { - noDetailedDiffs() - +class AsSyntax extends WordSpec with Matchers { import Conversion.asMethod "As syntax" should { "work on injections" in { val listAry = (1, 2).as[List[Array[Byte]]] - Injection.connect[(Int, Int), List[Array[Byte]]].invert(listAry) must be_==(Success(1 -> 2)) + Injection.connect[(Int, Int), List[Array[Byte]]].invert(listAry) should be(Success(1 -> 2)) } "work on bijections" in { - List(1, 2, 3).as[Vector[Int]] must be_==(Vector(1, 2, 3)) + List(1, 2, 3).as[Vector[Int]] should be (Vector(1, 2, 3)) } "work on functions" in { implicit def toS(i: Int): String = i.toString - 23.as[String] must be_==("23") + 23.as[String] should be ("23") } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/BaseProperties.scala b/bijection-core/src/test/scala/com/twitter/bijection/BaseProperties.scala index 7404d3e39..cce460a51 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/BaseProperties.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/BaseProperties.scala @@ -16,7 +16,10 @@ limitations under the License. package com.twitter.bijection -import org.scalacheck.{ Arbitrary, Properties } +import org.scalacheck.Arbitrary +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Prop.forAll import scala.math.Equiv diff --git a/bijection-core/src/test/scala/com/twitter/bijection/BinaryBijectionLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/BinaryBijectionLaws.scala index 511a258b5..223fc100a 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/BinaryBijectionLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/BinaryBijectionLaws.scala @@ -17,27 +17,36 @@ limitations under the License. package com.twitter.bijection import java.nio.ByteBuffer -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Prop._ -object BinaryBijectionLaws extends Properties("BinaryBijections") +class BinaryBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { implicit val arbBB = arbitraryViaFn[Array[Byte], ByteBuffer] { ByteBuffer.wrap(_) } // TODO: These are all bijections, - property("Array[Byte] <=> ByteBuffer") = + property("Array[Byte] <=> ByteBuffer") { isBijection[Array[Byte], ByteBuffer] + } // These are trivially bijecitons because the right-side is only defined as the image of the left: - property("rts Array[Byte] -> GZippedBytes") = + property("rts Array[Byte] -> GZippedBytes") { isInjective[Array[Byte], GZippedBytes] - property("rts Array[Byte] -> Base64String") = + } + + property("rts Array[Byte] -> Base64String") { isInjective[Array[Byte], Base64String] - property("rts Array[Byte] -> GZippedBase64String") = + } + + property("rts Array[Byte] -> GZippedBase64String") { isInjective[Array[Byte], GZippedBase64String] + } implicit val optSer = JavaSerializationInjection[Option[Int]] - property("java serialize Option[Int]") = + property("java serialize Option[Int]") { isInjection[Option[Int], Array[Byte]] + } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/BufferableLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/BufferableLaws.scala index 398fa8fba..6b8cf4be0 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/BufferableLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/BufferableLaws.scala @@ -27,7 +27,9 @@ import java.lang.{ import java.nio.ByteBuffer -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Arbitrary import org.scalacheck.Prop.forAll @@ -49,44 +51,99 @@ trait BaseBufferable { } } -object BufferableLaws extends Properties("Bufferable") with BaseBufferable { - property("Reallocate works properly") = forAll { (bytes: Array[Byte]) => - val bb = ByteBuffer.wrap(bytes) - bb.position(bytes.size) - val newBb = Bufferable.reallocate(bb) - (Bufferable.getBytes(bb).toList == Bufferable.getBytes(newBb).toList) && - (newBb.capacity > bb.capacity) && - (newBb.position == bb.position) +class BufferableLaws extends PropSpec with PropertyChecks with MustMatchers with BaseBufferable { + property("Reallocate works properly") { + forAll { (bytes: Array[Byte]) => + val bb = ByteBuffer.wrap(bytes) + bb.position(bytes.size) + val newBb = Bufferable.reallocate(bb) + assert(Bufferable.getBytes(bb).toList == Bufferable.getBytes(newBb).toList) + assert(newBb.capacity > bb.capacity) + assert(newBb.position == bb.position) + } } - property("getBytes works") = forAll { (bytes: Array[Byte]) => - val bb = ByteBuffer.wrap(bytes) - bb.position(bytes.size) - Bufferable.getBytes(bb).toList == bytes.toList + + property("getBytes works") { + forAll { (bytes: Array[Byte]) => + val bb = ByteBuffer.wrap(bytes) + bb.position(bytes.size) + assert(Bufferable.getBytes(bb).toList == bytes.toList) + } } implicit val laeq = itereq[List[Array[Byte]], Array[Byte]] - property("Bools roundtrip") = roundTrips[Boolean] - property("Ints roundtrip") = roundTrips[Int] - property("Doubles roundtrip") = roundTrips[Double] - property("Floats roundtrip") = roundTrips[Float] - property("Shorts roundtrip") = roundTrips[Int] - property("Longs roundtrip") = roundTrips[Long] - property("(Int,Long) roundtrip") = roundTrips[(Int, Long)] - property("(Int,Long,String) roundtrip") = roundTrips[(Int, Long, String)] - property("(Int,Long,String,(Int,Long)) roundtrip") = roundTrips[(Int, Long, String, (Int, Long))] - property("Array[Byte] roundtrip") = roundTrips[Array[Byte]] - property("Array[Int] roundtrip") = roundTrips[Array[Int]] - property("List[Double] roundtrip") = roundTrips[List[Double]] - property("List[Array[Byte]] roundtrip") = roundTrips[List[Array[Byte]]] - property("Set[Double] roundtrip") = roundTrips[Set[Double]] - property("Map[String, Int] roundtrip") = roundTrips[Map[String, Int]] - property("Option[(Long,Long)] roundtrip") = roundTrips[Option[(Long, Long)]] - property("Either[Long,String] roundtrip") = roundTrips[Either[Long, String]] + property("Bools roundtrip") { + roundTrips[Boolean] + } + + property("Ints roundtrip") { + roundTrips[Int] + } + + property("Doubles roundtrip") { + roundTrips[Double] + } + + property("Floats roundtrip") { + roundTrips[Float] + } + property("Shorts roundtrip") { + roundTrips[Int] + } + property("Longs roundtrip") { + roundTrips[Long] + } + property("(Int,Long) roundtrip") { + roundTrips[(Int, Long)] + } + + property("(Int,Long,String) roundtrip") { + roundTrips[(Int, Long, String)] + } + + property("(Int,Long,String,(Int,Long)) roundtrip") { + roundTrips[(Int, Long, String, (Int, Long))] + } + + property("Array[Byte] roundtrip") { + roundTrips[Array[Byte]] + } + + property("Array[Int] roundtrip") { + roundTrips[Array[Int]] + } + + property("List[Double] roundtrip") { + roundTrips[List[Double]] + } + + property("List[Array[Byte]] roundtrip") { + roundTrips[List[Array[Byte]]] + } + + property("Set[Double] roundtrip") { + roundTrips[Set[Double]] + } + + property("Map[String, Int] roundtrip") { + roundTrips[Map[String, Int]] + } + + property("Option[(Long,Long)] roundtrip") { + roundTrips[Option[(Long, Long)]] + } + + property("Either[Long,String] roundtrip") { + roundTrips[Either[Long, String]] + } implicit val symbolArb = Arbitrary { implicitly[Arbitrary[String]] .arbitrary.map { Symbol(_) } } - property("Symbol roundtrip") = roundTrips[Symbol] + + property("Symbol roundtrip") { + roundTrips[Symbol] + } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/CollectionLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/CollectionLaws.scala index b737b4e68..4e2e4fef2 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/CollectionLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/CollectionLaws.scala @@ -16,12 +16,14 @@ limitations under the License. package com.twitter.bijection -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Gen._ import org.scalacheck.Arbitrary import org.scalacheck.Prop._ -object CollectionLaws extends Properties("Collections") +class CollectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import StringArbs._ @@ -34,44 +36,67 @@ object CollectionLaws extends Properties("Collections") implicit def traversableArb[A](implicit la: Arbitrary[List[A]]) = arbitraryViaFn { (l: List[A]) => l.toTraversable } - property("round trip List[Int] <=> Vector[String @@ Rep[Int]]") = + property("round trip List[Int] <=> Vector[String @@ Rep[Int]]") { isBijection[List[Int], Vector[String @@ Rep[Int]]] + } - property("round trip List[long] <=> List[String @@ Rep[Int]]") = + property("round trip List[long] <=> List[String @@ Rep[Int]]") { isBijection[List[Long], List[String @@ Rep[Long]]] + } - property("round trip Vector[Double] <=> Vector[String @@ Rep[Double]]") = + property("round trip Vector[Double] <=> Vector[String @@ Rep[Double]]") { isBijection[Vector[Double], Vector[String @@ Rep[Double]]] + } - property("round trip Set[Double] <=> Set[String @@ Rep[Double]]") = + property("round trip Set[Double] <=> Set[String @@ Rep[Double]]") { isBijection[Set[Double], Set[String @@ Rep[Double]]] + } - property("round trip Seq[Double] <=> Seq[String @@ Rep[Double]]") = + property("round trip Seq[Double] <=> Seq[String @@ Rep[Double]]") { isBijection[Seq[Double], Seq[String @@ Rep[Double]]] + } - property("round trip Map[Long,Double] <=> Map[String @@ Rep[Long], String @@ Rep[Double]]") = + property("round trip Map[Long,Double] <=> Map[String @@ Rep[Long], String @@ Rep[Double]]") { isBijection[Map[Long, Double], Map[String @@ Rep[Long], String @@ Rep[Double]]] + } - property("Option[Long] <=> Option[String @@ Rep[Long]]") = + property("Option[Long] <=> Option[String @@ Rep[Long]]") { isBijection[Option[Long], Option[String @@ Rep[Long]]] + } + + property("Array[Int] <=> Seq[Int]") { + isBijection[Array[Int], Seq[Int]] + } + + property("Array[Int] <=> Traversable[Int]") { + isBijection[Array[Int], Traversable[Int]] + } + + property("Vector[Int] <=> Seq[Int]") { + isBijection[Vector[Int], Seq[Int]] + } - property("Array[Int] <=> Seq[Int]") = isBijection[Array[Int], Seq[Int]] - property("Array[Int] <=> Traversable[Int]") = isBijection[Array[Int], Traversable[Int]] - property("Vector[Int] <=> Seq[Int]") = isBijection[Vector[Int], Seq[Int]] - property("Vector[Int] <=> IndexedSeq[Int]") = isBijection[Vector[Int], IndexedSeq[Int]] + property("Vector[Int] <=> IndexedSeq[Int]") { + isBijection[Vector[Int], IndexedSeq[Int]] + } - property("List[Int] <=> IndexedSeq[String @@ Rep[Int]]") = + property("List[Int] <=> IndexedSeq[String @@ Rep[Int]]") { isBijection[List[Int], IndexedSeq[String @@ Rep[Int]]] + } - property("List[Int] <=> Vector[String @@ Rep[Int]]") = + property("List[Int] <=> Vector[String @@ Rep[Int]]") { isBijection[List[Int], Vector[String @@ Rep[Int]]] + } - property("Option[Int] <=> Option[Long]") = + property("Option[Int] <=> Option[Long]") { isInjection[Option[Int], Option[Long]] + } - property("Map[Int, Short] -> Set[(Int, Short)]") = + property("Map[Int, Short] -> Set[(Int, Short)]") { isInjection[Map[Int, Short], Set[(Int, Short)]] + } - property("round trip Set[Int] -> List[String]") = + property("round trip Set[Int] -> List[String]") { isInjection[Set[Int], List[String]] + } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/EnglishIntLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/EnglishIntLaws.scala index 1edb3bc4a..5ea68e37b 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/EnglishIntLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/EnglishIntLaws.scala @@ -16,12 +16,14 @@ limitations under the License. package com.twitter.bijection -import org.scalacheck.{ Prop, Gen, Properties } +import org.scalacheck.{ Prop, Gen } import org.scalacheck.Prop.forAll +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks import Conversion.asMethod // get the .as syntax -object EnglishIntLaws extends Properties("EnglishIntBijections") +class EnglishIntLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { var ct = 0 @@ -34,5 +36,8 @@ object EnglishIntLaws extends Properties("EnglishIntBijections") } val (tiny, small, medium, large) = (Gen.choose(0, 100), Gen.choose(100, 1000), Gen.choose(1000, 100 * 1000), Gen.choose(100 * 1000, 1000 * 1000 * 1000)) - property("as works") = List(tiny, small, medium, large).map(test).reduceLeft((a, b) => a && b) + property("as works") { + List(tiny, small, medium, large).map(test).reduceLeft((a, b) => a && b) + } + } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/JavaTests.scala b/bijection-core/src/test/scala/com/twitter/bijection/JavaTests.scala deleted file mode 100644 index 3a671a609..000000000 --- a/bijection-core/src/test/scala/com/twitter/bijection/JavaTests.scala +++ /dev/null @@ -1,8 +0,0 @@ -package com.twitter.bijection - -import org.scalatest.junit.JUnitSuite - -/** - * This is a dummy class to ensure "sbt test" runs the JUnit tests. - */ -class JavaTests extends TestBijectionInJava with JUnitSuite \ No newline at end of file diff --git a/bijection-core/src/test/scala/com/twitter/bijection/NumericBijectionLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/NumericBijectionLaws.scala index 01d5eb252..4de82d178 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/NumericBijectionLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/NumericBijectionLaws.scala @@ -28,79 +28,172 @@ import java.lang.{ import java.math.BigInteger import java.util.UUID -import org.scalacheck.{ Gen, Properties } +import org.scalacheck.Gen import org.scalacheck.Prop.forAll +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import Conversion.asMethod // get the .as syntax -object JavaNumArbs { - import NumericBijectionLaws.{ arbitraryViaFn => viaFn } +object JavaNumArbs extends BaseProperties { - implicit val byteA = viaFn { v: Byte => JByte.valueOf(v) } - implicit val shortA = viaFn { v: Short => JShort.valueOf(v) } - implicit val longA = viaFn { v: Long => JLong.valueOf(v) } - implicit val intA = viaFn { v: Int => JInt.valueOf(v) } - implicit val floatA = viaFn { v: Float => JFloat.valueOf(v) } - implicit val doubleA = viaFn { v: Double => JDouble.valueOf(v) } - implicit val bigInteger = viaFn { (l1l2: (Long, Long)) => + implicit val byteA = arbitraryViaFn { v: Byte => JByte.valueOf(v) } + implicit val shortA = arbitraryViaFn { v: Short => JShort.valueOf(v) } + implicit val longA = arbitraryViaFn { v: Long => JLong.valueOf(v) } + implicit val intA = arbitraryViaFn { v: Int => JInt.valueOf(v) } + implicit val floatA = arbitraryViaFn { v: Float => JFloat.valueOf(v) } + implicit val doubleA = arbitraryViaFn { v: Double => JDouble.valueOf(v) } + implicit val bigInteger = arbitraryViaFn { (l1l2: (Long, Long)) => (new BigInteger(l1l2._1.toString)).multiply(new BigInteger(l1l2._2.toString)) } } -object NumericBijectionLaws extends Properties("NumericBijections") +class NumericBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import StringArbs._ import JavaNumArbs._ - property("round trips byte -> jbyte") = isBijection[Byte, JByte] - property("round trips short -> jshort") = isBijection[Short, JShort] - property("round trips int -> jint") = isBijection[Int, JInt] - property("round trips long -> jlong") = isBijection[Long, JLong] - property("round trips float -> jfloat") = isBijection[Float, JFloat] - property("round trips double -> jdouble") = isBijection[Double, JDouble] - property("round trips BigInt <-> java.math.BigInteger") = isBijection[BigInt, BigInteger] - - property("round trips byte -> string") = isBijection[Byte, String @@ Rep[Byte]] - property("round trips short -> string") = isBijection[Short, String @@ Rep[Short]] - property("round trips int -> string") = isBijection[Int, String @@ Rep[Int]] - property("round trips long -> string") = isBijection[Long, String @@ Rep[Long]] - property("round trips float -> string") = isBijection[Float, String @@ Rep[Float]] - property("round trips double -> string") = isBijection[Double, String @@ Rep[Double]] + property("round trips byte -> jbyte") { + isBijection[Byte, JByte] + } + + property("round trips short -> jshort") { + isBijection[Short, JShort] + } + + property("round trips int -> jint") { + isBijection[Int, JInt] + } + + property("round trips long -> jlong") { + isBijection[Long, JLong] + } + + property("round trips float -> jfloat") { + isBijection[Float, JFloat] + } + + property("round trips double -> jdouble") { + isBijection[Double, JDouble] + } + + property("round trips BigInt <-> java.math.BigInteger") { + isBijection[BigInt, BigInteger] + } + + property("round trips byte -> string") { + isBijection[Byte, String @@ Rep[Byte]] + } + + property("round trips short -> string") { + isBijection[Short, String @@ Rep[Short]] + } + + property("round trips int -> string") { + isBijection[Int, String @@ Rep[Int]] + } + + property("round trips long -> string") { + isBijection[Long, String @@ Rep[Long]] + } + + property("round trips float -> string") { + isBijection[Float, String @@ Rep[Float]] + } + + property("round trips double -> string") { + isBijection[Double, String @@ Rep[Double]] + } + // Embedding in larger numbers: - property("round trips Short <-> (Byte,Byte)") = isBijection[Short, (Byte, Byte)] - property("round trips Int <-> (Short,Short)") = isBijection[Int, (Short, Short)] - property("round trips Long <-> (Int,Int)") = isBijection[Long, (Int, Int)] + property("round trips Short <-> (Byte,Byte)") { + isBijection[Short, (Byte, Byte)] + } + + property("round trips Int <-> (Short,Short)") { + isBijection[Int, (Short, Short)] + } + + property("round trips Long <-> (Int,Int)") { + isBijection[Long, (Int, Int)] + } + // Upcasting: - property("byte -> short") = isInjection[Byte, Short] - property("short -> int") = isInjection[Short, Int] - property("int -> long") = isInjection[Int, Long] - property("long -> BigInt") = isInjection[Long, BigInt] - property("int -> double") = isLooseInjection[Int, Double] - property("float -> double") = isLooseInjection[Float, Double] + property("byte -> short") { + isInjection[Byte, Short] + } + + property("short -> int") { + isInjection[Short, Int] + } + + property("int -> long") { + isInjection[Int, Long] + } + + property("long -> BigInt") { + isInjection[Long, BigInt] + } + + property("int -> double") { + isLooseInjection[Int, Double] + } + + property("float -> double") { + isLooseInjection[Float, Double] + } + // ModDiv - property("Int -> (Int,Int) by ModDiv") = forAll(Gen.choose(1, Int.MaxValue)) { mod => - implicit val modDiv: Injection[Int, (Int, Int)] = new IntModDivInjection(mod) - isInjection[Int, (Int, Int)] + property("Int -> (Int,Int) by ModDiv") { + forAll(Gen.choose(1, Int.MaxValue)) { mod => + implicit val modDiv: Injection[Int, (Int, Int)] = new IntModDivInjection(mod) + isInjection[Int, (Int, Int)] + } } - property("Long -> (Long,Long) by ModDiv") = forAll(Gen.choose(1L, Long.MaxValue)) { mod => - implicit val modDiv: Injection[Long, (Long, Long)] = new LongModDivInjection(mod) - isInjection[Long, (Long, Long)] + + property("Long -> (Long,Long) by ModDiv") { + forAll(Gen.choose(1L, Long.MaxValue)) { mod => + implicit val modDiv: Injection[Long, (Long, Long)] = new LongModDivInjection(mod) + isInjection[Long, (Long, Long)] + } } // TODO need Rep[Int], etc... on the Array[Byte] - property("round trips short -> Array[Byte]") = isLooseInjection[Short, Array[Byte]] - property("round trips int -> Array[Byte]") = isLooseInjection[Int, Array[Byte]] - property("round trips long -> Array[Byte]") = isLooseInjection[Long, Array[Byte]] - property("round trips float -> Array[Byte]") = isLooseInjection[Float, Array[Byte]] - property("round trips double -> Array[Byte]") = isLooseInjection[Double, Array[Byte]] + property("round trips short -> Array[Byte]") { + isLooseInjection[Short, Array[Byte]] + } + + property("round trips int -> Array[Byte]") { + isLooseInjection[Int, Array[Byte]] + } + + property("round trips long -> Array[Byte]") { + isLooseInjection[Long, Array[Byte]] + } + + property("round trips float -> Array[Byte]") { + isLooseInjection[Float, Array[Byte]] + } + + property("round trips double -> Array[Byte]") { + isLooseInjection[Double, Array[Byte]] + } + // Some other types through numbers: implicit val uuid = arbitraryViaFn { (uplow: (Long, Long)) => new UUID(uplow._1, uplow._2) } implicit val date = arbitraryViaFn { (dtime: Long) => new java.util.Date(dtime) } - property("round trips (Long,Long) -> UUID") = isBijection[(Long, Long), UUID] - property("round trips Long -> Date") = isBijection[Long, java.util.Date] + property("round trips (Long,Long) -> UUID") { + isBijection[(Long, Long), UUID] + } + + property("round trips Long -> Date") { + isBijection[Long, java.util.Date] + } - property("as works") = forAll { (i: Int) => - // TODO: once we have the .as fix, i.as[String] should work: - (i.as[String @@ Rep[Int]] == i.toString) && (Tag[String, Rep[Int]](i.toString).as[Int] == i) + property("as works") { + forAll { (i: Int) => + assert((i.as[String @@ Rep[Int]] == i.toString) && (Tag[String, Rep[Int]](i.toString).as[Int] == i)) + } } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/StringBijectionLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/StringBijectionLaws.scala index 34b827dda..a27ba23a9 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/StringBijectionLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/StringBijectionLaws.scala @@ -16,7 +16,9 @@ limitations under the License. package com.twitter.bijection -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Gen._ import org.scalacheck.Arbitrary import org.scalacheck.Prop._ @@ -35,13 +37,18 @@ object StringArbs extends BaseProperties { implicit val strDouble = arbitraryViaBijection[Double, String @@ Rep[Double]] } -object StringBijectionLaws extends Properties("StringBijections") +class StringBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import StringArbs._ - property("round trips string -> Array[String]") = isLooseInjection[String, Array[Byte]] + property("round trips string -> Array[String]") { + isLooseInjection[String, Array[Byte]] + } + implicit val symbol = arbitraryViaFn { (s: String) => Symbol(s) } - property("round trips string -> symbol") = isBijection[String, Symbol] + property("round trips string -> symbol") { + isBijection[String, Symbol] + } implicit val uuidArb = Arbitrary { for ( @@ -50,12 +57,17 @@ object StringBijectionLaws extends Properties("StringBijections") ) yield (new UUID(l, u)) } - property("UUID -> String") = isInjection[UUID, String] - //property("UUID <-> String @@ Rep[UUID]") = isBijection[UUID, String @@ Rep[UUID]]() + property("UUID -> String") { + isInjection[UUID, String] + } + + //property("UUID <-> String @@ Rep[UUID]") { + // isBijection[UUID, String @@ Rep[UUID]]() + // } def toUrl(s: String): Option[URL] = try { Some(new URL("http://" + s + ".com")) } - catch { case _ => None } + catch { case _: Throwable => None } implicit val urlArb = Arbitrary { implicitly[Arbitrary[String]] @@ -66,19 +78,26 @@ object StringBijectionLaws extends Properties("StringBijections") } // This is trivially a bijection if it injective - property("URL -> String") = isInjection[URL, String] + property("URL -> String") { + isInjection[URL, String] + } - property("rts through StringJoinBijection") = + property("rts through StringJoinBijection") { forAll { (sep: String, xs: List[String]) => val sjBij = StringJoinBijection(sep) val iter = xs.toIterable - (!iter.exists(_.contains(sep))) ==> (iter == rt(iter)(sjBij)) + whenever(!iter.exists(_.contains(sep))) { + assert(iter == rt(iter)(sjBij)) + } } + } //implicit val listOpt = StringJoinBijection.viaContainer[Int, List[Int]]() //property("viaCollection List[Int] -> Option[String]") = //roundTrips[List[Int], Option[String @@ Rep[List[Int]]]]() // implicit val listStr = StringJoinBijection.nonEmptyValues[Int, List[Int]]() - // property("viaCollection List[Int] -> String") = roundTrips[List[Int], String @@ Rep[List[Int]]]() + // property("viaCollection List[Int] -> String") { + // roundTrips[List[Int], String @@ Rep[List[Int]]]() + // } } diff --git a/bijection-core/src/test/scala/com/twitter/bijection/TupleBijectionLaws.scala b/bijection-core/src/test/scala/com/twitter/bijection/TupleBijectionLaws.scala index 21b30aff4..f921464a1 100644 --- a/bijection-core/src/test/scala/com/twitter/bijection/TupleBijectionLaws.scala +++ b/bijection-core/src/test/scala/com/twitter/bijection/TupleBijectionLaws.scala @@ -25,20 +25,27 @@ import java.lang.{ Byte => JByte } -import org.scalacheck.Properties -import org.scalacheck.Prop.forAll +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks -object TupleBijectionLaws extends Properties("TupleBijections") + +class TupleBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import StringArbs._ - property("round trips (Int,Long) -> (String,String)") = + property("round trips (Int,Long) -> (String,String)") { isBijection[(Int, Long), (String @@ Rep[Int], String @@ Rep[Long])] - property("round trips (Int,Long,String) -> (String,String,String)") = + } + + property("round trips (Int,Long,String) -> (String,String,String)") { isBijection[(Int, Long, String), (String @@ Rep[Int], String @@ Rep[Long], String)] - property("round trips (Int,Long,String,Long) -> (String,String,String,Array[Byte])") = - //This needs a tag on Array[Byte] @@ Rep[Long] + } + + property("round trips (Int,Long,String,Long) -> (String,String,String,Array[Byte])") { isInjection[(Int, Long, String, Long), (String, String, String, Array[Byte])] - property("Tuple to list") = + } + + property("Tuple to list") { isLooseInjection[(Int, Long, String), List[Array[Byte]]] + } } diff --git a/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBijectionLaws.scala b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBijectionLaws.scala index 03fc03246..a3819e99f 100644 --- a/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBijectionLaws.scala +++ b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBijectionLaws.scala @@ -22,7 +22,9 @@ import com.twitter.bijection.{ @@, BaseProperties, Bijection, Rep, Conversion } import com.twitter.bijection.Rep._ import org.scalacheck.Arbitrary -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Prop.forAll import java.lang.{ Long => JLong } @@ -30,35 +32,40 @@ import java.lang.{ Long => JLong } import Bijection.connect import Conversion.asMethod -object GuavaBijectionLaws extends Properties("GuavaBijections") with BaseProperties { +class GuavaBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import GuavaBijections._ implicit def arbOptional[T: Arbitrary] = arbitraryViaFn[T, Optional[T]] { Optional.of(_) } - property("round trips Option[Int] -> Optional[Int]") = + property("round trips Option[Int] -> Optional[Int]") { isBijection[Option[Int], Optional[Int]] + } - property("round trips Option[Long] -> Optional[Long]") = + property("round trips Option[Long] -> Optional[Long]") { isBijection[Option[Long], Optional[Long]] + } def roundTripsFn[A, B](fn: A => B)(implicit arb: Arbitrary[A], bij: Bijection[A => B, GFn[A, B]], eqb: Equiv[B]) = { val rtFn = bij(fn) - forAll { a: A => eqb.equiv(fn(a), rtFn.apply(a)) } + forAll { a: A => assert(eqb.equiv(fn(a), rtFn.apply(a))) } } - property("round trips Int => Long -> GuavaFn[Int, Long]") = + property("round trips Int => Long -> GuavaFn[Int, Long]") { roundTripsFn[Int, Long] { x => (x * x).toLong } + } - property("round trips () => Long -> Supplier[JLong]") = + property("round trips () => Long -> Supplier[JLong]") { forAll { l: Long => val fn = { () => l } - fn() == fn.as[Supplier[JLong]].get.as[Long] + assert(fn() == fn.as[Supplier[JLong]].get.as[Long]) } + } - property("round trips Long => Boolean -> Predicate[JLong]") = + property("round trips Long => Boolean -> Predicate[JLong]") { forAll { l: Long => val isEven = { l: Long => l % 2 == 0 } - isEven(l) == isEven.as[Predicate[JLong]].apply(l.as[JLong]) + assert(isEven(l) == isEven.as[Predicate[JLong]].apply(l.as[JLong])) } + } } diff --git a/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala index 9e82f8901..6944ce85b 100644 --- a/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala +++ b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala @@ -15,7 +15,9 @@ package com.twitter.bijection.guava import com.twitter.bijection.BaseProperties -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import com.twitter.bijection.guava.GuavaBinaryBijections._ import com.twitter.bijection.guava.GuavaBinaryBijections.Base64String import com.twitter.bijection.guava.GuavaBinaryBijections.Base64URLString @@ -24,12 +26,27 @@ import com.twitter.bijection.guava.GuavaBinaryBijections.Base64URLString * @author Muhammad Ashraf * @since 7/7/13 */ -object GuavaBinaryBijectionsLaws extends Properties("GuavaBinaryBijections") +class GuavaBinaryBijectionsLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { - property("rts Array[Byte] -> Base64String") = isInjective[Array[Byte], Base64String] - property("rts Array[Byte] -> Base64URLString") = isInjective[Array[Byte], Base64URLString] - property("rts Array[Byte] -> Base32String") = isInjective[Array[Byte], Base32String] - property("rts Array[Byte] -> Base32HEXString") = isInjective[Array[Byte], Base32HEXString] - property("rts Array[Byte] -> Base16String") = isInjective[Array[Byte], Base16String] + property("rts Array[Byte] -> Base64String") { + isInjective[Array[Byte], Base64String] + } + + property("rts Array[Byte] -> Base64URLString") { + isInjective[Array[Byte], Base64URLString] + } + + property("rts Array[Byte] -> Base32String") { + isInjective[Array[Byte], Base32String] + } + + property("rts Array[Byte] -> Base32HEXString") { + isInjective[Array[Byte], Base32HEXString] + } + + property("rts Array[Byte] -> Base16String") { + isInjective[Array[Byte], Base16String] + } + } diff --git a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala index a25e00381..79b772579 100644 --- a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala +++ b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala @@ -1,6 +1,8 @@ package com.twitter.bijection.hbase -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import com.twitter.bijection.BaseProperties import HBaseBijections._ import org.apache.hadoop.hbase.io.ImmutableBytesWritable @@ -10,62 +12,85 @@ import org.apache.hadoop.hbase.util.Bytes * @author Muhammad Ashraf * @since 7/10/13 */ -object HBaseBijectionsLaws extends Properties("HBaseBijections") +class HBaseBijectionsLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { - property("String <=> StringBytes") = isInjective[String, StringBytes] - property("Int <=> IntBytes") = isInjective[Int, IntBytes] - property("Long <=> LongBytes") = isInjective[Long, LongBytes] - property("Double <=> DoubleBytes") = isInjective[Double, DoubleBytes] - property("Float <=> FloatBytes") = isInjective[Float, FloatBytes] - property("Boolean <=> BooleanBytes") = isInjective[Boolean, BooleanBytes] - property("Short <=> ShortBytes") = isInjective[Short, ShortBytes] - property("BigDecimal <=> BigDecimalBytes") = isInjective[BigDecimal, BigDecimalBytes] + property("String <=> StringBytes") { + isInjective[String, StringBytes] + } + + property("Int <=> IntBytes") { + isInjective[Int, IntBytes] + } + + property("Long <=> LongBytes") { + isInjective[Long, LongBytes] + } + + property("Double <=> DoubleBytes") { + isInjective[Double, DoubleBytes] + } + + property("Float <=> FloatBytes") { + isInjective[Float, FloatBytes] + } + + property("Boolean <=> BooleanBytes") { + isInjective[Boolean, BooleanBytes] + } + + property("Short <=> ShortBytes") { + isInjective[Short, ShortBytes] + } + + property("BigDecimal <=> BigDecimalBytes") { + isInjective[BigDecimal, BigDecimalBytes] + } - property("BigDecimal <=> ImmutableBytesWritable") = { + property("BigDecimal <=> ImmutableBytesWritable") { implicit val arbBD = arbitraryViaFn { bd: BigDecimal => new ImmutableBytesWritable(Bytes.toBytes(bd.underlying())) } isBijection[BigDecimal, ImmutableBytesWritable] } - property("String <=> ImmutableBytesWritable") = { + property("String <=> ImmutableBytesWritable") { implicit val arbString = arbitraryViaFn { input: String => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[String, ImmutableBytesWritable] } - property("Long <=> ImmutableBytesWritable") = { + property("Long <=> ImmutableBytesWritable") { implicit val arbLong = arbitraryViaFn { input: Long => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[Long, ImmutableBytesWritable] } - property("Int <=> ImmutableBytesWritable") = { + property("Int <=> ImmutableBytesWritable") { implicit val arbInt = arbitraryViaFn { input: Int => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[Int, ImmutableBytesWritable] } - property("Double <=> ImmutableBytesWritable") = { + property("Double <=> ImmutableBytesWritable") { implicit val arbDouble = arbitraryViaFn { input: Double => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[Double, ImmutableBytesWritable] } - property("Float <=> ImmutableBytesWritable") = { + property("Float <=> ImmutableBytesWritable") { implicit val arbFloat = arbitraryViaFn { input: Float => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[Float, ImmutableBytesWritable] } - property("Short <=> ImmutableBytesWritable") = { + property("Short <=> ImmutableBytesWritable") { implicit val arbShort = arbitraryViaFn { input: Short => new ImmutableBytesWritable(Bytes.toBytes(input)) } isBijection[Short, ImmutableBytesWritable] } - property("Boolean <=> ImmutableBytesWritable") = { + property("Boolean <=> ImmutableBytesWritable") { implicit val arbBoolean = arbitraryViaFn { input: Boolean => new ImmutableBytesWritable(Bytes.toBytes(input)) } diff --git a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala index 90a8cadba..083ed1632 100644 --- a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala +++ b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala @@ -14,7 +14,7 @@ package com.twitter.bijection.hbase -import org.specs.Specification +import org.scalatest._ import com.twitter.bijection.{ Bijection, BaseProperties } import HBaseBijections._ import org.apache.hadoop.hbase.io.ImmutableBytesWritable @@ -23,112 +23,112 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable * @author Muhammad Ashraf * @since 7/10/13 */ -object HBaseBijectionsSpecifications extends Specification with BaseProperties { +object HBaseBijectionsSpecifications extends WordSpec with Matchers with BaseProperties { "HBaseBijections" should { "round trip String -> Array[Byte]" in { val expected = "Bonjour le monde" val bytes = Bijection[String, StringBytes](expected) val result = Bijection.invert[String, StringBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Long -> Array[Byte]" in { val expected = 42L val bytes = Bijection[Long, LongBytes](expected) val result = Bijection.invert[Long, LongBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Int -> Array[Byte]" in { val expected = 42 val bytes = Bijection[Int, IntBytes](expected) val result = Bijection.invert[Int, IntBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Double -> Array[Byte]" in { val expected = 42.0 val bytes = Bijection[Double, DoubleBytes](expected) val result = Bijection.invert[Double, DoubleBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Float -> Array[Byte]" in { val expected = 42.0F val bytes = Bijection[Float, FloatBytes](expected) val result = Bijection.invert[Float, FloatBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Short -> Array[Byte]" in { val expected = 1.toShort val bytes = Bijection[Short, ShortBytes](expected) val result = Bijection.invert[Short, ShortBytes](bytes) - result must_== expected + assert(result == expected) } "round trip BigDecimal -> Array[Byte]" in { val expected = BigDecimal(1) val bytes = Bijection[BigDecimal, BigDecimalBytes](expected) val result = Bijection.invert[BigDecimal, BigDecimalBytes](bytes) - result must_== expected + assert(result == expected) } "round trip Boolean -> Array[Byte]" in { val expected = true val bytes = Bijection[Boolean, BooleanBytes](expected) val result = Bijection.invert[Boolean, BooleanBytes](bytes) - result must_== expected + assert(result == expected) } "round trip String -> ImmutableBytesWritable" in { val expected = "Bonjour le monde" val bytes = Bijection[String, ImmutableBytesWritable](expected) val result = Bijection.invert[String, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip Long -> ImmutableBytesWritable" in { val expected = 1L val bytes = Bijection[Long, ImmutableBytesWritable](expected) val result = Bijection.invert[Long, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip Int -> ImmutableBytesWritable" in { val expected = 42 val bytes = Bijection[Int, ImmutableBytesWritable](expected) val result = Bijection.invert[Int, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip Double -> ImmutableBytesWritable" in { val expected = 42.0 val bytes = Bijection[Double, ImmutableBytesWritable](expected) val result = Bijection.invert[Double, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip Float -> ImmutableBytesWritable" in { val expected = 42.0F val bytes = Bijection[Float, ImmutableBytesWritable](expected) val result = Bijection.invert[Float, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip Short -> ImmutableBytesWritable" in { val expected = 1.toShort val bytes = Bijection[Short, ImmutableBytesWritable](expected) val result = Bijection.invert[Short, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } "round trip BigDecimal -> ImmutableBytesWritable" in { val expected = BigDecimal(1) val bytes = Bijection[BigDecimal, ImmutableBytesWritable](expected) val result = Bijection.invert[BigDecimal, ImmutableBytesWritable](bytes) - result must_== expected + assert(result == expected) } } diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 552e3cc51..66b22508f 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -1,14 +1,17 @@ package com.twitter.bijection.jodatime -import org.scalacheck.{ Arbitrary, Gen, Properties } -import org.scalacheck.Gen._ +import org.scalacheck.{ Arbitrary, Gen } import org.scalacheck.Prop._ + +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import com.twitter.bijection.{ Bijection, BaseProperties, ImplicitBijection } import java.util.Date import org.joda.time.{ DateTime, LocalDate, LocalTime, YearMonth, MonthDay } import com.twitter.bijection._ -object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties with DateBijections with DateInjections { +class DateBijectionsLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties with DateBijections with DateInjections { import Rep._ @@ -31,20 +34,36 @@ object DateBijectionsLaws extends Properties("DateBijections") with BaseProperti implicit val monthDay = arbitraryViaFn { (dtime: Timestamp) => new MonthDay(dtime.ts) } - property("Long <=> Joda") = isBijection[Long, DateTime] + property("Long <=> Joda") { + isBijection[Long, DateTime] + } - property("Date <=> Joda") = isBijection[Date, DateTime] + property("Date <=> Joda") { + isBijection[Date, DateTime] + } - property("round trips Date -> String") = isLooseInjection[DateTime, String] + property("round trips Date -> String") { + isLooseInjection[DateTime, String] + } - property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] + property("round trips Joda -> Date") { + isLooseInjection[DateTime, Date] + } - property("round trips LocalDate -> String") = isLooseInjection[LocalDate, String] + property("round trips LocalDate -> String") { + isLooseInjection[LocalDate, String] + } - property("round trips LocalTime -> String") = isLooseInjection[LocalTime, String] + property("round trips LocalTime -> String") { + isLooseInjection[LocalTime, String] + } - property("round trips YearMonth -> String") = isLooseInjection[YearMonth, String] + property("round trips YearMonth -> String") { + isLooseInjection[YearMonth, String] + } - property("round trips MonthDay -> String") = isLooseInjection[MonthDay, String] + property("round trips MonthDay -> String") { + isLooseInjection[MonthDay, String] + } } diff --git a/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala b/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala index 8e262bdc2..d46c3d91a 100644 --- a/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala +++ b/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala @@ -19,7 +19,9 @@ package com.twitter.bijection.json import com.twitter.bijection.Conversion.asMethod import com.twitter.bijection.{ BaseProperties, Bijection, Injection } -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Prop.forAll import org.scalacheck.Arbitrary @@ -27,7 +29,7 @@ import org.codehaus.jackson.JsonNode import com.twitter.bijection.json.JsonNodeInjection.{ fromJsonNode, toJsonNode } import scala.util.Try -object JsonInjectionLaws extends Properties("JsonInjection") with BaseProperties { +class JsonInjectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { // Needed from some recursive injections (like tuples) import JsonNodeInjection._ @@ -36,57 +38,129 @@ object JsonInjectionLaws extends Properties("JsonInjection") with BaseProperties isLooseInjection[T, String] } - property("Short") = roundTripJson[Short] - property("Int") = roundTripJson[Int] - property("Long") = roundTripJson[Long] - property("Float") = roundTripJson[Float] - property("Double") = roundTripJson[Double] - property("String") = roundTripJson[String] - property("Array[Byte]") = roundTripJson[Array[Byte]] + property("Short") { + roundTripJson[Short] + } + + property("Int") { + roundTripJson[Int] + } + + property("Long") { + roundTripJson[Long] + } + + property("Float") { + roundTripJson[Float] + } + + property("Double") { + roundTripJson[Double] + } + + property("String") { + roundTripJson[String] + } + + property("Array[Byte]") { + roundTripJson[Array[Byte]] + } + // Collections - property("(String,Int)") = roundTripJson[(String, Int)] - property("(String,Int,Long)") = roundTripJson[(String, Int, Long)] - property("(String,(String,Int))") = roundTripJson[(String, (String, Int))] - property("Either[String,Int]") = roundTripJson[Either[String, Int]] - property("Map[String, Either[String,Int]]") = roundTripJson[Map[String, Either[String, Int]]] - property("Map[String,Int]") = roundTripJson[Map[String, Int]] - property("Map[String,Long]") = roundTripJson[Map[String, Long]] - property("Map[String,String]") = roundTripJson[Map[String, String]] - property("Map[String,Map[String,Int]]") = roundTripJson[Map[String, Map[String, Int]]] - property("List[String]") = roundTripJson[List[String]] - property("List[Int]") = roundTripJson[List[Int]] - property("List[List[String]]") = roundTripJson[List[List[String]]] - property("Set[Int]") = roundTripJson[Set[Int]] + property("(String,Int)") { + roundTripJson[(String, Int)] + } + + property("(String,Int,Long)") { + roundTripJson[(String, Int, Long)] + } + + property("(String,(String,Int))") { + roundTripJson[(String, (String, Int))] + } + + property("Either[String,Int]") { + roundTripJson[Either[String, Int]] + } + + property("Map[String, Either[String,Int]]") { + roundTripJson[Map[String, Either[String, Int]]] + } + + property("Map[String,Int]") { + roundTripJson[Map[String, Int]] + } + + property("Map[String,Long]") { + roundTripJson[Map[String, Long]] + } + + property("Map[String,String]") { + roundTripJson[Map[String, String]] + } + + property("Map[String,Map[String,Int]]") { + roundTripJson[Map[String, Map[String, Int]]] + } + + property("List[String]") { + roundTripJson[List[String]] + } + + property("List[Int]") { + roundTripJson[List[Int]] + } + + property("List[List[String]]") { + roundTripJson[List[List[String]]] + } + + property("Set[Int]") { + roundTripJson[Set[Int]] + } implicit val jsonOpt = JsonNodeInjection.viaInjection[Option[Int], List[Int]] - property("Option[Int]") = roundTripJson[Option[Int]] + property("Option[Int]") { + roundTripJson[Option[Int]] + } implicit val arbSeq: Arbitrary[Seq[Int]] = arbitraryViaFn { s: List[Int] => s.toSeq } implicit val arbVec: Arbitrary[Vector[Int]] = arbitraryViaFn { s: List[Int] => Vector(s: _*) } implicit val arbIndexed: Arbitrary[IndexedSeq[Int]] = arbitraryViaFn { s: List[Int] => s.toIndexedSeq } - property("Seq[Int]") = roundTripJson[Seq[Int]] - property("Vector[Int]") = roundTripJson[Vector[Int]] - property("IndexedSeq[Int]") = roundTripJson[IndexedSeq[Int]] + property("Seq[Int]") { + roundTripJson[Seq[Int]] + } + + property("Vector[Int]") { + roundTripJson[Vector[Int]] + } + + property("IndexedSeq[Int]") { + roundTripJson[IndexedSeq[Int]] + } // Handle Mixed values: - property("Mixed values") = forAll { (kv: List[(String, Int, List[String])]) => - val mixedMap = kv.map { - case (key, intv, lv) => - if (scala.math.random < 0.5) { (key + "i", toJsonNode(intv)) } - else { (key + "l", toJsonNode(lv)) } - }.toMap - - val jsonMixed = mixedMap.as[UnparsedJson] - - jsonMixed.as[Try[Map[String, JsonNode]]].get.map({ kup: (String, JsonNode) => - val (k, up) = kup - if (k.endsWith("i")) { - fromJsonNode[Int](up).get == fromJsonNode[Int](mixedMap(k)).get - } else { - fromJsonNode[List[String]](up).get == fromJsonNode[List[String]](mixedMap(k)).get + property("Mixed values") { + forAll { (kv: List[(String, Int, List[String])]) => + + val mixedMap = kv.map { + case (key, intv, lv) => + if (scala.math.random < 0.5) { (key + "i", toJsonNode(intv)) } + else { (key + "l", toJsonNode(lv)) } + }.toMap + + val jsonMixed = mixedMap.as[UnparsedJson] + + jsonMixed.as[Try[Map[String, JsonNode]]].get.foreach { kup: (String, JsonNode) => + val (k, up) = kup + if (k.endsWith("i")) { + assert(fromJsonNode[Int](up).get == fromJsonNode[Int](mixedMap(k)).get) + } else { + assert(fromJsonNode[List[String]](up).get == fromJsonNode[List[String]](mixedMap(k)).get) + } } - }).forall { x => x } + } } } diff --git a/bijection-json4s/src/main/scala/com/twitter/bijection/json4s/Json4sInjections.scala b/bijection-json4s/src/main/scala/com/twitter/bijection/json4s/Json4sInjections.scala index d2fa3e8b8..292eb32fb 100644 --- a/bijection-json4s/src/main/scala/com/twitter/bijection/json4s/Json4sInjections.scala +++ b/bijection-json4s/src/main/scala/com/twitter/bijection/json4s/Json4sInjections.scala @@ -20,6 +20,8 @@ import org.json4s.native.JsonMethods._ import scala.util.Try import com.twitter.bijection.Inversion._ import org.json4s.native.Serialization._ +import scala.reflect.runtime.universe.TypeTag +import scala.reflect.ClassTag /** * @author Mansur Ashraf @@ -41,7 +43,7 @@ object Json4sInjections { * @tparam A Case Class * @return Json String */ - implicit def caseClass2Json[A <: AnyRef](implicit mf: Manifest[A], fmt: Formats): Injection[A, String] = new AbstractInjection[A, String] { + implicit def caseClass2Json[A <: AnyRef](implicit tt: TypeTag[A], ct: ClassTag[A], fmt: Formats): Injection[A, String] = new AbstractInjection[A, String] { override def apply(a: A): String = write(a) override def invert(b: String): Try[A] = attempt(b)(read[A]) @@ -52,7 +54,7 @@ object Json4sInjections { * @tparam A Case Class * @return JValue */ - implicit def caseClass2JValue[A <: AnyRef](implicit mf: Manifest[A], fmt: Formats): Injection[A, JValue] = new AbstractInjection[A, JValue] { + implicit def caseClass2JValue[A <: AnyRef](implicit tt: TypeTag[A], ct: ClassTag[A], fmt: Formats): Injection[A, JValue] = new AbstractInjection[A, JValue] { override def apply(a: A): JValue = Extraction.decompose(a) override def invert(b: JValue): Try[A] = attempt(b)(_.extract[A]) diff --git a/bijection-json4s/src/test/scala/com/twitter/bijection/json4s/Json4sInjectionLaws.scala b/bijection-json4s/src/test/scala/com/twitter/bijection/json4s/Json4sInjectionLaws.scala index 0fd9bba18..f925c7f3b 100644 --- a/bijection-json4s/src/test/scala/com/twitter/bijection/json4s/Json4sInjectionLaws.scala +++ b/bijection-json4s/src/test/scala/com/twitter/bijection/json4s/Json4sInjectionLaws.scala @@ -14,17 +14,21 @@ package com.twitter.bijection.json4s -import org.scalacheck.Properties +import org.scalatest.PropSpec +import org.scalatest.prop.PropertyChecks import com.twitter.bijection.{ Injection, BaseProperties } import org.json4s.JsonAST._ import org.json4s.JsonAST.JString +import scala.reflect.runtime.universe.TypeTag +import scala.reflect.ClassTag + /** * @author Mansur Ashraf * @since 1/10/14 */ -object Json4sInjectionLaws extends Properties("Json4sInjection") +class Json4sInjectionLaws extends PropSpec with PropertyChecks with BaseProperties { case class Twit(name: String, id: Int, id_str: String, indices: List[Int], screen_name: String) @@ -45,21 +49,21 @@ object Json4sInjectionLaws extends Properties("Json4sInjection") JField("screen_name", JString(in._5)))) } - def roundTripCaseClassToJson(implicit inj: Injection[Twit, String], mn: Manifest[Twit]) = isLooseInjection[Twit, String] + def roundTripCaseClassToJson(implicit inj: Injection[Twit, String], tt: TypeTag[Twit], ct: ClassTag[Twit]) = isLooseInjection[Twit, String] - def roundTripCaseClassToJValue(implicit inj: Injection[Twit, JValue], mn: Manifest[Twit]) = isLooseInjection[Twit, JValue] + def roundTripCaseClassToJValue(implicit inj: Injection[Twit, JValue], tt: TypeTag[Twit], ct: ClassTag[Twit]) = isLooseInjection[Twit, JValue] def roundTripJValueToString(implicit inj: Injection[JValue, String]) = isLooseInjection[JValue, String] - property("round trip Case Class to Json") = { + property("round trip Case Class to Json") { import Json4sInjections.caseClass2Json roundTripCaseClassToJson } - property("round trip Case Class to JValue") = { + property("round trip Case Class to JValue") { import Json4sInjections.caseClass2JValue roundTripCaseClassToJValue } - property("round trip JValue to String") = { + property("round trip JValue to String") { import Json4sInjections.jvalue2Json roundTripJValueToString } diff --git a/bijection-protobuf/src/main/scala/com/twitter/bijection/protobuf/ProtobufCodecs.scala b/bijection-protobuf/src/main/scala/com/twitter/bijection/protobuf/ProtobufCodecs.scala index cf58040e3..2ef6ee91e 100644 --- a/bijection-protobuf/src/main/scala/com/twitter/bijection/protobuf/ProtobufCodecs.scala +++ b/bijection-protobuf/src/main/scala/com/twitter/bijection/protobuf/ProtobufCodecs.scala @@ -7,6 +7,7 @@ import com.google.protobuf.ProtocolMessageEnum import java.lang.{ Integer => JInt } import scala.collection.mutable.{ Map => MMap } import scala.util.{ Failure, Success } +import scala.reflect._ /** * Bijections for use in serializing and deserializing Protobufs. @@ -15,8 +16,8 @@ object ProtobufCodec { /** * For scala instantiation. Uses reflection. */ - implicit def apply[T <: Message: Manifest]: Injection[T, Array[Byte]] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + implicit def apply[T <: Message: ClassTag]: Injection[T, Array[Byte]] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] fromClass(klass) } @@ -38,8 +39,8 @@ object ProtobufEnumCodec { /** * For scala instantiation. Uses reflection. */ - implicit def apply[T <: ProtocolMessageEnum: Manifest]: Injection[T, Int] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + implicit def apply[T <: ProtocolMessageEnum: ClassTag]: Injection[T, Int] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] fromClass(klass) } /** @@ -50,7 +51,7 @@ object ProtobufEnumCodec { /** * Implicit conversions between ProtocolMessageEnum and common types. */ - implicit def toBinary[T <: ProtocolMessageEnum: Manifest]: Injection[T, Array[Byte]] = + implicit def toBinary[T <: ProtocolMessageEnum: ClassTag]: Injection[T, Array[Byte]] = Injection.connect[T, Int, Array[Byte]] } diff --git a/bijection-protobuf/src/test/scala/com/twitter/bijection/protobuf/ProtobufCodecLaws.scala b/bijection-protobuf/src/test/scala/com/twitter/bijection/protobuf/ProtobufCodecLaws.scala index ae6eced25..e385be9b1 100644 --- a/bijection-protobuf/src/test/scala/com/twitter/bijection/protobuf/ProtobufCodecLaws.scala +++ b/bijection-protobuf/src/test/scala/com/twitter/bijection/protobuf/ProtobufCodecLaws.scala @@ -18,11 +18,13 @@ package com.twitter.bijection.protobuf import com.twitter.bijection.{ BaseProperties, Bijection } import com.twitter.bijection.protobuf.TestMessages.{ FatigueCount, Gender } -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Arbitrary -import org.specs._ +import org.scalatest._ -object ProtobufCodecLaws extends Properties("ProtobufCodec") with BaseProperties { +class ProtobufCodecLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { def buildFatigueCount(tuple: (Long, Long, Int)) = FatigueCount.newBuilder() .setTargetId(tuple._1) @@ -33,21 +35,21 @@ object ProtobufCodecLaws extends Properties("ProtobufCodec") with BaseProperties implicit val fatigueCount: Arbitrary[FatigueCount] = arbitraryViaFn { input: (Long, Long, Int) => buildFatigueCount(input) } - property("round trips protobuf -> Array[Byte]") = { + property("round trips protobuf -> Array[Byte]") { implicit val b = ProtobufCodec[FatigueCount] isLooseInjection[FatigueCount, Array[Byte]] } } -class ProtobufEnumTest extends Specification with BaseProperties { +class ProtobufEnumTest extends WordSpec with Matchers with BaseProperties { "ProtocolMessageEnum should roundtrip through ProtobufCodec" in { implicit val b = ProtobufEnumCodec[Gender] val male = Gender.valueOf(0) - male must_== rt(male) + assert(male == rt(male)) val female = Gender.valueOf(1) - female must_== rt(female) + assert(female == rt(female)) - b.invert(2).isFailure must beTrue + assert(b.invert(2).isFailure == true) } } diff --git a/bijection-scrooge/src/test/scala/com/twitter/bijection/scrooge/ScroogeCodecLaws.scala b/bijection-scrooge/src/test/scala/com/twitter/bijection/scrooge/ScroogeCodecLaws.scala index 63b753c8e..5b06b883b 100644 --- a/bijection-scrooge/src/test/scala/com/twitter/bijection/scrooge/ScroogeCodecLaws.scala +++ b/bijection-scrooge/src/test/scala/com/twitter/bijection/scrooge/ScroogeCodecLaws.scala @@ -18,10 +18,12 @@ package com.twitter.bijection.scrooge import com.twitter.bijection.{ BaseProperties, Bijection, Injection } -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Arbitrary -object ScroogeCodecLaws extends Properties("ScroogeCodecs") with BaseProperties { +class ScroogeCodecLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { def buildScrooge(i: (Int, String)) = TestStruct(i._1, Some(i._2)) @@ -33,9 +35,11 @@ object ScroogeCodecLaws extends Properties("ScroogeCodecs") with BaseProperties isLooseInjection[TestStruct, Array[Byte]] } - property("round trips thrift -> Array[Byte] through binary") = + property("round trips thrift -> Array[Byte] through binary") { roundTripsScrooge(BinaryScalaCodec(TestStruct)) + } - property("round trips thrift -> Array[Byte] through compact") = + property("round trips thrift -> Array[Byte] through compact") { roundTripsScrooge(CompactScalaCodec(TestStruct)) + } } diff --git a/bijection-thrift/src/main/scala/com/twitter/bijection/thrift/ThriftCodecs.scala b/bijection-thrift/src/main/scala/com/twitter/bijection/thrift/ThriftCodecs.scala index 98656750d..4ecfe5b68 100644 --- a/bijection-thrift/src/main/scala/com/twitter/bijection/thrift/ThriftCodecs.scala +++ b/bijection-thrift/src/main/scala/com/twitter/bijection/thrift/ThriftCodecs.scala @@ -15,6 +15,7 @@ import org.codehaus.jackson.map.MappingJsonFactory import java.lang.{ Integer => JInt } import scala.collection.mutable.{ Map => MMap } import scala.util.{ Failure, Success } +import scala.reflect._ /** * Codecs for use in serializing and deserializing Thrift structures. @@ -23,9 +24,9 @@ object ThriftCodec { /** * For scala instantiation. Uses reflection. */ - def apply[T <: TBase[_, _]: Manifest, P <: TProtocolFactory: Manifest]: Injection[T, Array[Byte]] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] - val factory = manifest[P].erasure.asInstanceOf[Class[P]].newInstance + def apply[T <: TBase[_, _]: ClassTag, P <: TProtocolFactory: ClassTag]: Injection[T, Array[Byte]] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] + val factory = classTag[P].runtimeClass.asInstanceOf[Class[P]].newInstance apply(klass, factory) } @@ -35,9 +36,9 @@ object ThriftCodec { def apply[T <: TBase[_, _], P <: TProtocolFactory](klass: Class[T], factory: P): Injection[T, Array[Byte]] = new ThriftCodec[T, P](klass, factory) - implicit def toBinary[T <: TBase[_, _]: Manifest]: Injection[T, Array[Byte]] = BinaryThriftCodec[T] - def toCompact[T <: TBase[_, _]: Manifest]: Injection[T, Array[Byte]] = CompactThriftCodec[T] - def toJson[T <: TBase[_, _]: Manifest]: Injection[T, String] = JsonThriftCodec[T] + implicit def toBinary[T <: TBase[_, _]: ClassTag]: Injection[T, Array[Byte]] = BinaryThriftCodec[T] + def toCompact[T <: TBase[_, _]: ClassTag]: Injection[T, Array[Byte]] = CompactThriftCodec[T] + def toJson[T <: TBase[_, _]: ClassTag]: Injection[T, String] = JsonThriftCodec[T] } class ThriftCodec[T <: TBase[_, _], P <: TProtocolFactory](klass: Class[T], factory: P) @@ -57,7 +58,7 @@ class ThriftCodec[T <: TBase[_, _], P <: TProtocolFactory](klass: Class[T], fact } object BinaryThriftCodec { - def apply[T <: TBase[_, _]: Manifest]: Injection[T, Array[Byte]] = fromClass(manifest[T].erasure.asInstanceOf[Class[T]]) + def apply[T <: TBase[_, _]: ClassTag]: Injection[T, Array[Byte]] = fromClass(classTag[T].runtimeClass.asInstanceOf[Class[T]]) def fromClass[T <: TBase[_, _]](klass: Class[T]): Injection[T, Array[Byte]] = new BinaryThriftCodec(klass) } @@ -65,7 +66,7 @@ class BinaryThriftCodec[T <: TBase[_, _]](klass: Class[T]) extends ThriftCodec[T, TBinaryProtocol.Factory](klass, new TBinaryProtocol.Factory) object CompactThriftCodec { - def apply[T <: TBase[_, _]: Manifest]: Injection[T, Array[Byte]] = fromClass(manifest[T].erasure.asInstanceOf[Class[T]]) + def apply[T <: TBase[_, _]: ClassTag]: Injection[T, Array[Byte]] = fromClass(classTag[T].runtimeClass.asInstanceOf[Class[T]]) def fromClass[T <: TBase[_, _]](klass: Class[T]): Injection[T, Array[Byte]] = new CompactThriftCodec(klass) } @@ -73,7 +74,7 @@ class CompactThriftCodec[T <: TBase[_, _]](klass: Class[T]) extends ThriftCodec[T, TCompactProtocol.Factory](klass, new TCompactProtocol.Factory) object JsonThriftCodec { - def apply[T <: TBase[_, _]: Manifest]: Injection[T, String] = fromClass(manifest[T].erasure.asInstanceOf[Class[T]]) + def apply[T <: TBase[_, _]: ClassTag]: Injection[T, String] = fromClass(classTag[T].runtimeClass.asInstanceOf[Class[T]]) def fromClass[T <: TBase[_, _]](klass: Class[T]): Injection[T, String] = // This is not really unsafe because we know JsonThriftCodec gives utf8 bytes as output (new JsonThriftCodec[T](klass)) @@ -94,8 +95,8 @@ object TEnumCodec { /** * For scala instantiation. Uses reflection. */ - implicit def apply[T <: TEnum: Manifest]: Injection[T, Int] = { - val klass = manifest[T].erasure.asInstanceOf[Class[T]] + implicit def apply[T <: TEnum: ClassTag]: Injection[T, Int] = { + val klass = classTag[T].runtimeClass.asInstanceOf[Class[T]] fromClass(klass) } /** @@ -107,7 +108,7 @@ object TEnumCodec { /** * Implicit conversions between TEnum and common types. */ - implicit def toBinary[T <: TEnum: Manifest]: Injection[T, Array[Byte]] = + implicit def toBinary[T <: TEnum: ClassTag]: Injection[T, Array[Byte]] = Injection.connect[T, Int, Array[Byte]] } diff --git a/bijection-thrift/src/test/scala/com/twitter/bijection/thrift/ThriftCodecLaws.scala b/bijection-thrift/src/test/scala/com/twitter/bijection/thrift/ThriftCodecLaws.scala index addd900f9..9f3ad783e 100644 --- a/bijection-thrift/src/test/scala/com/twitter/bijection/thrift/ThriftCodecLaws.scala +++ b/bijection-thrift/src/test/scala/com/twitter/bijection/thrift/ThriftCodecLaws.scala @@ -17,11 +17,13 @@ package com.twitter.bijection.thrift import com.twitter.bijection.{ BaseProperties, Bijection, Injection } -import org.scalacheck.Properties +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Arbitrary -import org.specs._ +import org.scalatest._ -object ThriftCodecLaws extends Properties("ThriftCodecs") with BaseProperties { +class ThriftCodecLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { def buildThrift(i: (Int, String)) = new TestThriftStructure().setANumber(i._1).setAString(i._2) @@ -32,27 +34,29 @@ object ThriftCodecLaws extends Properties("ThriftCodecs") with BaseProperties { isLooseInjection[TestThriftStructure, Array[Byte]] } - property("round trips thrift -> Array[Byte] through binary") = + property("round trips thrift -> Array[Byte] through binary") { roundTripsThrift(BinaryThriftCodec[TestThriftStructure]) + } - property("round trips thrift -> Array[Byte] through compact") = + property("round trips thrift -> Array[Byte] through compact") { roundTripsThrift(CompactThriftCodec[TestThriftStructure]) + } - property("round trips thrift -> String through json") = { + property("round trips thrift -> String through json") { implicit val b = JsonThriftCodec[TestThriftStructure] isLooseInjection[TestThriftStructure, String] } } -class TEnumTest extends Specification with BaseProperties { +class TEnumTest extends WordSpec with Matchers with BaseProperties { "TEnum should roundtrip through TEnumCodec" in { implicit val b = TEnumCodec[Gender] val male = Gender.findByValue(0) - male must_== rt(male) + assert(male == rt(male)) val female = Gender.findByValue(1) - female must_== rt(female) + assert(female == rt(female)) - b.invert(2).isFailure must beTrue + assert(b.invert(2).isFailure == true) } } diff --git a/bijection-util/src/test/scala/com/twitter/bijection/twitter_util/UtilBijectionLaws.scala b/bijection-util/src/test/scala/com/twitter/bijection/twitter_util/UtilBijectionLaws.scala index 6eebb1294..63f8ed52a 100644 --- a/bijection-util/src/test/scala/com/twitter/bijection/twitter_util/UtilBijectionLaws.scala +++ b/bijection-util/src/test/scala/com/twitter/bijection/twitter_util/UtilBijectionLaws.scala @@ -19,7 +19,10 @@ package com.twitter.bijection.twitter_util import com.twitter.bijection.{ BaseProperties, Bijection } import com.twitter.util.{ Future => TwitterFuture, Try => TwitterTry, Await => TwitterAwait } import java.lang.{ Integer => JInt, Long => JLong } -import org.scalacheck.{ Arbitrary, Properties } +import org.scalacheck.Arbitrary +import org.scalatest.{ PropSpec, MustMatchers } +import org.scalatest.prop.PropertyChecks + import org.scalacheck.Prop.forAll import scala.concurrent.{ Future => ScalaFuture, Await => ScalaAwait } import scala.concurrent.duration.Duration @@ -27,7 +30,7 @@ import scala.util.{ Try => ScalaTry } import scala.concurrent.future import scala.concurrent.ExecutionContext.Implicits.global -object UtilBijectionLaws extends Properties("UtilBijection") with BaseProperties { +class UtilBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { import UtilBijections._ protected def toOption[T](f: TwitterFuture[T]): Option[T] = TwitterTry(TwitterAwait.result(f)).toOption @@ -53,22 +56,28 @@ object UtilBijectionLaws extends Properties("UtilBijection") with BaseProperties type FromMap = Map[Int, Long] type ToMap = Map[JInt, JLong] - property("round trips com.twitter.util.Future[Map[Int, String]] -> com.twitter.util.Future[JInt, JLong]") = + property("round trips com.twitter.util.Future[Map[Int, String]] -> com.twitter.util.Future[JInt, JLong]") { isBijection[TwitterFuture[FromMap], TwitterFuture[ToMap]] + } - property("round trips scala.concurrent.Future[Map[Int, String]] -> scala.concurrent.Future[JInt, JLong]") = + property("round trips scala.concurrent.Future[Map[Int, String]] -> scala.concurrent.Future[JInt, JLong]") { isBijection[ScalaFuture[FromMap], ScalaFuture[ToMap]] + } - property("round trips com.twitter.util.Try[Map[Int, String]] -> com.twitter.util.Try[Map[JInt, JLong]]") = + property("round trips com.twitter.util.Try[Map[Int, String]] -> com.twitter.util.Try[Map[JInt, JLong]]") { isBijection[TwitterTry[FromMap], TwitterTry[ToMap]] + } - property("round trips scala.util.Try[Map[Int, String]] -> scala.util.Try[Map[JInt, JLong]]") = + property("round trips scala.util.Try[Map[Int, String]] -> scala.util.Try[Map[JInt, JLong]]") { isBijection[ScalaTry[FromMap], ScalaTry[ToMap]] + } - property("round trips com.twitter.util.Try[Map[JInt, JLong]] -> scala.util.Try[Map[JInt, JLong]]") = + property("round trips com.twitter.util.Try[Map[JInt, JLong]] -> scala.util.Try[Map[JInt, JLong]]") { isBijection[TwitterTry[ToMap], ScalaTry[ToMap]] + } - property("round trips com.twitter.util.Future[Map[JInt, JLong]] -> scala.concurrent.Future[Map[JInt, JLong]]") = + property("round trips com.twitter.util.Future[Map[JInt, JLong]] -> scala.concurrent.Future[Map[JInt, JLong]]") { isBijection[TwitterFuture[ToMap], ScalaFuture[ToMap]] + } } diff --git a/project/Build.scala b/project/Build.scala index dcfc03dcf..3df01584c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -9,21 +9,20 @@ import scalariform.formatter.preferences._ import com.typesafe.sbt.SbtScalariform._ object BijectionBuild extends Build { - def withCross(dep: ModuleID) = - dep cross CrossVersion.binaryMapped { - case "2.9.3" => "2.9.2" // TODO: hack because twitter hasn't built things against 2.9.3 - case version if version startsWith "2.10" => "2.10" // TODO: hack because sbt is broken - case x => x - } + + def isScala210x(scalaVersion: String) = scalaVersion match { + case version if version startsWith "2.10" => true + case _ => false + } val sharedSettings = Project.defaultSettings ++ osgiSettings ++ scalariformSettings ++ Seq( organization := "com.twitter", - crossScalaVersions := Seq("2.9.3", "2.10.4"), + crossScalaVersions := Seq("2.10.4", "2.11.2"), ScalariformKeys.preferences := formattingPreferences, - scalaVersion := "2.9.3", + scalaVersion := "2.10.4", javacOptions ++= Seq("-source", "1.6", "-target", "1.6"), @@ -31,7 +30,7 @@ object BijectionBuild extends Build { libraryDependencies ++= Seq( "org.scalacheck" %% "scalacheck" % "1.11.5" % "test", - "org.scala-tools.testing" %% "specs" % "1.6.9" % "test" + "org.scalatest" %% "scalatest" % "2.2.2" % "test" ), resolvers ++= Seq( @@ -41,7 +40,14 @@ object BijectionBuild extends Build { parallelExecution in Test := true, - scalacOptions ++= Seq("-unchecked", "-deprecation"), + scalacOptions ++= Seq("-unchecked", "-deprecation", "-language:implicitConversions", "-language:higherKinds", "-language:existentials"), + + scalacOptions <++= (scalaVersion) map { sv => + if (sv startsWith "2.10") + Seq("-Xdivergence211") + else + Seq() + }, OsgiKeys.importPackage <<= scalaVersion { sv => Seq("""scala.*;version="$"""".format(sv)) }, @@ -176,7 +182,9 @@ object BijectionBuild extends Build { lazy val bijectionProtobuf = module("protobuf").settings( osgiExportAll("com.twitter.bijection.protobuf"), - libraryDependencies += "com.google.protobuf" % "protobuf-java" % "2.4.1" + libraryDependencies ++= Seq( + "com.google.protobuf" % "protobuf-java" % "2.4.1" + ) ).dependsOn(bijectionCore % "test->test;compile->compile") val jsonParser = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.1" @@ -200,12 +208,22 @@ object BijectionBuild extends Build { ) ).dependsOn(bijectionCore % "test->test;compile->compile") + def scroogeBuildDeps(scalaVersion: String): Seq[sbt.ModuleID] = isScala210x(scalaVersion) match { + case false => Seq() + case true => Seq( + "com.twitter" %% "scrooge-serializer" % "3.6.0" + ) + } + lazy val bijectionScrooge = module("scrooge").settings( + skip in compile := !isScala210x(scalaVersion.value), + skip in test := !isScala210x(scalaVersion.value), + publishArtifact := isScala210x(scalaVersion.value), + osgiExportAll("com.twitter.bijection.scrooge"), libraryDependencies ++= Seq( - "org.apache.thrift" % "libthrift" % "0.6.1" exclude("junit", "junit"), - withCross("com.twitter" %% "scrooge-serializer" % "3.6.0") - ) + "org.apache.thrift" % "libthrift" % "0.6.1" exclude("junit", "junit") + ) ++ scroogeBuildDeps(scalaVersion.value) ).dependsOn(bijectionCore % "test->test;compile->compile") lazy val bijectionJson = module("json").settings( @@ -215,7 +233,7 @@ object BijectionBuild extends Build { lazy val bijectionUtil = module("util").settings( osgiExportAll("com.twitter.bijection.twitter_util"), - libraryDependencies += withCross("com.twitter" %% "util-core" % "6.3.0") + libraryDependencies += "com.twitter" %% "util-core" % "6.20.0" ).dependsOn(bijectionCore % "test->test;compile->compile") lazy val bijectionClojure = module("clojure").settings( @@ -254,8 +272,9 @@ object BijectionBuild extends Build { lazy val bijectionJson4s = module("json4s").settings( osgiExportAll("com.twitter.bijection.json4s"), libraryDependencies ++= Seq( - "org.json4s" %% "json4s-native" % "3.2.6", - "org.json4s" %% "json4s-ext" % "3.2.6" + "org.scala-lang" % "scala-reflect" % scalaVersion.value, + "org.json4s" %% "json4s-native" % "3.2.10", + "org.json4s" %% "json4s-ext" % "3.2.10" ) ).dependsOn(bijectionCore % "test->test;compile->compile")