lyng/lynglib/src/commonTest/kotlin/IfNullAssignTest.kt

120 lines
3.3 KiB
Kotlin

/*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* 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.
*
*/
import kotlinx.coroutines.test.runTest
import net.sergeych.lyng.eval
import kotlin.test.Test
class IfNullAssignTest {
@Test
fun testBasicAssignment() = runTest {
eval("""
var x = null
x ?= 42
assertEquals(42, x)
x ?= 100
assertEquals(42, x)
""".trimIndent())
}
@Test
fun testPropertyAssignment() = runTest {
eval("""
class Box(var value: Object?)
val b = Box(null)
b.value ?= 10
assertEquals(10, b.value)
b.value ?= 20
assertEquals(10, b.value)
""".trimIndent())
}
@Test
fun testIndexAssignment() = runTest {
eval("""
val a = [null, 1]
a[0] ?= 10
assertEquals(10, a[0])
a[1] ?= 20
assertEquals(1, a[1])
""".trimIndent())
}
@Test
fun testOptionalChaining() = runTest {
eval("""
class Inner(var value: Object?)
class Outer(var inner: Inner?)
var o: Outer? = null
o?.inner?.value ?= 10 // should do nothing
assertEquals(null, o)
o = Outer(null)
o?.inner?.value ?= 10 // should do nothing because inner is null
val outer = o as Outer
assertEquals(null, outer.inner)
outer.inner = Inner(null)
outer.inner?.value ?= 42
assertEquals(42, (outer.inner as Inner).value)
outer.inner?.value ?= 100
assertEquals(42, (outer.inner as Inner).value)
""".trimIndent())
}
@Test
fun testDoubleEvaluation() = runTest {
eval("""
var count = 0
fun getIdx() {
count = count + 1
return 0
}
val a = [null]
a[getIdx()] ?= 10
// Current behavior: double evaluation happens in ObjRef for compound ops
// getIdx() is called once for checking if null, and once for setting if it was null.
assertEquals(10, a[0])
assertEquals(2, count)
a[getIdx()] ?= 20
// If it's NOT null, it only evaluates once to check the value.
assertEquals(10, a[0])
assertEquals(3, count)
""".trimIndent())
}
@Test
fun testReturnValue() = runTest {
eval("""
var x = null
val r1 = (x ?= 42)
assertEquals(42, r1)
assertEquals(42, x)
val r2 = (x ?= 100)
assertEquals(42, r2)
assertEquals(42, x)
""".trimIndent())
}
}