Skip to content

Commit ebb5573

Browse files
committed
Supported assemble models for streams in concrete execution
1 parent 27a1af8 commit ebb5573

File tree

27 files changed

+552
-144
lines changed

27 files changed

+552
-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/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: 25 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
@@ -114,22 +115,31 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean {
114115
// unwrap primitive wrappers
115116
val left = primitiveByWrapper[this] ?: this
116117
val right = primitiveByWrapper[type] ?: type
118+
117119
if (left == right) {
118120
return true
119121
}
122+
120123
val leftClass = this
124+
val superTypes = leftClass.allSuperTypes()
125+
126+
return right in superTypes
127+
}
128+
129+
fun ClassId.allSuperTypes(): Sequence<ClassId> {
121130
val interfaces = sequence {
122-
var types = listOf(leftClass)
131+
var types = listOf(this@allSuperTypes)
123132
while (types.isNotEmpty()) {
124133
yieldAll(types)
125134
types = types
126135
.flatMap { it.interfaces.toList() }
127136
.map { it.id }
128137
}
129138
}
130-
val superclasses = generateSequence(leftClass) { it.superclass?.id }
131-
val superTypes = interfaces + superclasses
132-
return right in superTypes
139+
140+
val superclasses = generateSequence(this) { it.superclass?.id }
141+
142+
return interfaces + superclasses
133143
}
134144

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

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

272293
@Suppress("RemoveRedundantQualifierName")

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

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.utbot.framework.plugin.api.BuiltinClassId
99
import org.utbot.framework.plugin.api.BuiltinConstructorId
1010
import org.utbot.framework.plugin.api.ClassId
1111
import org.utbot.framework.plugin.api.MethodId
12+
import org.utbot.framework.plugin.api.util.baseStreamClassId
1213
import org.utbot.framework.plugin.api.util.booleanClassId
1314
import org.utbot.framework.plugin.api.util.builtinConstructorId
1415
import org.utbot.framework.plugin.api.util.classClassId
@@ -48,6 +49,7 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) {
4849
mapsDeepEqualsMethodId,
4950
hasCustomEqualsMethodId,
5051
getArrayLengthMethodId,
52+
consumeBaseStreamMethodId,
5153
buildStaticLambdaMethodId,
5254
buildLambdaMethodId,
5355
getLookupInMethodId,
@@ -165,6 +167,13 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) {
165167
arguments = arrayOf(objectClassId)
166168
)
167169

170+
val consumeBaseStreamMethodId: MethodId
171+
get() = utilClassId.utilMethodId(
172+
name = "consumeBaseStream",
173+
returnType = voidClassId,
174+
arguments = arrayOf(baseStreamClassId)
175+
)
176+
168177
val buildStaticLambdaMethodId: MethodId
169178
get() = utilClassId.utilMethodId(
170179
name = "buildStaticLambda",

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ internal interface CgContextOwner {
396396
val getArrayLength: MethodId
397397
get() = utilMethodProvider.getArrayLengthMethodId
398398

399+
val consumeBaseStream: MethodId
400+
get() = utilMethodProvider.consumeBaseStreamMethodId
401+
399402
val buildStaticLambda: MethodId
400403
get() = utilMethodProvider.buildStaticLambdaMethodId
401404

0 commit comments

Comments
 (0)