1 /*
<lambda>null2  * Copyright (C) 2021 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *    https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.moshi.kotlin.codegen.ksp
17 
18 import com.google.common.truth.Truth.assertThat
19 import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATED
20 import com.squareup.moshi.kotlin.codegen.api.Options.OPTION_GENERATE_PROGUARD_RULES
21 import com.tschuchort.compiletesting.KotlinCompilation
22 import com.tschuchort.compiletesting.SourceFile
23 import com.tschuchort.compiletesting.SourceFile.Companion.java
24 import com.tschuchort.compiletesting.SourceFile.Companion.kotlin
25 import com.tschuchort.compiletesting.kspArgs
26 import com.tschuchort.compiletesting.kspIncremental
27 import com.tschuchort.compiletesting.kspSourcesDir
28 import com.tschuchort.compiletesting.symbolProcessorProviders
29 import org.junit.Ignore
30 import org.junit.Rule
31 import org.junit.Test
32 import org.junit.rules.TemporaryFolder
33 
34 /** Execute kotlinc to confirm that either files are generated or errors are printed. */
35 class JsonClassSymbolProcessorTest {
36 
37   @Rule @JvmField var temporaryFolder: TemporaryFolder = TemporaryFolder()
38 
39   @Test
40   fun privateConstructor() {
41     val result = compile(
42       kotlin(
43         "source.kt",
44         """
45           package test
46           import com.squareup.moshi.JsonClass
47 
48           @JsonClass(generateAdapter = true)
49           class PrivateConstructor private constructor(var a: Int, var b: Int) {
50             fun a() = a
51             fun b() = b
52             companion object {
53               fun newInstance(a: Int, b: Int) = PrivateConstructor(a, b)
54             }
55           }
56           """
57       )
58     )
59     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
60     assertThat(result.messages).contains("constructor is not internal or public")
61   }
62 
63   @Test
64   fun privateConstructorParameter() {
65     val result = compile(
66       kotlin(
67         "source.kt",
68         """
69           package test
70           import com.squareup.moshi.JsonClass
71 
72           @JsonClass(generateAdapter = true)
73           class PrivateConstructorParameter(private var a: Int)
74           """
75       )
76     )
77     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
78     assertThat(result.messages).contains("property a is not visible")
79   }
80 
81   @Test
82   fun privateProperties() {
83     val result = compile(
84       kotlin(
85         "source.kt",
86         """
87           package test
88           import com.squareup.moshi.JsonClass
89 
90           @JsonClass(generateAdapter = true)
91           class PrivateProperties {
92             private var a: Int = -1
93             private var b: Int = -1
94           }
95           """
96       )
97     )
98     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
99     assertThat(result.messages).contains("property a is not visible")
100   }
101 
102   @Test
103   fun interfacesNotSupported() {
104     val result = compile(
105       kotlin(
106         "source.kt",
107         """
108           package test
109           import com.squareup.moshi.JsonClass
110 
111           @JsonClass(generateAdapter = true)
112           interface Interface
113           """
114       )
115     )
116     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
117     assertThat(result.messages).contains(
118       "@JsonClass can't be applied to test.Interface: must be a Kotlin class"
119     )
120   }
121 
122   @Test
123   fun interfacesDoNotErrorWhenGeneratorNotSet() {
124     val result = compile(
125       kotlin(
126         "source.kt",
127         """
128           package test
129           import com.squareup.moshi.JsonClass
130 
131           @JsonClass(generateAdapter = true, generator="customGenerator")
132           interface Interface
133           """
134       )
135     )
136     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
137   }
138 
139   @Test
140   fun abstractClassesNotSupported() {
141     val result = compile(
142       kotlin(
143         "source.kt",
144         """
145           package test
146           import com.squareup.moshi.JsonClass
147 
148           @JsonClass(generateAdapter = true)
149           abstract class AbstractClass(val a: Int)
150           """
151       )
152     )
153     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
154     assertThat(result.messages).contains(
155       "@JsonClass can't be applied to test.AbstractClass: must not be abstract"
156     )
157   }
158 
159   @Test
160   fun sealedClassesNotSupported() {
161     val result = compile(
162       kotlin(
163         "source.kt",
164         """
165           package test
166           import com.squareup.moshi.JsonClass
167 
168           @JsonClass(generateAdapter = true)
169           sealed class SealedClass(val a: Int)
170           """
171       )
172     )
173     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
174     assertThat(result.messages).contains(
175       "@JsonClass can't be applied to test.SealedClass: must not be sealed"
176     )
177   }
178 
179   @Test
180   fun innerClassesNotSupported() {
181     val result = compile(
182       kotlin(
183         "source.kt",
184         """
185           package test
186           import com.squareup.moshi.JsonClass
187 
188           class Outer {
189             @JsonClass(generateAdapter = true)
190             inner class InnerClass(val a: Int)
191           }
192           """
193       )
194     )
195     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
196     assertThat(result.messages).contains(
197       "@JsonClass can't be applied to test.Outer.InnerClass: must not be an inner class"
198     )
199   }
200 
201   @Test
202   fun enumClassesNotSupported() {
203     val result = compile(
204       kotlin(
205         "source.kt",
206         """
207           package test
208           import com.squareup.moshi.JsonClass
209 
210           @JsonClass(generateAdapter = true)
211           enum class KotlinEnum {
212             A, B
213           }
214           """
215       )
216     )
217     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
218     assertThat(result.messages).contains(
219       "@JsonClass with 'generateAdapter = \"true\"' can't be applied to test.KotlinEnum: code gen for enums is not supported or necessary"
220     )
221   }
222 
223   // Annotation processors don't get called for local classes, so we don't have the opportunity to
224   @Ignore
225   @Test
226   fun localClassesNotSupported() {
227     val result = compile(
228       kotlin(
229         "source.kt",
230         """
231           package test
232           import com.squareup.moshi.JsonClass
233 
234           fun outer() {
235             @JsonClass(generateAdapter = true)
236             class LocalClass(val a: Int)
237           }
238           """
239       )
240     )
241     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
242     assertThat(result.messages).contains(
243       "@JsonClass can't be applied to LocalClass: must not be local"
244     )
245   }
246 
247   @Test
248   fun privateClassesNotSupported() {
249     val result = compile(
250       kotlin(
251         "source.kt",
252         """
253           package test
254           import com.squareup.moshi.JsonClass
255 
256           @JsonClass(generateAdapter = true)
257           private class PrivateClass(val a: Int)
258           """
259       )
260     )
261     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
262     assertThat(result.messages).contains(
263       "@JsonClass can't be applied to test.PrivateClass: must be internal or public"
264     )
265   }
266 
267   @Test
268   fun objectDeclarationsNotSupported() {
269     val result = compile(
270       kotlin(
271         "source.kt",
272         """
273           package test
274           import com.squareup.moshi.JsonClass
275 
276           @JsonClass(generateAdapter = true)
277           object ObjectDeclaration {
278             var a = 5
279           }
280           """
281       )
282     )
283     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
284     assertThat(result.messages).contains(
285       "@JsonClass can't be applied to test.ObjectDeclaration: must be a Kotlin class"
286     )
287   }
288 
289   @Test
290   fun objectExpressionsNotSupported() {
291     val result = compile(
292       kotlin(
293         "source.kt",
294         """
295           package test
296           import com.squareup.moshi.JsonClass
297 
298           @JsonClass(generateAdapter = true)
299           val expression = object : Any() {
300             var a = 5
301           }
302           """
303       )
304     )
305     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
306     assertThat(result.messages).contains(
307       "@JsonClass can't be applied to test.expression: must be a Kotlin class"
308     )
309   }
310 
311   @Test
312   fun requiredTransientConstructorParameterFails() {
313     val result = compile(
314       kotlin(
315         "source.kt",
316         """
317           package test
318           import com.squareup.moshi.JsonClass
319 
320           @JsonClass(generateAdapter = true)
321           class RequiredTransientConstructorParameter(@Transient var a: Int)
322           """
323       )
324     )
325     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
326     assertThat(result.messages).contains(
327       "No default value for transient/ignored property a"
328     )
329   }
330 
331   @Test
332   fun requiredIgnoredConstructorParameterFails() {
333     val result = compile(
334       kotlin(
335         "source.kt",
336         """
337           package test
338           import com.squareup.moshi.Json
339           import com.squareup.moshi.JsonClass
340 
341           @JsonClass(generateAdapter = true)
342           class RequiredTransientConstructorParameter(@Json(ignore = true) var a: Int)
343           """
344       )
345     )
346     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
347     assertThat(result.messages).contains(
348       "No default value for transient/ignored property a"
349     )
350   }
351 
352   @Test
353   fun nonPropertyConstructorParameter() {
354     val result = compile(
355       kotlin(
356         "source.kt",
357         """
358           package test
359           import com.squareup.moshi.JsonClass
360 
361           @JsonClass(generateAdapter = true)
362           class NonPropertyConstructorParameter(a: Int, val b: Int)
363           """
364       )
365     )
366     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
367     assertThat(result.messages).contains(
368       "No property for required constructor parameter a"
369     )
370   }
371 
372   @Test
373   fun badGeneratedAnnotation() {
374     val result = prepareCompilation(
375       kotlin(
376         "source.kt",
377         """
378           package test
379           import com.squareup.moshi.JsonClass
380 
381           @JsonClass(generateAdapter = true)
382           data class Foo(val a: Int)
383           """
384       )
385     ).apply {
386       kspArgs[OPTION_GENERATED] = "javax.annotation.GeneratedBlerg"
387     }.compile()
388     assertThat(result.messages).contains(
389       "Invalid option value for $OPTION_GENERATED"
390     )
391   }
392 
393   @Test
394   fun disableProguardGeneration() {
395     val compilation = prepareCompilation(
396       kotlin(
397         "source.kt",
398         """
399           package test
400           import com.squareup.moshi.JsonClass
401 
402           @JsonClass(generateAdapter = true)
403           data class Foo(val a: Int)
404           """
405       )
406     ).apply {
407       kspArgs[OPTION_GENERATE_PROGUARD_RULES] = "false"
408     }
409     val result = compilation.compile()
410     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
411     assertThat(compilation.kspSourcesDir.walkTopDown().filter { it.extension == "pro" }.toList()).isEmpty()
412   }
413 
414   @Test
415   fun multipleErrors() {
416     val result = compile(
417       kotlin(
418         "source.kt",
419         """
420           package test
421           import com.squareup.moshi.JsonClass
422 
423           @JsonClass(generateAdapter = true)
424           class Class1(private var a: Int, private var b: Int)
425 
426           @JsonClass(generateAdapter = true)
427           class Class2(private var c: Int)
428           """
429       )
430     )
431     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
432     assertThat(result.messages).contains("property a is not visible")
433     assertThat(result.messages).contains("property c is not visible")
434   }
435 
436   @Test
437   fun extendPlatformType() {
438     val result = compile(
439       kotlin(
440         "source.kt",
441         """
442           package test
443           import com.squareup.moshi.JsonClass
444           import java.util.Date
445 
446           @JsonClass(generateAdapter = true)
447           class ExtendsPlatformClass(var a: Int) : Date()
448           """
449       )
450     )
451     assertThat(result.messages).contains("supertype java.util.Date is not a Kotlin type")
452   }
453 
454   @Test
455   fun extendJavaType() {
456     val result = compile(
457       kotlin(
458         "source.kt",
459         """
460           package test
461           import com.squareup.moshi.JsonClass
462           import com.squareup.moshi.kotlin.codegen.JavaSuperclass
463 
464           @JsonClass(generateAdapter = true)
465           class ExtendsJavaType(var b: Int) : JavaSuperclass()
466           """
467       ),
468       java(
469         "JavaSuperclass.java",
470         """
471         package com.squareup.moshi.kotlin.codegen;
472         public class JavaSuperclass {
473           public int a = 1;
474         }
475         """
476       )
477     )
478     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
479     assertThat(result.messages)
480       .contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type")
481   }
482 
483   @Test
484   fun nonFieldApplicableQualifier() {
485     val result = compile(
486       kotlin(
487         "source.kt",
488         """
489           package test
490           import com.squareup.moshi.JsonClass
491           import com.squareup.moshi.JsonQualifier
492           import kotlin.annotation.AnnotationRetention.RUNTIME
493           import kotlin.annotation.AnnotationTarget.PROPERTY
494           import kotlin.annotation.Retention
495           import kotlin.annotation.Target
496 
497           @Retention(RUNTIME)
498           @Target(PROPERTY)
499           @JsonQualifier
500           annotation class UpperCase
501 
502           @JsonClass(generateAdapter = true)
503           class ClassWithQualifier(@UpperCase val a: Int)
504           """
505       )
506     )
507     // We instantiate directly, no FIELD site target necessary
508     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
509   }
510 
511   @Test
512   fun nonRuntimeQualifier() {
513     val result = compile(
514       kotlin(
515         "source.kt",
516         """
517           package test
518           import com.squareup.moshi.JsonClass
519           import com.squareup.moshi.JsonQualifier
520           import kotlin.annotation.AnnotationRetention.BINARY
521           import kotlin.annotation.AnnotationTarget.FIELD
522           import kotlin.annotation.AnnotationTarget.PROPERTY
523           import kotlin.annotation.Retention
524           import kotlin.annotation.Target
525 
526           @Retention(BINARY)
527           @Target(PROPERTY, FIELD)
528           @JsonQualifier
529           annotation class UpperCase
530 
531           @JsonClass(generateAdapter = true)
532           class ClassWithQualifier(@UpperCase val a: Int)
533           """
534       )
535     )
536     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
537     assertThat(result.messages).contains("JsonQualifier @UpperCase must have RUNTIME retention")
538   }
539 
540   @Test
541   fun invalidGenericSyntaxErrorMessaging() {
542     val result = compile(
543       kotlin(
544         "source.kt",
545         """
546           package test
547           import com.squareup.moshi.JsonClass
548 
549           @JsonClass(generateAdapter = true)
550           data class ElementEnvelope(val elements: List)
551           """,
552       ),
553     )
554     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
555     assertThat(result.messages).contains("Error preparing ElementEnvelope")
556   }
557 
558   @Test
559   fun `TypeAliases with the same backing type should share the same adapter`() {
560     val result = compile(
561       kotlin(
562         "source.kt",
563         """
564           package test
565           import com.squareup.moshi.JsonClass
566 
567           typealias FirstName = String
568           typealias LastName = String
569 
570           @JsonClass(generateAdapter = true)
571           data class Person(val firstName: FirstName, val lastName: LastName, val hairColor: String)
572           """
573       )
574     )
575     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
576 
577     // We're checking here that we only generate one `stringAdapter` that's used for both the
578     // regular string properties as well as the the aliased ones.
579     // TODO loading compiled classes from results not supported in KSP yet
580 //    val adapterClass = result.classLoader.loadClass("PersonJsonAdapter").kotlin
581 //    assertThat(adapterClass.declaredMemberProperties.map { it.returnType }).containsExactly(
582 //      JsonReader.Options::class.createType(),
583 //      JsonAdapter::class.parameterizedBy(String::class)
584 //    )
585   }
586 
587   @Test
588   fun `Processor should generate comprehensive proguard rules`() {
589     val compilation = prepareCompilation(
590       kotlin(
591         "source.kt",
592         """
593           package testPackage
594           import com.squareup.moshi.JsonClass
595           import com.squareup.moshi.JsonQualifier
596 
597           typealias FirstName = String
598           typealias LastName = String
599 
600           @JsonClass(generateAdapter = true)
601           data class Aliases(val firstName: FirstName, val lastName: LastName, val hairColor: String)
602 
603           @JsonClass(generateAdapter = true)
604           data class Simple(val firstName: String)
605 
606           @JsonClass(generateAdapter = true)
607           data class Generic<T>(val firstName: T, val lastName: String)
608 
609           @JsonQualifier
610           annotation class MyQualifier
611 
612           @JsonClass(generateAdapter = true)
613           data class UsingQualifiers(val firstName: String, @MyQualifier val lastName: String)
614 
615           @JsonClass(generateAdapter = true)
616           data class MixedTypes(val firstName: String, val otherNames: MutableList<String>)
617 
618           @JsonClass(generateAdapter = true)
619           data class DefaultParams(val firstName: String = "")
620 
621           @JsonClass(generateAdapter = true)
622           data class Complex<T>(val firstName: FirstName = "", @MyQualifier val names: MutableList<String>, val genericProp: T)
623 
624           object NestedType {
625             @JsonQualifier
626             annotation class NestedQualifier
627 
628             @JsonClass(generateAdapter = true)
629             data class NestedSimple(@NestedQualifier val firstName: String)
630           }
631 
632           @JsonClass(generateAdapter = true)
633           class MultipleMasks(
634               val arg0: Long = 0,
635               val arg1: Long = 1,
636               val arg2: Long = 2,
637               val arg3: Long = 3,
638               val arg4: Long = 4,
639               val arg5: Long = 5,
640               val arg6: Long = 6,
641               val arg7: Long = 7,
642               val arg8: Long = 8,
643               val arg9: Long = 9,
644               val arg10: Long = 10,
645               val arg11: Long,
646               val arg12: Long = 12,
647               val arg13: Long = 13,
648               val arg14: Long = 14,
649               val arg15: Long = 15,
650               val arg16: Long = 16,
651               val arg17: Long = 17,
652               val arg18: Long = 18,
653               val arg19: Long = 19,
654               @Suppress("UNUSED_PARAMETER") arg20: Long = 20,
655               val arg21: Long = 21,
656               val arg22: Long = 22,
657               val arg23: Long = 23,
658               val arg24: Long = 24,
659               val arg25: Long = 25,
660               val arg26: Long = 26,
661               val arg27: Long = 27,
662               val arg28: Long = 28,
663               val arg29: Long = 29,
664               val arg30: Long = 30,
665               val arg31: Long = 31,
666               val arg32: Long = 32,
667               val arg33: Long = 33,
668               val arg34: Long = 34,
669               val arg35: Long = 35,
670               val arg36: Long = 36,
671               val arg37: Long = 37,
672               val arg38: Long = 38,
673               @Transient val arg39: Long = 39,
674               val arg40: Long = 40,
675               val arg41: Long = 41,
676               val arg42: Long = 42,
677               val arg43: Long = 43,
678               val arg44: Long = 44,
679               val arg45: Long = 45,
680               val arg46: Long = 46,
681               val arg47: Long = 47,
682               val arg48: Long = 48,
683               val arg49: Long = 49,
684               val arg50: Long = 50,
685               val arg51: Long = 51,
686               val arg52: Long = 52,
687               @Transient val arg53: Long = 53,
688               val arg54: Long = 54,
689               val arg55: Long = 55,
690               val arg56: Long = 56,
691               val arg57: Long = 57,
692               val arg58: Long = 58,
693               val arg59: Long = 59,
694               val arg60: Long = 60,
695               val arg61: Long = 61,
696               val arg62: Long = 62,
697               val arg63: Long = 63,
698               val arg64: Long = 64,
699               val arg65: Long = 65
700           )
701           """
702       )
703     )
704     val result = compilation.compile()
705     assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
706 
707     compilation.kspSourcesDir.walkTopDown().filter { it.extension == "pro" }.forEach { generatedFile ->
708       when (generatedFile.nameWithoutExtension) {
709         "moshi-testPackage.Aliases" -> assertThat(generatedFile.readText()).contains(
710           """
711           -if class testPackage.Aliases
712           -keepnames class testPackage.Aliases
713           -if class testPackage.Aliases
714           -keep class testPackage.AliasesJsonAdapter {
715               public <init>(com.squareup.moshi.Moshi);
716           }
717           """.trimIndent()
718         )
719         "moshi-testPackage.Simple" -> assertThat(generatedFile.readText()).contains(
720           """
721           -if class testPackage.Simple
722           -keepnames class testPackage.Simple
723           -if class testPackage.Simple
724           -keep class testPackage.SimpleJsonAdapter {
725               public <init>(com.squareup.moshi.Moshi);
726           }
727           """.trimIndent()
728         )
729         "moshi-testPackage.Generic" -> assertThat(generatedFile.readText()).contains(
730           """
731           -if class testPackage.Generic
732           -keepnames class testPackage.Generic
733           -if class testPackage.Generic
734           -keep class testPackage.GenericJsonAdapter {
735               public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
736           }
737           """.trimIndent()
738         )
739         "moshi-testPackage.UsingQualifiers" -> {
740           assertThat(generatedFile.readText()).contains(
741             """
742             -if class testPackage.UsingQualifiers
743             -keepnames class testPackage.UsingQualifiers
744             -if class testPackage.UsingQualifiers
745             -keep class testPackage.UsingQualifiersJsonAdapter {
746                 public <init>(com.squareup.moshi.Moshi);
747             }
748             """.trimIndent()
749           )
750         }
751         "moshi-testPackage.MixedTypes" -> assertThat(generatedFile.readText()).contains(
752           """
753           -if class testPackage.MixedTypes
754           -keepnames class testPackage.MixedTypes
755           -if class testPackage.MixedTypes
756           -keep class testPackage.MixedTypesJsonAdapter {
757               public <init>(com.squareup.moshi.Moshi);
758           }
759           """.trimIndent()
760         )
761         "moshi-testPackage.DefaultParams" -> assertThat(generatedFile.readText()).contains(
762           """
763           -if class testPackage.DefaultParams
764           -keepnames class testPackage.DefaultParams
765           -if class testPackage.DefaultParams
766           -keep class testPackage.DefaultParamsJsonAdapter {
767               public <init>(com.squareup.moshi.Moshi);
768           }
769           -if class testPackage.DefaultParams
770           -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
771           -if class testPackage.DefaultParams
772           -keepclassmembers class testPackage.DefaultParams {
773               public synthetic <init>(java.lang.String,int,kotlin.jvm.internal.DefaultConstructorMarker);
774           }
775           """.trimIndent()
776         )
777         "moshi-testPackage.Complex" -> {
778           assertThat(generatedFile.readText()).contains(
779             """
780             -if class testPackage.Complex
781             -keepnames class testPackage.Complex
782             -if class testPackage.Complex
783             -keep class testPackage.ComplexJsonAdapter {
784                 public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
785             }
786             -if class testPackage.Complex
787             -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
788             -if class testPackage.Complex
789             -keepclassmembers class testPackage.Complex {
790                 public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
791             }
792             """.trimIndent()
793           )
794         }
795         "moshi-testPackage.MultipleMasks" -> assertThat(generatedFile.readText()).contains(
796           """
797           -if class testPackage.MultipleMasks
798           -keepnames class testPackage.MultipleMasks
799           -if class testPackage.MultipleMasks
800           -keep class testPackage.MultipleMasksJsonAdapter {
801               public <init>(com.squareup.moshi.Moshi);
802           }
803           -if class testPackage.MultipleMasks
804           -keepnames class kotlin.jvm.internal.DefaultConstructorMarker
805           -if class testPackage.MultipleMasks
806           -keepclassmembers class testPackage.MultipleMasks {
807               public synthetic <init>(long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,int,int,int,kotlin.jvm.internal.DefaultConstructorMarker);
808           }
809           """.trimIndent()
810         )
811         "moshi-testPackage.NestedType.NestedSimple" -> {
812           assertThat(generatedFile.readText()).contains(
813             """
814             -if class testPackage.NestedType${'$'}NestedSimple
815             -keepnames class testPackage.NestedType${'$'}NestedSimple
816             -if class testPackage.NestedType${'$'}NestedSimple
817             -keep class testPackage.NestedType_NestedSimpleJsonAdapter {
818                 public <init>(com.squareup.moshi.Moshi);
819             }
820             """.trimIndent()
821           )
822         }
823         else -> error("Unexpected proguard file! ${generatedFile.name}")
824       }
825     }
826   }
827 
828   private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation {
829     return KotlinCompilation()
830       .apply {
831         workingDir = temporaryFolder.root
832         inheritClassPath = true
833         symbolProcessorProviders = listOf(JsonClassSymbolProcessorProvider())
834         sources = sourceFiles.asList()
835         verbose = false
836         kspIncremental = true // The default now
837       }
838   }
839 
840   private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result {
841     return prepareCompilation(*sourceFiles).compile()
842   }
843 }
844