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