Skip to content

Commit

Permalink
Ensure @Serializable generated constructor is filtered out (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmook committed May 2, 2023
1 parent ea9a686 commit 3e579d1
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 7 deletions.
4 changes: 4 additions & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ object Versions {
const val testExtJunit = "1.1.5"
const val testRunner = "1.5.2"
}

object KotlinX {
const val serialization = "1.5.0"
}
}
3 changes: 3 additions & 0 deletions fixture/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ plugins {
id("com.android.lint")
id("com.vanniktech.maven.publish")
id("org.jetbrains.dokka")
id("org.jetbrains.kotlin.plugin.serialization") version Versions.kotlin
}

apply(from = "$rootDir/gradle/scripts/jacoco.gradle.kts")
Expand All @@ -46,6 +47,8 @@ dependencies {
testImplementation(kotlin("test-junit"))
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:${Versions.mockitoKotlin}")

testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.KotlinX.serialization}")

// Used for ComparisonTest
@Suppress("GradleDependency")
testImplementation("com.github.marcellogalhardo:kotlin-fixture:${Versions.marcellogalhardo}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Appmattus Limited
* Copyright 2020-2023 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,4 +28,15 @@ interface ConstructorStrategy {
* Returns [obj] constructors in the order to try when generating an instance.
*/
fun constructors(context: Context, obj: KClass<*>): Collection<KFunction<*>>

/**
* Constructors of the class with Serializable constructors filtered out
*/
val KClass<*>.filteredConstructors: Collection<KFunction<*>>
get() = constructors.filterNot { it.isSerializationConstructor() }

private fun KFunction<Any>.isSerializationConstructor(): Boolean {
val lastParameterType = (parameters.lastOrNull()?.type?.classifier as? KClass<*>)?.qualifiedName
return lastParameterType == "kotlinx.serialization.internal.SerializationConstructorMarker"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Appmattus Limited
* Copyright 2020-2023 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,6 @@ import kotlin.reflect.KFunction
*/
object GreedyConstructorStrategy : ConstructorStrategy {
override fun constructors(context: Context, obj: KClass<*>): Collection<KFunction<*>> {
return obj.constructors.sortedByDescending { it.parameters.size }
return obj.filteredConstructors.sortedByDescending { it.parameters.size }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Appmattus Limited
* Copyright 2020-2023 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,6 @@ import kotlin.reflect.KFunction
*/
object ModestConstructorStrategy : ConstructorStrategy {
override fun constructors(context: Context, obj: KClass<*>): Collection<KFunction<*>> {
return obj.constructors.sortedBy { it.parameters.size }
return obj.filteredConstructors.sortedBy { it.parameters.size }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 Appmattus Limited
* Copyright 2020-2023 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -25,6 +25,6 @@ import kotlin.reflect.KFunction
*/
object RandomConstructorStrategy : ConstructorStrategy {
override fun constructors(context: Context, obj: KClass<*>): Collection<KFunction<*>> {
return obj.constructors.shuffled(context.random)
return obj.filteredConstructors.shuffled(context.random)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2023 Appmattus Limited
*
* 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
*
* http://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.appmattus.kotlinfixture

import com.appmattus.kotlinfixture.config.TestGenerator.fixture
import kotlinx.serialization.Serializable
import kotlin.test.Test

class SerializableTest {

@Serializable
private data class ErrorCodeDto(
val errorCode: String,
val errorDetail: String,
val errorDescription: String
)

@Test
fun `serializing and deserializing ErrorCodeDto should result in original instance`() {
repeat(100) {
val original = fixture<ErrorCodeDto>()
// Serializable generates synthetic constructors with nullable parameters so we ensure we verify we don't use that constructor
@Suppress("SENSELESS_COMPARISON")
runCatching {
require(original.errorCode != null)
require(original.errorDetail != null)
require(original.errorDescription != null)
}.onFailure {
println(original)
throw IllegalArgumentException()
}
}
}
}

0 comments on commit 3e579d1

Please sign in to comment.