Skip to content

Commit 48c57b8

Browse files
committed
Supported assemble models for streams in concrete execution
1 parent 6ce8761 commit 48c57b8

File tree

28 files changed

+567
-144
lines changed

28 files changed

+567
-144
lines changed
Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,55 @@
11
package org.utbot.common
22

3+
import java.lang.reflect.Array
34
import java.lang.reflect.InvocationTargetException
45
import java.lang.reflect.Method
56
import kotlin.reflect.KClass
67

78
val Class<*>.nameOfPackage: String get() = `package`?.name?:""
89

910
fun Method.invokeCatching(obj: Any?, args: List<Any?>) = try {
10-
Result.success(invoke(obj, *args.toTypedArray()))
11+
val invocation = if (isVarArgs) {
12+
// In java only last parameter could be vararg
13+
val firstNonVarargParametersCount = parameterCount - 1
14+
15+
val varargArray = constructVarargParameterArray(firstNonVarargParametersCount, args)
16+
17+
if (firstNonVarargParametersCount == 0) {
18+
// Only vararg parameter, just pass only it as an array
19+
invoke(obj, varargArray)
20+
} else {
21+
// Pass first non vararg parameters as vararg, and the vararg parameter as an array
22+
val firstNonVarargParameters = args.take(firstNonVarargParametersCount)
23+
24+
invoke(obj, *firstNonVarargParameters.toTypedArray(), varargArray)
25+
}
26+
} else {
27+
invoke(obj, *args.toTypedArray())
28+
}
29+
30+
Result.success(invocation)
1131
} catch (e: InvocationTargetException) {
1232
Result.failure<Nothing>(e.targetException)
1333
}
1434

35+
// https://stackoverflow.com/a/59857242
36+
private fun Method.constructVarargParameterArray(firstNonVarargParametersCount: Int, args: List<Any?>): Any {
37+
val varargCount = args.size - firstNonVarargParametersCount
38+
val varargElements = args.drop(firstNonVarargParametersCount)
39+
40+
val varargElementType = parameterTypes.last().componentType
41+
requireNotNull(varargElementType) {
42+
"Vararg parameter of method $this was expected to be array but ${parameterTypes.last()} found"
43+
}
44+
45+
val varargArray = Array.newInstance(parameterTypes.last().componentType, varargCount)
46+
47+
varargElements.forEachIndexed { index, value ->
48+
Array.set(varargArray, index, value)
49+
}
50+
51+
return varargArray
52+
}
53+
1554
val KClass<*>.allNestedClasses: List<KClass<*>>
1655
get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses }

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,9 @@ open class ClassId @JvmOverloads constructor(
785785
open val isAbstract: Boolean
786786
get() = Modifier.isAbstract(jClass.modifiers)
787787

788+
open val isInterface: Boolean
789+
get() = Modifier.isInterface(jClass.modifiers)
790+
788791
open val isAnonymous: Boolean
789792
get() = jClass.isAnonymousClass
790793

@@ -889,6 +892,7 @@ class BuiltinClassId(
889892
override val isFinal: Boolean = false,
890893
override val isStatic: Boolean = false,
891894
override val isAbstract: Boolean = false,
895+
override val isInterface: Boolean = false,
892896
override val isAnonymous: Boolean = false,
893897
override val isLocal: Boolean = false,
894898
override val isInner: Boolean = false,

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ data class UtSandboxFailure(
2121
override val exception: Throwable
2222
) : UtExecutionFailure()
2323

24+
data class UtStreamConsumingFailure(
25+
override val exception: Throwable
26+
) : UtExecutionFailure()
27+
2428
/**
2529
* unexpectedFail (when exceptions such as NPE, IOBE, etc. appear, but not thrown by a user, applies both for function under test and nested calls )
2630
* expectedCheckedThrow (when function under test or nested call explicitly says that checked exception could be thrown and throws it)
@@ -41,6 +45,13 @@ class TimeoutException(s: String) : Exception(s)
4145

4246
data class UtTimeoutException(override val exception: TimeoutException) : UtExecutionFailure()
4347

48+
/**
49+
* Represents an exception that occurs during consuming a stream. Stores it in [innerException].
50+
*/
51+
data class UtStreamConsumingException(val innerException: Exception) : RuntimeException() {
52+
override fun toString(): String = innerException.toString()
53+
}
54+
4455
/**
4556
* Indicates failure in concrete execution.
4657
* For now it is explicitly throwing by ConcreteExecutor in case child process death.

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.plugin.api.util
22

3+
import org.utbot.common.withAccessibility
34
import org.utbot.framework.plugin.api.BuiltinClassId
45
import org.utbot.framework.plugin.api.BuiltinConstructorId
56
import org.utbot.framework.plugin.api.BuiltinMethodId
@@ -115,22 +116,31 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean {
115116
// unwrap primitive wrappers
116117
val left = primitiveByWrapper[this] ?: this
117118
val right = primitiveByWrapper[type] ?: type
119+
118120
if (left == right) {
119121
return true
120122
}
123+
121124
val leftClass = this
125+
val superTypes = leftClass.allSuperTypes()
126+
127+
return right in superTypes
128+
}
129+
130+
fun ClassId.allSuperTypes(): Sequence<ClassId> {
122131
val interfaces = sequence {
123-
var types = listOf(leftClass)
132+
var types = listOf(this@allSuperTypes)
124133
while (types.isNotEmpty()) {
125134
yieldAll(types)
126135
types = types
127136
.flatMap { it.interfaces.toList() }
128137
.map { it.id }
129138
}
130139
}
131-
val superclasses = generateSequence(leftClass) { it.superclass?.id }
132-
val superTypes = interfaces + superclasses
133-
return right in superTypes
140+
141+
val superclasses = generateSequence(this) { it.superclass?.id }
142+
143+
return interfaces + superclasses
134144
}
135145

136146
infix fun ClassId.isNotSubtypeOf(type: ClassId): Boolean = !(this isSubtypeOf type)
@@ -268,6 +278,17 @@ val atomicIntegerGetAndIncrement = MethodId(atomicIntegerClassId, "getAndIncreme
268278
val iterableClassId = java.lang.Iterable::class.id
269279
val mapClassId = java.util.Map::class.id
270280

281+
val baseStreamClassId = java.util.stream.BaseStream::class.id
282+
val streamClassId = java.util.stream.Stream::class.id
283+
val intStreamClassId = java.util.stream.IntStream::class.id
284+
val longStreamClassId = java.util.stream.LongStream::class.id
285+
val doubleStreamClassId = java.util.stream.DoubleStream::class.id
286+
287+
val intStreamToArrayMethodId = methodId(intStreamClassId, "toArray", intArrayClassId)
288+
val longStreamToArrayMethodId = methodId(longStreamClassId, "toArray", longArrayClassId)
289+
val doubleStreamToArrayMethodId = methodId(doubleStreamClassId, "toArray", doubleArrayClassId)
290+
val streamToArrayMethodId = methodId(streamClassId, "toArray", objectArrayClassId)
291+
271292
val dateClassId = java.util.Date::class.id
272293

273294
@Suppress("RemoveRedundantQualifierName")
@@ -515,6 +536,17 @@ val ExecutableId.isPackagePrivate: Boolean
515536
val ExecutableId.isAbstract: Boolean
516537
get() = Modifier.isAbstract(modifiers)
517538

539+
val ExecutableId.isVarArgs: Boolean
540+
get() {
541+
// In case this field value would be changed in the future, use reflection to extract it
542+
val varargModifierField = Class.forName("java.lang.reflect.Modifier").getDeclaredField("VARARGS")
543+
val varargModifierValue = varargModifierField.withAccessibility {
544+
varargModifierField.get(null) as Int
545+
}
546+
547+
return modifiers and varargModifierValue != 0
548+
}
549+
518550

519551
/**
520552
* Construct MethodId

utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,6 @@ class BaseStreamExampleTest : UtValueTestCaseChecker(
2929
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
3030
)
3131
) {
32-
@Test
33-
fun testReturningStreamExample() {
34-
withoutConcrete {
35-
check(
36-
BaseStreamExample::returningStreamExample,
37-
eq(2),
38-
// NOTE: the order of the matchers is important because Stream could be used only once
39-
{ c, r -> c.isNotEmpty() && c == r!!.toList() },
40-
{ c, r -> c.isEmpty() && c == r!!.toList() },
41-
coverage = FullWithAssumptions(assumeCallsNumber = 1)
42-
)
43-
}
44-
}
45-
4632
@Test
4733
fun testReturningStreamAsParameterExample() {
4834
withoutConcrete {

utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,6 @@ class DoubleStreamExampleTest : UtValueTestCaseChecker(
2121
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
2222
)
2323
) {
24-
@Test
25-
fun testReturningStreamExample() {
26-
check(
27-
DoubleStreamExample::returningStreamExample,
28-
ignoreExecutionsNumber,
29-
// NOTE: the order of the matchers is important because Stream could be used only once
30-
{ c, r -> c.isNotEmpty() && c.doubles().contentEquals(r!!.toArray()) },
31-
{ c, r -> c.isEmpty() && r!!.count() == 0L },
32-
coverage = FullWithAssumptions(assumeCallsNumber = 1)
33-
)
34-
}
35-
3624
@Test
3725
fun testReturningStreamAsParameterExample() {
3826
withoutConcrete {

utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,6 @@ class IntStreamExampleTest : UtValueTestCaseChecker(
2222
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
2323
)
2424
) {
25-
@Test
26-
fun testReturningStreamExample() {
27-
check(
28-
IntStreamExample::returningStreamExample,
29-
ignoreExecutionsNumber,
30-
// NOTE: the order of the matchers is important because Stream could be used only once
31-
{ c, r -> c.isNotEmpty() && c.ints().contentEquals(r!!.toArray()) },
32-
{ c, r -> c.isEmpty() && r!!.count() == 0L },
33-
coverage = FullWithAssumptions(assumeCallsNumber = 1)
34-
)
35-
}
36-
3725
@Test
3826
fun testReturningStreamAsParameterExample() {
3927
withoutConcrete {

utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,6 @@ class LongStreamExampleTest : UtValueTestCaseChecker(
2222
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
2323
)
2424
) {
25-
@Test
26-
fun testReturningStreamExample() {
27-
check(
28-
LongStreamExample::returningStreamExample,
29-
ignoreExecutionsNumber,
30-
// NOTE: the order of the matchers is important because Stream could be used only once
31-
{ c, r -> c.isNotEmpty() && c.longs().contentEquals(r!!.toArray()) },
32-
{ c, r -> c.isEmpty() && r!!.count() == 0L },
33-
coverage = FullWithAssumptions(assumeCallsNumber = 1)
34-
)
35-
}
36-
3725
@Test
3826
fun testReturningStreamAsParameterExample() {
3927
withoutConcrete {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package org.utbot.examples.stream
2+
3+
import org.junit.jupiter.api.Test
4+
import org.utbot.framework.codegen.ParametrizedTestSource
5+
import org.utbot.framework.plugin.api.CodegenLanguage
6+
import org.utbot.framework.plugin.api.UtStreamConsumingException
7+
import org.utbot.testcheckers.eq
8+
import org.utbot.tests.infrastructure.CodeGeneration
9+
import org.utbot.tests.infrastructure.FullWithAssumptions
10+
import org.utbot.tests.infrastructure.UtValueTestCaseChecker
11+
import kotlin.streams.toList
12+
13+
// TODO 1 instruction is always uncovered https://github.com/UnitTestBot/UTBotJava/issues/193
14+
// TODO failed Kotlin compilation (generics) JIRA:1332
15+
class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker(
16+
testClass = StreamsAsMethodResultExample::class,
17+
testCodeGeneration = true,
18+
pipelines = listOf(
19+
TestLastStage(CodegenLanguage.JAVA),
20+
TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration)
21+
),
22+
codeGenerationModes = listOf(ParametrizedTestSource.DO_NOT_PARAMETRIZE) // TODO exception from concrete is passed to arguments list somehow
23+
) {
24+
@Test
25+
fun testReturningStreamExample() {
26+
check(
27+
StreamsAsMethodResultExample::returningStreamExample,
28+
eq(2),
29+
{ c, r -> c.isEmpty() && c == r!!.toList() },
30+
{ c, r -> c.isNotEmpty() && c == r!!.toList() },
31+
coverage = FullWithAssumptions(assumeCallsNumber = 1)
32+
)
33+
}
34+
35+
@Test
36+
fun testReturningIntStreamExample() {
37+
checkWithException(
38+
StreamsAsMethodResultExample::returningIntStreamExample,
39+
eq(3),
40+
{ c, r -> c.isEmpty() && c == r.getOrThrow().toList() },
41+
{ c, r -> c.isNotEmpty() && c.none { it == null } && c.toIntArray().contentEquals(r.getOrThrow().toArray()) },
42+
{ c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() },
43+
coverage = FullWithAssumptions(assumeCallsNumber = 2)
44+
)
45+
}
46+
47+
@Test
48+
fun testReturningLongStreamExample() {
49+
checkWithException(
50+
StreamsAsMethodResultExample::returningLongStreamExample,
51+
eq(3),
52+
{ c, r -> c.isEmpty() && c == r.getOrThrow().toList() },
53+
{ c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toLong() }.toLongArray().contentEquals(r.getOrThrow().toArray()) },
54+
{ c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() },
55+
coverage = FullWithAssumptions(assumeCallsNumber = 2)
56+
)
57+
}
58+
59+
@Test
60+
fun testReturningDoubleStreamExample() {
61+
checkWithException(
62+
StreamsAsMethodResultExample::returningDoubleStreamExample,
63+
eq(3),
64+
{ c, r -> c.isEmpty() && c == r.getOrThrow().toList() },
65+
{ c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toDouble() }.toDoubleArray().contentEquals(r.getOrThrow().toArray()) },
66+
{ c, r -> c.isNotEmpty() && c.any { it == null } && r.isNPE() },
67+
coverage = FullWithAssumptions(assumeCallsNumber = 2)
68+
)
69+
}
70+
71+
/**
72+
* Checks the result in [NullPointerException] from the engine or
73+
* [UtStreamConsumingException] with [NullPointerException] from concrete execution.
74+
*/
75+
private fun Result<*>.isNPE(): Boolean =
76+
exceptionOrNull()?.let {
77+
if (it is UtStreamConsumingException) {
78+
return@let it.innerException is NullPointerException
79+
}
80+
81+
return@let it is NullPointerException
82+
} ?: false
83+
}

utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,20 @@ import org.utbot.framework.plugin.api.classId
1818
import org.utbot.framework.plugin.api.util.defaultValueModel
1919
import org.utbot.framework.plugin.api.util.doubleArrayClassId
2020
import org.utbot.framework.plugin.api.util.doubleClassId
21+
import org.utbot.framework.plugin.api.util.doubleStreamClassId
2122
import org.utbot.framework.plugin.api.util.id
2223
import org.utbot.framework.plugin.api.util.intArrayClassId
2324
import org.utbot.framework.plugin.api.util.intClassId
25+
import org.utbot.framework.plugin.api.util.intStreamClassId
2426
import org.utbot.framework.plugin.api.util.isArray
2527
import org.utbot.framework.plugin.api.util.isPrimitiveWrapper
2628
import org.utbot.framework.plugin.api.util.longArrayClassId
2729
import org.utbot.framework.plugin.api.util.longClassId
30+
import org.utbot.framework.plugin.api.util.longStreamClassId
2831
import org.utbot.framework.plugin.api.util.methodId
2932
import org.utbot.framework.plugin.api.util.objectArrayClassId
3033
import org.utbot.framework.plugin.api.util.objectClassId
34+
import org.utbot.framework.plugin.api.util.streamClassId
3135
import org.utbot.framework.util.nextModelName
3236

3337
/**
@@ -58,10 +62,10 @@ enum class UtStreamClass {
5862

5963
val overriddenStreamClassId: ClassId
6064
get() = when (this) {
61-
UT_STREAM -> java.util.stream.Stream::class.java.id
62-
UT_INT_STREAM -> java.util.stream.IntStream::class.java.id
63-
UT_LONG_STREAM -> java.util.stream.LongStream::class.java.id
64-
UT_DOUBLE_STREAM -> java.util.stream.DoubleStream::class.java.id
65+
UT_STREAM -> streamClassId
66+
UT_INT_STREAM -> intStreamClassId
67+
UT_LONG_STREAM -> longStreamClassId
68+
UT_DOUBLE_STREAM -> doubleStreamClassId
6569
}
6670
}
6771

0 commit comments

Comments
 (0)