-
Notifications
You must be signed in to change notification settings - Fork 513
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Respecting required field of inner case classes in Coder macro (#4645)
* Respecting required field of inner case classes in Coder macro * Fix scala 2.12 compilation * fix scalafix+compile errors * Added negative unsupported test scenarios * Failing on all inner classes * Update scio-test/src/test/scala/com/spotify/scio/coders/CoderTest.scala Co-authored-by: Michel Davit <[email protected]> * Addressing the comment Co-authored-by: Michel Davit <[email protected]>
- Loading branch information
1 parent
ea7cb3d
commit 7242316
Showing
3 changed files
with
144 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -121,10 +121,39 @@ final case class AnyValExample(value: String) extends AnyVal | |
// Non deterministic | ||
final case class NonDeterministic(a: Double, b: Double) | ||
|
||
class ClassWrapper() { | ||
case class InnerCaseClass(str: String) | ||
|
||
def runWithImplicit(implicit | ||
c: Coder[InnerCaseClass] | ||
): Unit = | ||
InnerCaseClass("51") coderShould roundtrip() | ||
|
||
def run(): Unit = | ||
InnerCaseClass("51") coderShould roundtrip() | ||
} | ||
|
||
object TopLevelObject { | ||
case class InnerCaseClass(str: String) | ||
} | ||
|
||
final class CoderTest extends AnyFlatSpec with Matchers { | ||
|
||
val userId: UserId = UserId(Seq[Byte](1, 2, 3, 4)) | ||
val user: User = User(userId, "johndoe", "[email protected]") | ||
|
||
/* | ||
* Case class nested inside another class. Do not move outside | ||
* */ | ||
case class InnerCaseClass(str: String) | ||
|
||
/* | ||
* Object nested inside another class. Do not move outside | ||
* */ | ||
object InnerObject { | ||
case class InnerCaseClass(str: String) | ||
} | ||
|
||
def materialize[T](coder: Coder[T]): BCoder[T] = | ||
CoderMaterializer.beam(PipelineOptionsFactory.create(), coder) | ||
|
||
|
@@ -135,7 +164,7 @@ final class CoderTest extends AnyFlatSpec with Matchers { | |
4.5 coderShould roundtrip() | ||
} | ||
|
||
it should "support Scala collections" in { | ||
"Coders" should "support Scala collections" in { | ||
import scala.collection.BitSet | ||
|
||
val nil: Seq[String] = Nil | ||
|
@@ -168,6 +197,73 @@ final class CoderTest extends AnyFlatSpec with Matchers { | |
CoderProperties.structuralValueConsistentWithEquals(bmc, m, m) | ||
} | ||
|
||
"Coders" should "not support inner case classes" in { | ||
{ | ||
the[Throwable] thrownBy { | ||
InnerObject coderShould roundtrip() | ||
} | ||
}.getMessage should include( | ||
"Found an $outer field in class com.spotify.scio.coders.CoderTest$$" | ||
) | ||
|
||
val cw = new ClassWrapper() | ||
try { | ||
cw.runWithImplicit | ||
throw new Throwable("Is expected to throw when passing implicit from outer class") | ||
} catch { | ||
case e: NullPointerException => | ||
// In this case outer field is called "$cw" and it is hard to wrap it with proper exception | ||
// so we allow it to fail with NullPointerException | ||
e.getMessage should be(null) | ||
} | ||
|
||
{ | ||
the[Throwable] thrownBy { | ||
cw.InnerCaseClass("49") coderShould roundtrip() | ||
} | ||
}.getMessage should startWith( | ||
"Found an $outer field in class com.spotify.scio.coders.CoderTest$$" | ||
) | ||
|
||
{ | ||
the[Throwable] thrownBy { | ||
cw.run() | ||
} | ||
}.getMessage should startWith( | ||
"Found an $outer field in class com.spotify.scio.coders.ClassWrapper$$" | ||
) | ||
|
||
{ | ||
the[Throwable] thrownBy { | ||
InnerCaseClass("42") coderShould roundtrip() | ||
} | ||
}.getMessage should startWith( | ||
"Found an $outer field in class com.spotify.scio.coders.CoderTest$$" | ||
) | ||
|
||
case class ClassInsideMethod(str: String) | ||
|
||
{ | ||
the[Throwable] thrownBy { | ||
ClassInsideMethod("50") coderShould roundtrip() | ||
} | ||
}.getMessage should startWith( | ||
"Found an $outer field in class com.spotify.scio.coders.CoderTest$$" | ||
) | ||
|
||
{ | ||
the[Throwable] thrownBy { | ||
InnerObject.InnerCaseClass("42") coderShould roundtrip() | ||
} | ||
}.getMessage should startWith( | ||
"Found an $outer field in class com.spotify.scio.coders.CoderTest$$" | ||
) | ||
} | ||
|
||
"Coders" should "support inner classes in objects" in { | ||
TopLevelObject.InnerCaseClass("42") coderShould roundtrip() | ||
} | ||
|
||
it should "support tuples" in { | ||
import shapeless.syntax.std.tuple._ | ||
val t22 = ( | ||
|