mirror of
https://github.com/fankes/moshi.git
synced 2025-10-18 23:49:21 +08:00
Change the directory structure to match our modules (#1451)
This commit is contained in:
69
moshi-kotlin-tests/build.gradle.kts
Normal file
69
moshi-kotlin-tests/build.gradle.kts
Normal file
@@ -0,0 +1,69 @@
|
||||
import Build_gradle.TestMode.KAPT
|
||||
import Build_gradle.TestMode.KSP
|
||||
import Build_gradle.TestMode.REFLECT
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("kapt") apply false
|
||||
id("com.google.devtools.ksp") apply false
|
||||
}
|
||||
|
||||
enum class TestMode {
|
||||
REFLECT, KAPT, KSP
|
||||
}
|
||||
|
||||
val testMode = findProperty("kotlinTestMode")?.toString()
|
||||
?.let(TestMode::valueOf)
|
||||
?: REFLECT
|
||||
|
||||
when (testMode) {
|
||||
REFLECT -> {
|
||||
// Do nothing!
|
||||
}
|
||||
KAPT -> {
|
||||
apply(plugin = "org.jetbrains.kotlin.kapt")
|
||||
}
|
||||
KSP -> {
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
// ExtendsPlatformClassWithProtectedField tests a case where we set a protected ByteArrayOutputStream.buf field
|
||||
jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED")
|
||||
}
|
||||
|
||||
val useWError = findProperty("kotlinLanguageVersion")?.toString()
|
||||
?.startsWith("1.5")
|
||||
?: false
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = useWError
|
||||
@Suppress("SuspiciousCollectionReassignment")
|
||||
freeCompilerArgs += listOf(
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
when (testMode) {
|
||||
REFLECT -> {
|
||||
// Do nothing
|
||||
}
|
||||
KAPT -> {
|
||||
"kaptTest"(project(":moshi-kotlin-codegen"))
|
||||
}
|
||||
KSP -> {
|
||||
"kspTest"(project(":moshi-kotlin-codegen"))
|
||||
}
|
||||
}
|
||||
testImplementation(project(":moshi"))
|
||||
testImplementation(project(":moshi-kotlin"))
|
||||
testImplementation(project(":moshi-kotlin-tests:extra-moshi-test-module"))
|
||||
testImplementation(kotlin("reflect"))
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.assertj)
|
||||
testImplementation(libs.truth)
|
||||
}
|
71
moshi-kotlin-tests/codegen-only/build.gradle.kts
Normal file
71
moshi-kotlin-tests/codegen-only/build.gradle.kts
Normal file
@@ -0,0 +1,71 @@
|
||||
import Build_gradle.TestMode.KAPT
|
||||
import Build_gradle.TestMode.KSP
|
||||
import Build_gradle.TestMode.REFLECT
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("kapt") apply false
|
||||
id("com.google.devtools.ksp") apply false
|
||||
}
|
||||
|
||||
enum class TestMode {
|
||||
REFLECT, KAPT, KSP
|
||||
}
|
||||
|
||||
val testMode = findProperty("kotlinTestMode")?.toString()
|
||||
?.let(TestMode::valueOf)
|
||||
?: KSP
|
||||
|
||||
when (testMode) {
|
||||
REFLECT -> {
|
||||
// Default to KSP. This is a CI-only thing
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
}
|
||||
KAPT -> {
|
||||
apply(plugin = "org.jetbrains.kotlin.kapt")
|
||||
}
|
||||
KSP -> {
|
||||
apply(plugin = "com.google.devtools.ksp")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
// ExtendsPlatformClassWithProtectedField tests a case where we set a protected ByteArrayOutputStream.buf field
|
||||
jvmArgs("--add-opens=java.base/java.io=ALL-UNNAMED")
|
||||
}
|
||||
|
||||
val useWError = findProperty("kotlinLanguageVersion")?.toString()
|
||||
?.startsWith("1.5")
|
||||
?: false
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = useWError
|
||||
@Suppress("SuspiciousCollectionReassignment")
|
||||
freeCompilerArgs += listOf(
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
when (testMode) {
|
||||
REFLECT -> {
|
||||
// Default to KSP in this case, this is a CI-only thing
|
||||
"kspTest"(project(":moshi-kotlin-codegen"))
|
||||
}
|
||||
KAPT -> {
|
||||
"kaptTest"(project(":moshi-kotlin-codegen"))
|
||||
}
|
||||
KSP -> {
|
||||
"kspTest"(project(":moshi-kotlin-codegen"))
|
||||
}
|
||||
}
|
||||
testImplementation(project(":moshi"))
|
||||
testImplementation(project(":moshi-kotlin"))
|
||||
testImplementation(project(":moshi-kotlin-tests:extra-moshi-test-module"))
|
||||
testImplementation(kotlin("reflect"))
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.assertj)
|
||||
testImplementation(libs.truth)
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.kotlin.codegen.test.extra.AbstractClassInModuleA
|
||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
import kotlin.annotation.AnnotationTarget.TYPE
|
||||
|
||||
/*
|
||||
* These are classes that need only compile.
|
||||
*/
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/905
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GenericTestClassWithDefaults<T>(
|
||||
val input: String = "",
|
||||
val genericInput: T
|
||||
)
|
||||
|
||||
@Target(TYPE)
|
||||
annotation class TypeAnnotation
|
||||
|
||||
/**
|
||||
* Compilation-only test to ensure we don't render types with their annotations.
|
||||
* Regression test for https://github.com/square/moshi/issues/1033
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAnnotationClass(
|
||||
val propertyWithAnnotatedType: @TypeAnnotation String = "",
|
||||
val generic: List<@TypeAnnotation String>
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/1277
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OtherTestModel(val TestModel: TestModel? = null)
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TestModel(
|
||||
val someVariable: Int,
|
||||
val anotherVariable: String
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/1022
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class MismatchParentAndNestedClassVisibility(
|
||||
val type: Int,
|
||||
val name: String? = null
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NestedClass(
|
||||
val nestedProperty: String
|
||||
)
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/1052
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class KeysWithSpaces(
|
||||
@Json(name = "1. Information") val information: String,
|
||||
@Json(name = "2. Symbol") val symbol: String,
|
||||
@Json(name = "3. Last Refreshed") val lastRefreshed: String,
|
||||
@Json(name = "4. Interval") val interval: String,
|
||||
@Json(name = "5. Output Size") val size: String,
|
||||
@Json(name = "6. Time Zone") val timeZone: String
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/848
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Hotwords(
|
||||
val `class`: List<String>?
|
||||
)
|
||||
|
||||
/**
|
||||
* This is here mostly just to ensure it still compiles. Covers variance, @Json, default values,
|
||||
* nullability, primitive arrays, and some wacky generics.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SmokeTestType(
|
||||
@Json(name = "first_name") val firstName: String,
|
||||
@Json(name = "last_name") val lastName: String,
|
||||
val age: Int,
|
||||
val nationalities: List<String> = emptyList(),
|
||||
val weight: Float,
|
||||
val tattoos: Boolean = false,
|
||||
val race: String?,
|
||||
val hasChildren: Boolean = false,
|
||||
val favoriteFood: String? = null,
|
||||
val favoriteDrink: String? = "Water",
|
||||
val wildcardOut: MutableList<out String> = mutableListOf(),
|
||||
val nullableWildcardOut: MutableList<out String?> = mutableListOf(),
|
||||
val wildcardIn: Array<in String>,
|
||||
val any: List<*>,
|
||||
val anyTwo: List<Any>,
|
||||
val anyOut: MutableList<out Any>,
|
||||
val nullableAnyOut: MutableList<out Any?>,
|
||||
val favoriteThreeNumbers: IntArray,
|
||||
val favoriteArrayValues: Array<String>,
|
||||
val favoriteNullableArrayValues: Array<String?>,
|
||||
val nullableSetListMapArrayNullableIntWithDefault: Set<List<Map<String, Array<IntArray?>>>>? = null,
|
||||
val aliasedName: TypeAliasName = "Woah",
|
||||
val genericAlias: GenericTypeAlias = listOf("Woah"),
|
||||
// Regression test for https://github.com/square/moshi/issues/1272
|
||||
val nestedArray: Array<Map<String, Any>>? = null
|
||||
)
|
||||
|
||||
typealias TypeAliasName = String
|
||||
typealias GenericTypeAlias = List<String>
|
||||
|
||||
// Regression test for enum constants in annotations and array types
|
||||
// https://github.com/ZacSweers/MoshiX/issues/103
|
||||
@Retention(RUNTIME)
|
||||
@JsonQualifier
|
||||
annotation class UpperCase(val foo: Array<Foo>)
|
||||
|
||||
enum class Foo { BAR }
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ClassWithQualifier(
|
||||
@UpperCase(foo = [Foo.BAR])
|
||||
val a: Int
|
||||
)
|
||||
|
||||
// Regression for https://github.com/ZacSweers/MoshiX/issues/120
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class DataClassInModuleB(
|
||||
val id: String
|
||||
) : AbstractClassInModuleA()
|
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@file:Suppress("UNUSED", "UNUSED_PARAMETER")
|
||||
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.Test
|
||||
|
||||
class ComplexGenericsInheritanceTest {
|
||||
|
||||
private val moshi = Moshi.Builder().build()
|
||||
|
||||
@Test
|
||||
fun simple() {
|
||||
val adapter = moshi.adapter<PersonResponse>()
|
||||
|
||||
@Language("JSON")
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = PersonResponse().apply {
|
||||
data = Person("foo")
|
||||
}
|
||||
assertThat(instance).isEqualTo(testInstance)
|
||||
assertThat(adapter.toJson(instance)).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nested() {
|
||||
val adapter = moshi.adapter<NestedPersonResponse>()
|
||||
|
||||
@Language("JSON")
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = NestedPersonResponse().apply {
|
||||
data = Person("foo")
|
||||
}
|
||||
assertThat(instance).isEqualTo(testInstance)
|
||||
assertThat(adapter.toJson(instance)).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun untyped() {
|
||||
val adapter = moshi.adapter<UntypedNestedPersonResponse<Person>>()
|
||||
|
||||
@Language("JSON")
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = UntypedNestedPersonResponse<Person>().apply {
|
||||
data = Person("foo")
|
||||
}
|
||||
assertThat(instance).isEqualTo(testInstance)
|
||||
assertThat(adapter.toJson(instance)).isEqualTo(json)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun complex() {
|
||||
val adapter = moshi.adapter<Layer4<Person, UntypedNestedPersonResponse<Person>>>()
|
||||
|
||||
@Language("JSON")
|
||||
val json =
|
||||
"""{"layer4E":{"name":"layer4E"},"layer4F":{"data":{"name":"layer4F"},"data2":"layer4F","data3":"layer4F"},"layer3C":[1,2,3],"layer3D":"layer3D","layer2":"layer2","layer1":"layer1"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = Layer4(
|
||||
layer4E = Person("layer4E"),
|
||||
layer4F = UntypedNestedPersonResponse<Person>().apply {
|
||||
data = Person("layer4F")
|
||||
data2 = "layer4F"
|
||||
data3 = "layer4F"
|
||||
}
|
||||
).apply {
|
||||
layer3C = listOf(1, 2, 3)
|
||||
layer3D = "layer3D"
|
||||
layer2 = "layer2"
|
||||
layer1 = "layer1"
|
||||
}
|
||||
assertThat(instance).isEqualTo(testInstance)
|
||||
assertThat(adapter.toJson(testInstance)).isEqualTo(json)
|
||||
}
|
||||
}
|
||||
|
||||
open class ResponseWithSettableProperty<T, R> {
|
||||
var data: T? = null
|
||||
var data2: R? = null
|
||||
var data3: R? = null
|
||||
}
|
||||
|
||||
interface Personable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Person(val name: String) : Personable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PersonResponse(
|
||||
val extra: String? = null
|
||||
) : ResponseWithSettableProperty<Person, String>()
|
||||
|
||||
abstract class NestedResponse<T : Personable> : ResponseWithSettableProperty<T, String>()
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NestedPersonResponse(val extra: String? = null) : NestedResponse<Person>()
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UntypedNestedPersonResponse<T : Personable>(
|
||||
val extra: String? = null
|
||||
) : NestedResponse<T>()
|
||||
|
||||
interface LayerInterface<I>
|
||||
|
||||
abstract class Layer1<A> {
|
||||
var layer1: A? = null
|
||||
}
|
||||
|
||||
abstract class Layer2<B> : Layer1<B>(), LayerInterface<B> {
|
||||
var layer2: B? = null
|
||||
}
|
||||
|
||||
abstract class Layer3<C, D> : Layer2<D>() {
|
||||
var layer3C: C? = null
|
||||
var layer3D: D? = null
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Layer4<E : Personable, F>(
|
||||
val layer4E: E,
|
||||
val layer4F: F? = null
|
||||
) : Layer3<List<Int>, String>(), LayerInterface<String>
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultConstructorTest {
|
||||
|
||||
@Test fun minimal() {
|
||||
val expected = TestClass("requiredClass")
|
||||
val json =
|
||||
"""{"required":"requiredClass"}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>()
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun allSet() {
|
||||
val expected = TestClass("requiredClass", "customOptional", 4, "setDynamic", 5, 6)
|
||||
val json =
|
||||
"""{"required":"requiredClass","optional":"customOptional","optional2":4,"dynamicSelfReferenceOptional":"setDynamic","dynamicOptional":5,"dynamicInlineOptional":6}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>()
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun customDynamic() {
|
||||
val expected = TestClass("requiredClass", "customOptional")
|
||||
val json =
|
||||
"""{"required":"requiredClass","optional":"customOptional"}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>()
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TestClass(
|
||||
val required: String,
|
||||
val optional: String = "optional",
|
||||
val optional2: Int = 2,
|
||||
val dynamicSelfReferenceOptional: String = required,
|
||||
val dynamicOptional: Int = createInt(),
|
||||
val dynamicInlineOptional: Int = createInlineInt()
|
||||
)
|
||||
|
||||
private fun createInt(): Int {
|
||||
return 3
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun createInlineInt(): Int {
|
||||
return 3
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.kotlin.codegen.GeneratedAdaptersTest.CustomGeneratedClass
|
||||
|
||||
// This also tests custom generated types with no moshi constructor
|
||||
class GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter : JsonAdapter<CustomGeneratedClass>() {
|
||||
override fun fromJson(reader: JsonReader): CustomGeneratedClass? {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: CustomGeneratedClass?) {
|
||||
TODO()
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen.LooksLikeAClass
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* https://github.com/square/moshi/issues/783
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ClassInPackageThatLooksLikeAClass(val foo: String)
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import org.junit.Test
|
||||
|
||||
class MixingReflectAndCodeGen {
|
||||
@Test
|
||||
fun mixingReflectionAndCodegen() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val generatedAdapter = moshi.adapter<UsesGeneratedAdapter>()
|
||||
val reflectionAdapter = moshi.adapter<UsesReflectionAdapter>()
|
||||
|
||||
assertThat(generatedAdapter.toString())
|
||||
.isEqualTo("GeneratedJsonAdapter(MixingReflectAndCodeGen.UsesGeneratedAdapter).nullSafe()")
|
||||
assertThat(reflectionAdapter.toString())
|
||||
.isEqualTo(
|
||||
"KotlinJsonAdapter(com.squareup.moshi.kotlin.codegen.MixingReflectAndCodeGen" +
|
||||
".UsesReflectionAdapter).nullSafe()"
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class UsesGeneratedAdapter(var a: Int, var b: Int)
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
class UsesReflectionAdapter(var a: Int, var b: Int)
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import org.junit.Test
|
||||
|
||||
// Regression tests specific to Moshi-KSP
|
||||
class MoshiKspTest {
|
||||
private val moshi = Moshi.Builder().build()
|
||||
|
||||
// Regression test for https://github.com/ZacSweers/MoshiX/issues/44
|
||||
@Test
|
||||
fun onlyInterfaceSupertypes() {
|
||||
val adapter = moshi.adapter<SimpleImpl>()
|
||||
//language=JSON
|
||||
val json = """{"a":"aValue","b":"bValue"}"""
|
||||
val expected = SimpleImpl("aValue", "bValue")
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance).isEqualTo(expected)
|
||||
val encoded = adapter.toJson(instance)
|
||||
assertThat(encoded).isEqualTo(json)
|
||||
}
|
||||
|
||||
interface SimpleInterface {
|
||||
val a: String
|
||||
}
|
||||
|
||||
// NOTE the Any() superclass is important to test that we're detecting the farthest parent class
|
||||
// correct.y
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SimpleImpl(override val a: String, val b: String) : Any(), SimpleInterface
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* This test explicitly tests mask generation for classes with more than 32 parameters. Each mask
|
||||
* can only indicate up to 32 parameters, so constructors with more than 32 parameters have to use
|
||||
* multiple masks.
|
||||
*
|
||||
* This covers a few cases of this:
|
||||
* - Ensuring values from json are matched to properties correctly
|
||||
* - Some `@Transient` parameters (which participate in the constructor signature and mask indices)
|
||||
* - This example has 3 total masks generated.
|
||||
*
|
||||
* Regression test for https://github.com/square/moshi/issues/977
|
||||
*/
|
||||
class MultipleMasksTest {
|
||||
@Test fun testMultipleMasks() {
|
||||
|
||||
// Set some arbitrary values to make sure offsets are aligning correctly
|
||||
@Language("JSON")
|
||||
val json =
|
||||
"""{"arg50":500,"arg3":34,"arg11":11,"arg65":67}"""
|
||||
|
||||
val instance = Moshi.Builder().build().adapter<MultipleMasks>()
|
||||
.fromJson(json)!!
|
||||
|
||||
assertEquals(instance.arg2, 2)
|
||||
assertEquals(instance.arg3, 34)
|
||||
assertEquals(instance.arg11, 11)
|
||||
assertEquals(instance.arg49, 49)
|
||||
assertEquals(instance.arg50, 500)
|
||||
assertEquals(instance.arg65, 67)
|
||||
assertEquals(instance.arg64, 64)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleMasks(
|
||||
val arg0: Long = 0,
|
||||
val arg1: Long = 1,
|
||||
val arg2: Long = 2,
|
||||
val arg3: Long = 3,
|
||||
val arg4: Long = 4,
|
||||
val arg5: Long = 5,
|
||||
val arg6: Long = 6,
|
||||
val arg7: Long = 7,
|
||||
val arg8: Long = 8,
|
||||
val arg9: Long = 9,
|
||||
val arg10: Long = 10,
|
||||
val arg11: Long,
|
||||
val arg12: Long = 12,
|
||||
val arg13: Long = 13,
|
||||
val arg14: Long = 14,
|
||||
val arg15: Long = 15,
|
||||
val arg16: Long = 16,
|
||||
val arg17: Long = 17,
|
||||
val arg18: Long = 18,
|
||||
val arg19: Long = 19,
|
||||
@Suppress("UNUSED_PARAMETER") arg20: Long = 20,
|
||||
val arg21: Long = 21,
|
||||
val arg22: Long = 22,
|
||||
val arg23: Long = 23,
|
||||
val arg24: Long = 24,
|
||||
val arg25: Long = 25,
|
||||
val arg26: Long = 26,
|
||||
val arg27: Long = 27,
|
||||
val arg28: Long = 28,
|
||||
val arg29: Long = 29,
|
||||
val arg30: Long = 30,
|
||||
val arg31: Long = 31,
|
||||
val arg32: Long = 32,
|
||||
val arg33: Long = 33,
|
||||
val arg34: Long = 34,
|
||||
val arg35: Long = 35,
|
||||
val arg36: Long = 36,
|
||||
val arg37: Long = 37,
|
||||
val arg38: Long = 38,
|
||||
@Transient val arg39: Long = 39,
|
||||
val arg40: Long = 40,
|
||||
val arg41: Long = 41,
|
||||
val arg42: Long = 42,
|
||||
val arg43: Long = 43,
|
||||
val arg44: Long = 44,
|
||||
val arg45: Long = 45,
|
||||
val arg46: Long = 46,
|
||||
val arg47: Long = 47,
|
||||
val arg48: Long = 48,
|
||||
val arg49: Long = 49,
|
||||
val arg50: Long = 50,
|
||||
val arg51: Long = 51,
|
||||
val arg52: Long = 52,
|
||||
@Transient val arg53: Long = 53,
|
||||
val arg54: Long = 54,
|
||||
val arg55: Long = 55,
|
||||
val arg56: Long = 56,
|
||||
val arg57: Long = 57,
|
||||
val arg58: Long = 58,
|
||||
val arg59: Long = 59,
|
||||
val arg60: Long = 60,
|
||||
val arg61: Long = 61,
|
||||
val arg62: Long = 62,
|
||||
val arg63: Long = 63,
|
||||
val arg64: Long = 64,
|
||||
val arg65: Long = 65
|
||||
)
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen.annotation
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
import com.squareup.moshi.ToJson
|
||||
import java.util.Locale
|
||||
|
||||
@JsonQualifier
|
||||
annotation class UppercaseInAnnotationPackage
|
||||
|
||||
class UppercaseInAnnotationPackageJsonAdapter {
|
||||
@ToJson
|
||||
fun toJson(@UppercaseInAnnotationPackage s: String): String {
|
||||
return s.uppercase(Locale.US)
|
||||
}
|
||||
@FromJson
|
||||
@UppercaseInAnnotationPackage
|
||||
fun fromJson(s: String): String {
|
||||
return s.lowercase(Locale.US)
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":moshi"))
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin.codegen.test.extra
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
public abstract class AbstractClassInModuleA {
|
||||
// Ignored/transient to ensure processor sees them across module boundaries.
|
||||
@Transient private lateinit var lateinitTransient: String
|
||||
@Transient private var regularTransient: String = "regularTransient"
|
||||
// Note that we target the field because otherwise it is stored on the synthetic holder method for
|
||||
// annotations, which isn't visible from kapt
|
||||
@field:Json(ignore = true) private lateinit var lateinitIgnored: String
|
||||
@field:Json(ignore = true) private var regularIgnored: String = "regularIgnored"
|
||||
}
|
@@ -0,0 +1,782 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Square, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.squareup.moshi.kotlin
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.ToJson
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.adapter
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Test
|
||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
|
||||
class DualKotlinTest {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val moshi = Moshi.Builder()
|
||||
// If code gen ran, the generated adapter will be tried first. If it can't find it, it will
|
||||
// gracefully fall back to the KotlinJsonAdapter. This allows us to easily test both.
|
||||
.addLast(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
@Test fun requiredValueAbsent() {
|
||||
val jsonAdapter = moshi.adapter<RequiredValueAbsent>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Required value 'b' missing at $")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredValueAbsent(var a: Int = 3, var b: Int)
|
||||
|
||||
@Test fun requiredValueWithDifferentJsonNameAbsent() {
|
||||
val jsonAdapter = moshi.adapter<RequiredValueWithDifferentJsonNameAbsent>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("""{"a":4}""")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Required value 'b' (JSON name 'bPrime') missing at \$")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredValueWithDifferentJsonNameAbsent(var a: Int = 3, @Json(name = "bPrime") var b: Int)
|
||||
|
||||
@Test fun nonNullPropertySetToNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertySetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class HasNonNullProperty {
|
||||
var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertyWithJsonNameSetToNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"aPrime\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' (JSON name 'aPrime') was null at \$.aPrime")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullPropertyWithJsonNameSetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"aPrime\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' (JSON name 'aPrime') was null at \$.aPrime")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class HasNonNullPropertyDifferentJsonName {
|
||||
@Json(name = "aPrime") var a: String = ""
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":null}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
jsonAdapter.fromJson("{\"a\":\"hello\"}")
|
||||
fail()
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Non-null value 'a' was null at \$.a")
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RUNTIME)
|
||||
annotation class Nullable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNonNullConstructorParameter(val a: String)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableConstructorParameter(val a: String?)
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullChecking() {
|
||||
val localMoshi = moshi.newBuilder()
|
||||
.add(
|
||||
object {
|
||||
@FromJson
|
||||
fun fromJson(@Nullable string: String?): String {
|
||||
return string ?: "fallback"
|
||||
}
|
||||
|
||||
@ToJson
|
||||
fun toJson(@Nullable value: String?): String {
|
||||
return value ?: "fallback"
|
||||
}
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
val hasNonNullConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNonNullConstructorParameter>()
|
||||
assertThat(
|
||||
//language=JSON
|
||||
hasNonNullConstructorParameterAdapter
|
||||
.fromJson("{\"a\":null}")
|
||||
).isEqualTo(HasNonNullConstructorParameter("fallback"))
|
||||
|
||||
val hasNullableConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNullableConstructorParameter>()
|
||||
assertThat(
|
||||
//language=JSON
|
||||
hasNullableConstructorParameterAdapter
|
||||
.fromJson("{\"a\":null}")
|
||||
).isEqualTo(HasNullableConstructorParameter("fallback"))
|
||||
//language=JSON
|
||||
assertThat(
|
||||
hasNullableConstructorParameterAdapter
|
||||
.toJson(HasNullableConstructorParameter(null))
|
||||
).isEqualTo("{\"a\":\"fallback\"}")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class HasNullableTypeWithGeneratedAdapter(val a: HasNonNullConstructorParameter?)
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullCheckingWithGeneratedAdapter() {
|
||||
val adapter = moshi.adapter<HasNullableTypeWithGeneratedAdapter>()
|
||||
|
||||
val encoded = HasNullableTypeWithGeneratedAdapter(null)
|
||||
//language=JSON
|
||||
assertThat(adapter.toJson(encoded)).isEqualTo("""{}""")
|
||||
//language=JSON
|
||||
assertThat(adapter.serializeNulls().toJson(encoded)).isEqualTo("""{"a":null}""")
|
||||
|
||||
//language=JSON
|
||||
val decoded = adapter.fromJson("""{"a":null}""")!!
|
||||
assertThat(decoded.a).isEqualTo(null)
|
||||
}
|
||||
|
||||
@Test fun valueClass() {
|
||||
val adapter = moshi.adapter<ValueClass>()
|
||||
|
||||
val inline = ValueClass(5)
|
||||
|
||||
val expectedJson =
|
||||
"""{"i":5}"""
|
||||
assertThat(adapter.toJson(inline)).isEqualTo(expectedJson)
|
||||
|
||||
val testJson =
|
||||
"""{"i":6}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.i).isEqualTo(6)
|
||||
|
||||
// TODO doesn't work yet. https://github.com/square/moshi/issues/1170
|
||||
// need to invoke the constructor_impl$default static method, invoke constructor with result
|
||||
// val testEmptyJson =
|
||||
// """{}"""
|
||||
// val result2 = adapter.fromJson(testEmptyJson)!!
|
||||
// assertThat(result2.i).isEqualTo(0)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class InlineConsumer(val inline: ValueClass)
|
||||
|
||||
@Test fun inlineClassConsumer() {
|
||||
val adapter = moshi.adapter<InlineConsumer>()
|
||||
|
||||
val consumer = InlineConsumer(ValueClass(23))
|
||||
|
||||
@Language("JSON")
|
||||
val expectedJson =
|
||||
"""{"inline":{"i":23}}"""
|
||||
assertThat(adapter.toJson(consumer)).isEqualTo(expectedJson)
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"inline":{"i":42}}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.inline.i).isEqualTo(42)
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/955
|
||||
@Test fun backwardReferencingTypeVars() {
|
||||
val adapter = moshi.adapter<TextAssetMetaData>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"text":"text"}"""
|
||||
|
||||
assertThat(adapter.toJson(TextAssetMetaData("text"))).isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.text).isEqualTo("text")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class TextAssetMetaData(val text: String) : AssetMetaData<TextAsset>()
|
||||
class TextAsset : Asset<TextAsset>()
|
||||
abstract class Asset<A : Asset<A>>
|
||||
abstract class AssetMetaData<A : Asset<A>>
|
||||
|
||||
// Regression test for https://github.com/ZacSweers/MoshiX/issues/125
|
||||
@Test fun selfReferencingTypeVars() {
|
||||
val adapter = moshi.adapter<StringNodeNumberNode>()
|
||||
|
||||
val data = StringNodeNumberNode().also {
|
||||
it.t = StringNodeNumberNode().also {
|
||||
it.text = "child 1"
|
||||
}
|
||||
it.text = "root"
|
||||
it.r = NumberStringNode().also {
|
||||
it.number = 0
|
||||
it.t = NumberStringNode().also {
|
||||
it.number = 1
|
||||
}
|
||||
it.r = StringNodeNumberNode().also {
|
||||
it.text = "grand child 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(adapter.toJson(data))
|
||||
//language=JSON
|
||||
.isEqualTo(
|
||||
"""
|
||||
{"text":"root","r":{"number":0,"r":{"text":"grand child 1"},"t":{"number":1}},"t":{"text":"child 1"}}
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
open class Node<T : Node<T, R>, R : Node<R, T>> {
|
||||
// kotlin-reflect doesn't preserve ordering, so put these in alphabetical order so that
|
||||
// both reflective and code gen tests work the same
|
||||
var r: R? = null
|
||||
var t: T? = null
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class StringNodeNumberNode : Node<StringNodeNumberNode, NumberStringNode>() {
|
||||
var text: String = ""
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class NumberStringNode : Node<NumberStringNode, StringNodeNumberNode>() {
|
||||
var number: Int = 0
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/968
|
||||
@Test fun abstractSuperProperties() {
|
||||
val adapter = moshi.adapter<InternalAbstractProperty>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"test":"text"}"""
|
||||
|
||||
assertThat(adapter.toJson(InternalAbstractProperty("text"))).isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.test).isEqualTo("text")
|
||||
}
|
||||
|
||||
abstract class InternalAbstractPropertyBase {
|
||||
internal abstract val test: String
|
||||
|
||||
// Regression for https://github.com/square/moshi/issues/974
|
||||
abstract fun abstractFun(): String
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class InternalAbstractProperty(override val test: String) : InternalAbstractPropertyBase() {
|
||||
override fun abstractFun(): String {
|
||||
return test
|
||||
}
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/975
|
||||
@Test fun multipleConstructors() {
|
||||
val adapter = moshi.adapter<MultipleConstructorsB>()
|
||||
|
||||
//language=JSON
|
||||
assertThat(adapter.toJson(MultipleConstructorsB(6))).isEqualTo("""{"f":{"f":6},"b":6}""")
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"b":6}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.b).isEqualTo(6)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleConstructorsA(val f: Int)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleConstructorsB(val f: MultipleConstructorsA = MultipleConstructorsA(5), val b: Int) {
|
||||
constructor(f: Int, b: Int = 6) : this(MultipleConstructorsA(f), b)
|
||||
}
|
||||
|
||||
@Test fun `multiple non-property parameters`() {
|
||||
val adapter = moshi.adapter<MultipleNonPropertyParameters>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"prop":7}"""
|
||||
|
||||
assertThat(adapter.toJson(MultipleNonPropertyParameters(7))).isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.prop).isEqualTo(7)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleNonPropertyParameters(
|
||||
val prop: Int,
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
) {
|
||||
init {
|
||||
// Ensure the params always uses their default value
|
||||
require(param1 == 1)
|
||||
require(param2 == 2)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the case of multiple parameters with no parameter properties.
|
||||
@Test fun `only multiple non-property parameters`() {
|
||||
val adapter = moshi.adapter<OnlyMultipleNonPropertyParameters>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"prop":7}"""
|
||||
|
||||
assertThat(adapter.toJson(OnlyMultipleNonPropertyParameters().apply { prop = 7 }))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.prop).isEqualTo(7)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class OnlyMultipleNonPropertyParameters(
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
) {
|
||||
init {
|
||||
// Ensure the params always uses their default value
|
||||
require(param1 == 1)
|
||||
require(param2 == 2)
|
||||
}
|
||||
|
||||
var prop: Int = 0
|
||||
}
|
||||
|
||||
@Test fun typeAliasUnwrapping() {
|
||||
val adapter = moshi
|
||||
.newBuilder()
|
||||
.add(Types.supertypeOf(Int::class.javaObjectType), moshi.adapter<Int>())
|
||||
.build()
|
||||
.adapter<TypeAliasUnwrapping>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"simpleClass":6,"parameterized":{"value":6},"wildcardIn":{"value":6},"wildcardOut":{"value":6},"complex":{"value":[{"value":6}]}}"""
|
||||
|
||||
val testValue = TypeAliasUnwrapping(
|
||||
simpleClass = 6,
|
||||
parameterized = GenericClass(6),
|
||||
wildcardIn = GenericClass(6),
|
||||
wildcardOut = GenericClass(6),
|
||||
complex = GenericClass(listOf(GenericClass(6)))
|
||||
)
|
||||
assertThat(adapter.toJson(testValue)).isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(testValue)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAliasUnwrapping(
|
||||
val simpleClass: TypeAlias,
|
||||
val parameterized: GenericClass<TypeAlias>,
|
||||
val wildcardIn: GenericClass<in TypeAlias>,
|
||||
val wildcardOut: GenericClass<out TypeAlias>,
|
||||
val complex: GenericClass<GenericTypeAlias>?
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/991
|
||||
@Test fun nullablePrimitiveProperties() {
|
||||
val adapter = moshi.adapter<NullablePrimitives>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"objectType":"value","boolean":true,"byte":3,"char":"a","short":3,"int":3,"long":3,"float":3.2,"double":3.2}"""
|
||||
|
||||
val instance = NullablePrimitives(
|
||||
objectType = "value",
|
||||
boolean = true,
|
||||
byte = 3,
|
||||
char = 'a',
|
||||
short = 3,
|
||||
int = 3,
|
||||
long = 3,
|
||||
float = 3.2f,
|
||||
double = 3.2
|
||||
)
|
||||
assertThat(adapter.toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NullablePrimitives(
|
||||
val objectType: String = "",
|
||||
val boolean: Boolean,
|
||||
val nullableBoolean: Boolean? = null,
|
||||
val byte: Byte,
|
||||
val nullableByte: Byte? = null,
|
||||
val char: Char,
|
||||
val nullableChar: Char? = null,
|
||||
val short: Short,
|
||||
val nullableShort: Short? = null,
|
||||
val int: Int,
|
||||
val nullableInt: Int? = null,
|
||||
val long: Long,
|
||||
val nullableLong: Long? = null,
|
||||
val float: Float,
|
||||
val nullableFloat: Float? = null,
|
||||
val double: Double,
|
||||
val nullableDouble: Double? = null
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/990
|
||||
@Test fun nullableProperties() {
|
||||
val adapter = moshi.adapter<NullableList>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"nullableList":null}"""
|
||||
|
||||
assertThat(adapter.serializeNulls().toJson(NullableList(null)))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.nullableList).isNull()
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NullableList(val nullableList: List<Any>?)
|
||||
|
||||
@Test fun typeAliasNullability() {
|
||||
val adapter = moshi.adapter<TypeAliasNullability>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"aShouldBeNonNull":3,"nullableAShouldBeNullable":null,"redundantNullableAShouldBeNullable":null,"manuallyNullableAShouldBeNullable":null,"convolutedMultiNullableShouldBeNullable":null,"deepNestedNullableShouldBeNullable":null}"""
|
||||
|
||||
val instance = TypeAliasNullability(3, null, null, null, null, null)
|
||||
assertThat(adapter.serializeNulls().toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
}
|
||||
|
||||
@Suppress("REDUNDANT_NULLABLE")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAliasNullability(
|
||||
val aShouldBeNonNull: A,
|
||||
val nullableAShouldBeNullable: NullableA,
|
||||
val redundantNullableAShouldBeNullable: NullableA?,
|
||||
val manuallyNullableAShouldBeNullable: A?,
|
||||
val convolutedMultiNullableShouldBeNullable: NullableB?,
|
||||
val deepNestedNullableShouldBeNullable: E
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/1009
|
||||
@Test fun outDeclaration() {
|
||||
val adapter = moshi.adapter<OutDeclaration<Int>>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"input":3}"""
|
||||
|
||||
val instance = OutDeclaration(3)
|
||||
assertThat(adapter.serializeNulls().toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OutDeclaration<out T>(val input: T)
|
||||
|
||||
@Test fun intersectionTypes() {
|
||||
val adapter = moshi.adapter<IntersectionTypes<IntersectionTypesEnum>>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson =
|
||||
"""{"value":"VALUE"}"""
|
||||
|
||||
val instance = IntersectionTypes(IntersectionTypesEnum.VALUE)
|
||||
assertThat(adapter.serializeNulls().toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
}
|
||||
|
||||
interface IntersectionTypeInterface<E : Enum<E>>
|
||||
|
||||
enum class IntersectionTypesEnum : IntersectionTypeInterface<IntersectionTypesEnum> {
|
||||
VALUE
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class IntersectionTypes<E>(
|
||||
val value: E
|
||||
) where E : Enum<E>, E : IntersectionTypeInterface<E>
|
||||
|
||||
@Test fun transientConstructorParameter() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<TransientConstructorParameter>()
|
||||
|
||||
val encoded = TransientConstructorParameter(3, 5)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.b).isEqualTo(6)
|
||||
}
|
||||
|
||||
class TransientConstructorParameter(@Transient var a: Int = -1, var b: Int = -1)
|
||||
|
||||
@Test fun multipleTransientConstructorParameters() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter(MultipleTransientConstructorParameters::class.java)
|
||||
|
||||
val encoded = MultipleTransientConstructorParameters(3, 5, 7)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.b).isEqualTo(6)
|
||||
assertThat(decoded.c).isEqualTo(-1)
|
||||
}
|
||||
|
||||
class MultipleTransientConstructorParameters(@Transient var a: Int = -1, var b: Int = -1, @Transient var c: Int = -1)
|
||||
|
||||
@Test fun transientProperty() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<TransientProperty>()
|
||||
|
||||
val encoded = TransientProperty()
|
||||
encoded.a = 3
|
||||
encoded.setB(4)
|
||||
encoded.c = 5
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"c":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":5,"c":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.getB()).isEqualTo(-1)
|
||||
assertThat(decoded.c).isEqualTo(6)
|
||||
}
|
||||
|
||||
class TransientProperty {
|
||||
@Transient var a: Int = -1
|
||||
@Transient private var b: Int = -1
|
||||
var c: Int = -1
|
||||
|
||||
fun getB() = b
|
||||
|
||||
fun setB(b: Int) {
|
||||
this.b = b
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun ignoredConstructorParameter() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<IgnoredConstructorParameter>()
|
||||
|
||||
val encoded = IgnoredConstructorParameter(3, 5)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.b).isEqualTo(6)
|
||||
}
|
||||
|
||||
class IgnoredConstructorParameter(@Json(ignore = true) var a: Int = -1, var b: Int = -1)
|
||||
|
||||
@Test fun multipleIgnoredConstructorParameters() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter(MultipleIgnoredConstructorParameters::class.java)
|
||||
|
||||
val encoded = MultipleIgnoredConstructorParameters(3, 5, 7)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.b).isEqualTo(6)
|
||||
assertThat(decoded.c).isEqualTo(-1)
|
||||
}
|
||||
|
||||
class MultipleIgnoredConstructorParameters(
|
||||
@Json(ignore = true) var a: Int = -1,
|
||||
var b: Int = -1,
|
||||
@Json(ignore = true) var c: Int = -1
|
||||
)
|
||||
|
||||
@Test fun ignoredProperty() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<IgnoredProperty>()
|
||||
|
||||
val encoded = IgnoredProperty()
|
||||
encoded.a = 3
|
||||
encoded.setB(4)
|
||||
encoded.c = 5
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"c":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":5,"c":6}""")!!
|
||||
assertThat(decoded.a).isEqualTo(-1)
|
||||
assertThat(decoded.getB()).isEqualTo(-1)
|
||||
assertThat(decoded.c).isEqualTo(6)
|
||||
}
|
||||
|
||||
class IgnoredProperty {
|
||||
@Json(ignore = true) var a: Int = -1
|
||||
@Json(ignore = true) private var b: Int = -1
|
||||
var c: Int = -1
|
||||
|
||||
fun getB() = b
|
||||
|
||||
fun setB(b: Int) {
|
||||
this.b = b
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun propertyNameHasDollarSign() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<PropertyWithDollarSign>()
|
||||
|
||||
val value = PropertyWithDollarSign("apple", "banana")
|
||||
val json = """{"${'$'}a":"apple","${'$'}b":"banana"}"""
|
||||
assertThat(jsonAdapter.toJson(value)).isEqualTo(json)
|
||||
assertThat(jsonAdapter.fromJson(json)).isEqualTo(value)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PropertyWithDollarSign(
|
||||
val `$a`: String,
|
||||
@Json(name = "\$b") val b: String
|
||||
)
|
||||
}
|
||||
|
||||
typealias TypeAlias = Int
|
||||
@Suppress("REDUNDANT_PROJECTION")
|
||||
typealias GenericTypeAlias = List<out GenericClass<in TypeAlias>?>?
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GenericClass<T>(val value: T)
|
||||
|
||||
// Has to be outside since value classes are only allowed on top level
|
||||
@JvmInline
|
||||
@JsonClass(generateAdapter = true)
|
||||
value class ValueClass(val i: Int = 0)
|
||||
|
||||
typealias A = Int
|
||||
typealias NullableA = A?
|
||||
typealias B = NullableA
|
||||
typealias NullableB = B?
|
||||
typealias C = NullableA
|
||||
typealias D = C
|
||||
typealias E = D
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user