import org.junit.Test
import kotlin.reflect.KClass
import kotlin.reflect.typeOf
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class FunctionsClassic {
fun add(num1: Int, num2: Int): Int = num1 + num2
fun printNum(num: Int) {
print(num)
}
fun triple(num: Int): Int = num * 3
fun produceName(name: String): Name = Name(name)
fun longestOf(
str1: String,
str2: String,
str3: String,
): String =
maxOf(str1, str2, str3, compareBy { it.length })
}
data class Name(val name: String)
class FunctionReference {
val add: (Int, Int) -> Int = Int::plus
val printNum: (Int) -> Unit = ::print
val triple: (Int) -> Int = 3::times
val produceName: (String) -> Name = ::Name
}
class FunctionMemberReference {
val add: (Int, Int) -> Int = this::add
val printNum: (Int) -> Unit = this::printNum
val triple: (Int) -> Int = this::triple
val produceName: (String) -> Name = this::produceName
val longestOf: (String, String, String) -> String =
this::longestOf
private fun add(num1: Int, num2: Int): Int = num1 + num2
private fun printNum(num: Int) {
print(num)
}
private fun triple(num: Int): Int = num * 3
private fun produceName(name: String): Name = Name(name)
private fun longestOf(
str1: String,
str2: String,
str3: String
): String =
maxOf(str1, str2, str3, compareBy { it.length })
}
class BoundedFunctionReference {
private val classic = FunctionsClassic()
val add: (Int, Int) -> Int = classic::add
val printNum: (Int) -> Unit = classic::printNum
val triple: (Int) -> Int = classic::triple
val produceName: (String) -> Name = classic::produceName
val longestOf: (String, String, String) -> String =
classic::longestOf
}
class ReferencesTest {
@Test
fun `FunctionReference has correct property signatures`() {
checkPropertySignatures(FunctionReference::class, expectLongestOf = false)
}
@Test
fun `FunctionReference has correct property behavior`() {
checkPropertyBehavior(FunctionReference(), expectLongestOf = false)
}
@Test
fun `FunctionMemberReference has correct property signatures`() {
checkPropertySignatures(FunctionMemberReference::class)
}
@Test
fun `FunctionMemberReference has correct property behavior`() {
checkPropertyBehavior(FunctionMemberReference())
}
@Test
fun `BoundedFunctionReference has correct property signatures`() {
checkPropertySignatures(BoundedFunctionReference::class)
}
@Test
fun `BoundedFunctionReference has correct property behavior`() {
checkPropertyBehavior(BoundedFunctionReference())
}
private fun checkPropertySignatures(
classToCheck: KClass<*>,
expectLongestOf: Boolean = true,
) {
val c = classToCheck.members
val properties = mutableMapOf(
"add" to typeOf<(Int, Int) -> Int>(),
"printNum" to typeOf<(Int) -> Unit>(),
"triple" to typeOf<(Int) -> Int>(),
"produceName" to typeOf<(String) -> Name>(),
)
if (expectLongestOf) {
properties += "longestOf" to typeOf<(String, String, String) -> String>()
}
for ((propertyName, propertyType) in properties) {
val propertyReference = c.find { it.name == propertyName }
assertNotNull(propertyReference) { "Property $propertyName is missing" }
assertEquals(propertyType, propertyReference.returnType, "Property $propertyName has wrong type")
}
}
@Suppress("UNCHECKED_CAST")
private fun <T: Any> checkPropertyBehavior(
instance: T,
expectLongestOf: Boolean = true,
) {
val members = instance::class.members
val add = members.find { it.name == "add" }!!
assertEquals(3, (add.call(instance) as (Int, Int) -> Int)(1, 2))
assertEquals(12, (add.call(instance) as (Int, Int) -> Int)(4, 8))
val printNum = members.find { it.name == "printNum" }!!
(printNum.call(instance) as (Int) -> Unit)(42)
val triple = members.find { it.name == "triple" }!!
assertEquals(9, (triple.call(instance) as (Int) -> Int)(3))
assertEquals(15, (triple.call(instance) as (Int) -> Int)(5))
val produceName = members.find { it.name == "produceName" }!!
assertEquals(Name("John"), (produceName.call(instance) as (String) -> Name)("John"))
assertEquals(Name("Jane"), (produceName.call(instance) as (String) -> Name)("Jane"))
if (expectLongestOf) {
val longestOf = members.find { it.name == "longestOf" }!!
assertEquals("abc", (longestOf.call(instance) as (String, String, String) -> String)("a", "ab", "abc"))
assertEquals("xyz", (longestOf.call(instance) as (String, String, String) -> String)("x", "xy", "xyz"))
}
}
}