Skip to content

Commit

Permalink
Add support to all types in KSP
Browse files Browse the repository at this point in the history
  • Loading branch information
xgouchet committed Jan 12, 2022
1 parent 23d862d commit 710940c
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 9 deletions.
35 changes: 26 additions & 9 deletions ksp/src/main/kotlin/fr/xgouchet/elmyr/ksp/ForgerableProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSValueParameter
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.visitor.KSDefaultVisitor
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.BYTE
import com.squareup.kotlinpoet.CHAR
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.DOUBLE
import com.squareup.kotlinpoet.FLOAT
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.INT
Expand Down Expand Up @@ -164,15 +168,29 @@ class ForgerableProcessor(
private fun generateParameterForgery(
parameter: KSValueParameter
): CodeBlock {
val typeName = parameter.type.toTypeName()
val forgeryStatement = when (typeName) {
STRING -> "forge.aString()"
BYTE -> "forge.aByte()"
SHORT -> "forge.aShort()"
INT -> "forge.anInt()"
LONG -> "forge.aLong()"
else -> "?"
logger.info("Converting type ${parameter.type}")
val resolvedType = parameter.type.resolve()
val isNullable = resolvedType.isMarkedNullable
val nonNullType = resolvedType.makeNotNullable()
val typeName = nonNullType.toTypeName()
val forgeryMethod = when {
typeName == STRING -> "aString()"
typeName == BOOLEAN -> "aBool()"
typeName == BYTE -> "aByte()"
typeName == SHORT -> "aShort()"
typeName == INT -> "anInt()"
typeName == LONG -> "aLong()"
typeName == FLOAT -> "aFloat()"
typeName == DOUBLE -> "aDouble()"
typeName == CHAR -> "aChar()"
else -> "getForgery()"
}
val forgeryStatement = if (isNullable){
"forge.aNullable { $forgeryMethod }"
} else {
"forge.$forgeryMethod"
}

return CodeBlock.builder()
.addStatement(
"val %L: %T = %L",
Expand All @@ -181,7 +199,6 @@ class ForgerableProcessor(
forgeryStatement
).build()
}

}

data class ConstructorContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,167 @@ internal class ForgerableProcessorProviderTest {
}
}


@Test
fun processDataClassWithPrimitiveArguments() {
val className = "Spam"
val sourceContent = """
package com.example
data class $className(
val s: String,
val b: Boolean,
val y: Byte,
val h: Short,
val i: Int,
val l: Long,
val f: Float,
val d: Double,
val c: Char
)
"""

testCompilation(
"$className.kt",
sourceContent
) {
assertThat(it.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
it.assertGeneratedFactoryEquals(
className,
"""
package com.example
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.ForgeryFactory
import kotlin.Boolean
import kotlin.Byte
import kotlin.Char
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.Short
import kotlin.String
public class SpamForgeryFactory : ForgeryFactory {
public override fun getForgery(forge: Forge): Spam {
val s: String = forge.aString()
val b: Boolean = forge.aBool()
val y: Byte = forge.aByte()
val h: Short = forge.aShort()
val i: Int = forge.anInt()
val l: Long = forge.aLong()
val f: Float = forge.aFloat()
val d: Double = forge.aDouble()
val c: Char = forge.aChar()
return Spam(s = s, b = b, y = y, h = h, i = i, l = l, f = f, d = d, c = c)
}
}
""".trimIndent()
)
}
}


@Test
fun processDataClassWithNullablePrimitiveArguments() {
val className = "Spam"
val sourceContent = """
package com.example
data class $className(
val s: String?,
val b: Boolean?,
val y: Byte?,
val h: Short?,
val i: Int?,
val l: Long?,
val f: Float?,
val d: Double?,
val c: Char?
)
"""

testCompilation(
"$className.kt",
sourceContent
) {
assertThat(it.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
it.assertGeneratedFactoryEquals(
className,
"""
package com.example
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.ForgeryFactory
import kotlin.Boolean
import kotlin.Byte
import kotlin.Char
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.Short
import kotlin.String
public class SpamForgeryFactory : ForgeryFactory {
public override fun getForgery(forge: Forge): Spam {
val s: String? = forge.aNullable { aString() }
val b: Boolean? = forge.aNullable { aBool() }
val y: Byte? = forge.aNullable { aByte() }
val h: Short? = forge.aNullable { aShort() }
val i: Int? = forge.aNullable { anInt() }
val l: Long? = forge.aNullable { aLong() }
val f: Float? = forge.aNullable { aFloat() }
val d: Double? = forge.aNullable { aDouble() }
val c: Char? = forge.aNullable { aChar() }
return Spam(s = s, b = b, y = y, h = h, i = i, l = l, f = f, d = d, c = c)
}
}
""".trimIndent()
)
}
}


@Test
fun processDataClassWithEnumArguments() {
val className = "Bacon"
val sourceContent = """
package com.example
enum class Day { MON, TUE, WED, THU, FRI, SAT, SUN }
data class $className(
val d: Day
)
"""

testCompilation(
"$className.kt",
sourceContent
) {
assertThat(it.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
it.assertGeneratedFactoryEquals(
className,
"""
package com.example
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.ForgeryFactory
public class BaconForgeryFactory : ForgeryFactory {
public override fun getForgery(forge: Forge): Bacon {
val d: Day = forge.getForgery()
return Bacon(d = d)
}
}
""".trimIndent()
)
}
}

@Test
fun processDataClassWithSecondaryConst() {
val className = "Foo"
Expand Down Expand Up @@ -208,4 +369,6 @@ internal class ForgerableProcessorProviderTest {
javaGeneratedDir.walk().toList()
}

// endregion

}

0 comments on commit 710940c

Please sign in to comment.