@@ -121,10 +121,39 @@ final case class AnyValExample(value: String) extends AnyVal
121
121
// Non deterministic
122
122
final case class NonDeterministic (a : Double , b : Double )
123
123
124
+ class ClassWrapper () {
125
+ case class InnerCaseClass (str : String )
126
+
127
+ def runWithImplicit (implicit
128
+ c : Coder [InnerCaseClass ]
129
+ ): Unit =
130
+ InnerCaseClass (" 51" ) coderShould roundtrip()
131
+
132
+ def run (): Unit =
133
+ InnerCaseClass (" 51" ) coderShould roundtrip()
134
+ }
135
+
136
+ object TopLevelObject {
137
+ case class InnerCaseClass (str : String )
138
+ }
139
+
124
140
final class CoderTest extends AnyFlatSpec with Matchers {
141
+
125
142
val userId : UserId = UserId (Seq [Byte ](1 , 2 , 3 , 4 ))
126
143
val user : User = User (userId,
" johndoe" ,
" [email protected] " )
127
144
145
+ /*
146
+ * Case class nested inside another class. Do not move outside
147
+ * */
148
+ case class InnerCaseClass (str : String )
149
+
150
+ /*
151
+ * Object nested inside another class. Do not move outside
152
+ * */
153
+ object InnerObject {
154
+ case class InnerCaseClass (str : String )
155
+ }
156
+
128
157
def materialize [T ](coder : Coder [T ]): BCoder [T ] =
129
158
CoderMaterializer .beam(PipelineOptionsFactory .create(), coder)
130
159
@@ -135,7 +164,7 @@ final class CoderTest extends AnyFlatSpec with Matchers {
135
164
4.5 coderShould roundtrip()
136
165
}
137
166
138
- it should " support Scala collections" in {
167
+ " Coders " should " support Scala collections" in {
139
168
import scala .collection .BitSet
140
169
141
170
val nil : Seq [String ] = Nil
@@ -168,6 +197,73 @@ final class CoderTest extends AnyFlatSpec with Matchers {
168
197
CoderProperties .structuralValueConsistentWithEquals(bmc, m, m)
169
198
}
170
199
200
+ " Coders" should " not support inner case classes" in {
201
+ {
202
+ the[Throwable ] thrownBy {
203
+ InnerObject coderShould roundtrip()
204
+ }
205
+ }.getMessage should include(
206
+ " Found an $outer field in class com.spotify.scio.coders.CoderTest$$"
207
+ )
208
+
209
+ val cw = new ClassWrapper ()
210
+ try {
211
+ cw.runWithImplicit
212
+ throw new Throwable (" Is expected to throw when passing implicit from outer class" )
213
+ } catch {
214
+ case e : NullPointerException =>
215
+ // In this case outer field is called "$cw" and it is hard to wrap it with proper exception
216
+ // so we allow it to fail with NullPointerException
217
+ e.getMessage should be(null )
218
+ }
219
+
220
+ {
221
+ the[Throwable ] thrownBy {
222
+ cw.InnerCaseClass (" 49" ) coderShould roundtrip()
223
+ }
224
+ }.getMessage should startWith(
225
+ " Found an $outer field in class com.spotify.scio.coders.CoderTest$$"
226
+ )
227
+
228
+ {
229
+ the[Throwable ] thrownBy {
230
+ cw.run()
231
+ }
232
+ }.getMessage should startWith(
233
+ " Found an $outer field in class com.spotify.scio.coders.ClassWrapper$$"
234
+ )
235
+
236
+ {
237
+ the[Throwable ] thrownBy {
238
+ InnerCaseClass (" 42" ) coderShould roundtrip()
239
+ }
240
+ }.getMessage should startWith(
241
+ " Found an $outer field in class com.spotify.scio.coders.CoderTest$$"
242
+ )
243
+
244
+ case class ClassInsideMethod (str : String )
245
+
246
+ {
247
+ the[Throwable ] thrownBy {
248
+ ClassInsideMethod (" 50" ) coderShould roundtrip()
249
+ }
250
+ }.getMessage should startWith(
251
+ " Found an $outer field in class com.spotify.scio.coders.CoderTest$$"
252
+ )
253
+
254
+ {
255
+ the[Throwable ] thrownBy {
256
+ InnerObject .InnerCaseClass (" 42" ) coderShould roundtrip()
257
+ }
258
+ }.getMessage should startWith(
259
+ " Found an $outer field in class com.spotify.scio.coders.CoderTest$$"
260
+ )
261
+ }
262
+
263
+ " Coders" should " support inner classes in objects" in {
264
+ TopLevelObject .InnerCaseClass (" 42" ) coderShould roundtrip()
265
+ }
266
+
171
267
it should " support tuples" in {
172
268
import shapeless .syntax .std .tuple ._
173
269
val t22 = (
0 commit comments