Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instantiate testClass taking into account its enclosing class #466

Merged
merged 2 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/jvm/main/org/jetbrains/kotlinx/lincheck/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -329,3 +329,20 @@ internal inline fun <R> runOutsideIgnoredSection(descriptor: ThreadDescriptor?,

internal const val LINCHECK_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck."
internal const val LINCHECK_RUNNER_PACKAGE_NAME = "org.jetbrains.kotlinx.lincheck.runner."

internal fun <T> Class<T>.newDefaultInstance(): T {
@Suppress("UNCHECKED_CAST")
val constructor = this.declaredConstructors.singleOrNull { it.parameterCount == 0 } as? Constructor<T>
if (constructor != null) {
return constructor.newInstance()
}

if (this.enclosingClass != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we check if enclosingClass != null as a first step, and only then look for default constructor with zero arguments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. Class can have a default constructor even if enclosingClass != null. For example

package org.example

class A {
    inner class B
}

class C {
    class D
}

fun main() {
    println(A.B::class.java.enclosingClass) // class org.example.A
    println(C.D::class.java.enclosingClass) // class org.example.C
    // But
    A().B() // requires A object
    C.D()   // doesn't require C object
}

I can probably add such a test.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see, thanks.

Yes, it would be nice to add another test case for nested non-inner class (static nested in Java terminology).

val enclosingObject = this.enclosingClass.newDefaultInstance()
return this.getDeclaredConstructor(this.enclosingClass)
.also { it.isAccessible = true }
.newInstance(enclosingObject)
}

throw IllegalStateException("No suitable constructor found for ${this.canonicalName}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@ internal open class ParallelThreadsRunner(
private var ensuredTestInstanceIsTransformed = false

private fun createTestInstance() {
@Suppress("DEPRECATION")
testInstance = testClass.newInstance()
testInstance = testClass.newDefaultInstance()
if (strategy is ModelCheckingStrategy) {
// In the model checking mode, we need to ensure
// that all the necessary classes and instrumented
Expand Down
3 changes: 1 addition & 2 deletions src/jvm/main/org/jetbrains/kotlinx/lincheck/verifier/LTS.kt
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ class LTS(private val sequentialSpecification: Class<*>) {
}

private fun createInitialStateInstance(): Any {
@Suppress("DEPRECATION")
return sequentialSpecification.newInstance().also {
return sequentialSpecification.newDefaultInstance().also {
// the sequential version of the data structure used for verification
// may differ from the original parallel version,
// in this case we need to ensure that the sequential class is also instrumented
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Lincheck
*
* Copyright (C) 2019 - 2025 JetBrains s.r.o.
*
* This Source Code Form is subject to the terms of the
* Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
* with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

package org.jetbrains.kotlinx.lincheck_test.runner

import org.jetbrains.kotlinx.lincheck.annotations.Operation
import org.jetbrains.kotlinx.lincheck.check
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions
import org.junit.Test

class OuterClass {
inner class InnerClass {
@Operation
fun zero() = 0
}

class NestedClass {
@Operation
fun zero() = 0
}
}

class InnerClassTest {
@Test
fun testInnerClass() {
StressOptions()
.invocationsPerIteration(1)
.iterations(1)
.check(OuterClass.InnerClass::class)
}

@Test
fun testNestedClass() {
StressOptions()
.invocationsPerIteration(1)
.iterations(1)
.check(OuterClass.NestedClass::class)
}
}