diff --git a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt index 881b708f55..e375e2ee0b 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/HackUtil.kt @@ -67,4 +67,13 @@ enum class WorkaroundReason { * requires thorough [investigation](https://github.com/UnitTestBot/UTBotJava/issues/716). */ IGNORE_STATICS_FROM_TRUSTED_LIBRARIES, + /** + * Methods that return [java.util.stream.BaseStream] as a result, can return them ”dirty” - consuming of them lead to the exception. + * The symbolic engine and concrete execution create UtStreamConsumingFailure executions in such cases. To warn a + * user about unsafety of using such “dirty” streams, code generation consumes them (mostly with `toArray` methods) + * and asserts exception. Unfortunately, it doesn't work well for parametrized tests - they create assertions relying on + * such-called “generic execution”, so resulted tests always contain `deepEquals` for streams, and we cannot easily + * construct `toArray` invocation (because streams cannot be consumed twice). + */ + CONSUME_DIRTY_STREAMS, } \ No newline at end of file diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 1c0a88d02a..b1371c8015 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -6,8 +6,14 @@ import kotlin.reflect.KClass val Class<*>.nameOfPackage: String get() = `package`?.name?:"" +/** + * Invokes [this] method of passed [obj] instance (null for static methods) with the passed [args] arguments. + * NOTE: vararg parameters must be passed as an array of the corresponding type. + */ fun Method.invokeCatching(obj: Any?, args: List) = try { - Result.success(invoke(obj, *args.toTypedArray())) + val invocation = invoke(obj, *args.toTypedArray()) + + Result.success(invocation) } catch (e: InvocationTargetException) { Result.failure(e.targetException) } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index af70771bcc..3703940eab 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -1271,7 +1271,8 @@ enum class CodegenLanguage( "-d", buildDirectory, "-cp", classPath, "-XDignore.symbol.file", // to let javac use classes from rt.jar - "--add-exports", "java.base/sun.reflect.generics.repository=ALL-UNNAMED" + "--add-exports", "java.base/sun.reflect.generics.repository=ALL-UNNAMED", + "--add-exports", "java.base/sun.text=ALL-UNNAMED", ).plus(sourcesFiles) KOTLIN -> listOf("-d", buildDirectory, "-jvm-target", jvmTarget, "-cp", classPath).plus(sourcesFiles) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt index b242ab39ec..cb6d6c9f61 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt @@ -1,5 +1,6 @@ package org.utbot.framework.plugin.api +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import java.io.File import java.util.LinkedList @@ -11,6 +12,13 @@ data class UtExecutionSuccess(val model: UtModel) : UtExecutionResult() { sealed class UtExecutionFailure : UtExecutionResult() { abstract val exception: Throwable + + /** + * Represents the most inner exception in the failure. + * Often equals to [exception], but is wrapped exception in [UtStreamConsumingException]. + */ + open val rootCauseException: Throwable + get() = exception } data class UtOverflowFailure( @@ -21,6 +29,13 @@ data class UtSandboxFailure( override val exception: Throwable ) : UtExecutionFailure() +data class UtStreamConsumingFailure( + override val exception: UtStreamConsumingException, +) : UtExecutionFailure() { + override val rootCauseException: Throwable + get() = exception.innerExceptionOrAny +} + /** * unexpectedFail (when exceptions such as NPE, IOBE, etc. appear, but not thrown by a user, applies both for function under test and nested calls ) * expectedCheckedThrow (when function under test or nested call explicitly says that checked exception could be thrown and throws it) @@ -83,7 +98,7 @@ inline fun UtExecutionResult.onSuccess(action: (model: UtModel) -> Unit): UtExec } inline fun UtExecutionResult.onFailure(action: (exception: Throwable) -> Unit): UtExecutionResult { - if (this is UtExecutionFailure) action(exception) + if (this is UtExecutionFailure) action(rootCauseException) return this } @@ -93,6 +108,6 @@ fun UtExecutionResult.getOrThrow(): UtModel = when (this) { } fun UtExecutionResult.exceptionOrNull(): Throwable? = when (this) { - is UtExecutionFailure -> exception + is UtExecutionFailure -> rootCauseException is UtExecutionSuccess -> null } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 57dbb004c0..4b782b9b10 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -1,5 +1,6 @@ package org.utbot.framework.plugin.api.util +import org.utbot.common.withAccessibility import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.BuiltinMethodId @@ -114,12 +115,20 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean { // unwrap primitive wrappers val left = primitiveByWrapper[this] ?: this val right = primitiveByWrapper[type] ?: type + if (left == right) { return true } + val leftClass = this + val superTypes = leftClass.allSuperTypes() + + return right in superTypes +} + +fun ClassId.allSuperTypes(): Sequence { val interfaces = sequence { - var types = listOf(leftClass) + var types = listOf(this@allSuperTypes) while (types.isNotEmpty()) { yieldAll(types) types = types @@ -127,9 +136,10 @@ infix fun ClassId.isSubtypeOf(type: ClassId): Boolean { .map { it.id } } } - val superclasses = generateSequence(leftClass) { it.superclass?.id } - val superTypes = interfaces + superclasses - return right in superTypes + + val superclasses = generateSequence(this) { it.superclass?.id } + + return interfaces + superclasses } infix fun ClassId.isNotSubtypeOf(type: ClassId): Boolean = !(this isSubtypeOf type) @@ -267,6 +277,17 @@ val atomicIntegerGetAndIncrement = MethodId(atomicIntegerClassId, "getAndIncreme val iterableClassId = java.lang.Iterable::class.id val mapClassId = java.util.Map::class.id +val baseStreamClassId = java.util.stream.BaseStream::class.id +val streamClassId = java.util.stream.Stream::class.id +val intStreamClassId = java.util.stream.IntStream::class.id +val longStreamClassId = java.util.stream.LongStream::class.id +val doubleStreamClassId = java.util.stream.DoubleStream::class.id + +val intStreamToArrayMethodId = methodId(intStreamClassId, "toArray", intArrayClassId) +val longStreamToArrayMethodId = methodId(longStreamClassId, "toArray", longArrayClassId) +val doubleStreamToArrayMethodId = methodId(doubleStreamClassId, "toArray", doubleArrayClassId) +val streamToArrayMethodId = methodId(streamClassId, "toArray", objectArrayClassId) + val dateClassId = java.util.Date::class.id @Suppress("RemoveRedundantQualifierName") diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt new file mode 100644 index 0000000000..fc25154daa --- /dev/null +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/visible/UtStreamConsumingException.kt @@ -0,0 +1,15 @@ +package org.utbot.framework.plugin.api.visible + +/** + * An artificial exception that stores an exception that would be thrown in case of consuming stream by invoking terminal operations. + * [innerException] stores this possible exception (null if [UtStreamConsumingException] was constructed by the engine). + */ +data class UtStreamConsumingException(private val innerException: Exception?) : RuntimeException() { + /** + * Returns the original exception [innerException] if possible, and any [RuntimeException] otherwise. + */ + val innerExceptionOrAny: Throwable + get() = innerException ?: RuntimeException("Unknown runtime exception during consuming stream") + + override fun toString(): String = innerExceptionOrAny.toString() +} \ No newline at end of file diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt index 7e48a85432..7206db0cea 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/casts/CastExampleTest.kt @@ -80,12 +80,14 @@ internal class CastExampleTest : UtValueTestCaseChecker( @Test fun testComplicatedCast() { - check( - CastExample::complicatedCast, - eq(2), - { i, a, _ -> i == 0 && a != null && a[i] != null && a[i] !is CastClassFirstSucc }, - { i, a, r -> i == 0 && a != null && a[i] != null && a[i] is CastClassFirstSucc && r is CastClassFirstSucc }, - coverage = DoNotCalculate - ) + withEnabledTestingCodeGeneration(testCodeGeneration = false) { // error: package sun.text is not visible + check( + CastExample::complicatedCast, + eq(2), + { i, a, _ -> i == 0 && a != null && a[i] != null && a[i] !is CastClassFirstSucc }, + { i, a, r -> i == 0 && a != null && a[i] != null && a[i] is CastClassFirstSucc && r is CastClassFirstSucc }, + coverage = DoNotCalculate + ) + } } } \ No newline at end of file diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt index b1dbb5a64d..ff0407947e 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/BaseStreamExampleTest.kt @@ -29,20 +29,6 @@ class BaseStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - withoutConcrete { - check( - BaseStreamExample::returningStreamExample, - eq(2), - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c == r!!.toList() }, - { c, r -> c.isEmpty() && c == r!!.toList() }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt index b480daa90c..53216b306f 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/DoubleStreamExampleTest.kt @@ -21,18 +21,6 @@ class DoubleStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - DoubleStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.doubles().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt index 62a1ecbca0..5aef3eb609 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/IntStreamExampleTest.kt @@ -22,18 +22,6 @@ class IntStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - IntStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.ints().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt index 8864db2a3a..0ac7785d6c 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/LongStreamExampleTest.kt @@ -22,18 +22,6 @@ class LongStreamExampleTest : UtValueTestCaseChecker( TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) ) ) { - @Test - fun testReturningStreamExample() { - check( - LongStreamExample::returningStreamExample, - ignoreExecutionsNumber, - // NOTE: the order of the matchers is important because Stream could be used only once - { c, r -> c.isNotEmpty() && c.longs().contentEquals(r!!.toArray()) }, - { c, r -> c.isEmpty() && r!!.count() == 0L }, - coverage = FullWithAssumptions(assumeCallsNumber = 1) - ) - } - @Test fun testReturningStreamAsParameterExample() { withoutConcrete { diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt new file mode 100644 index 0000000000..54706bb9bb --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/stream/StreamsAsMethodResultExampleTest.kt @@ -0,0 +1,69 @@ +package org.utbot.examples.stream + +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException +import org.utbot.testcheckers.eq +import org.utbot.tests.infrastructure.CodeGeneration +import org.utbot.tests.infrastructure.FullWithAssumptions +import org.utbot.tests.infrastructure.UtValueTestCaseChecker +import org.utbot.tests.infrastructure.isException +import kotlin.streams.toList + +// TODO 1 instruction is always uncovered https://github.com/UnitTestBot/UTBotJava/issues/193 +// TODO failed Kotlin compilation (generics) JIRA:1332 +class StreamsAsMethodResultExampleTest : UtValueTestCaseChecker( + testClass = StreamsAsMethodResultExample::class, + testCodeGeneration = true, + pipelines = listOf( + TestLastStage(CodegenLanguage.JAVA), + TestLastStage(CodegenLanguage.KOTLIN, CodeGeneration) + ), +) { + @Test + fun testReturningStreamExample() { + check( + StreamsAsMethodResultExample::returningStreamExample, + eq(2), + { c, r -> c.isEmpty() && c == r!!.toList() }, + { c, r -> c.isNotEmpty() && c == r!!.toList() }, + coverage = FullWithAssumptions(assumeCallsNumber = 1) + ) + } + + @Test + fun testReturningIntStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningIntStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.toIntArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } + + @Test + fun testReturningLongStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningLongStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toLong() }.toLongArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } + + @Test + fun testReturningDoubleStreamExample() { + checkWithException( + StreamsAsMethodResultExample::returningDoubleStreamExample, + eq(3), + { c, r -> c.isEmpty() && c == r.getOrThrow().toList() }, + { c, r -> c.isNotEmpty() && c.none { it == null } && c.map { it.toDouble() }.toDoubleArray().contentEquals(r.getOrThrow().toArray()) }, + { c, r -> c.isNotEmpty() && c.any { it == null } && r.isException() }, + coverage = FullWithAssumptions(assumeCallsNumber = 2) + ) + } +} diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java index d1e9768c2f..1d1ef89ecf 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtDoubleStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.DoubleSummaryStatistics; import java.util.NoSuchElementException; @@ -122,8 +123,13 @@ public DoubleStream filter(DoublePredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { double element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -137,7 +143,11 @@ public DoubleStream map(DoubleUnaryOperator mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -153,7 +163,11 @@ public Stream mapToObj(DoubleFunction mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((U[]) mapped, size); @@ -166,7 +180,11 @@ public IntStream mapToInt(DoubleToIntFunction mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -179,7 +197,11 @@ public LongStream mapToLong(DoubleToLongFunction mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -256,7 +278,11 @@ public DoubleStream peek(DoubleConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -315,13 +341,16 @@ public DoubleStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(DoubleConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(DoubleConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java index 322859d6b4..96788e9983 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtIntStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.IntSummaryStatistics; import java.util.NoSuchElementException; @@ -123,8 +124,13 @@ public IntStream filter(IntPredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { int element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -138,7 +144,11 @@ public IntStream map(IntUnaryOperator mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -154,7 +164,11 @@ public Stream mapToObj(IntFunction mapper) { int size = elementData.end; U[] mapped = (U[]) new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>(mapped, size); @@ -167,7 +181,11 @@ public LongStream mapToLong(IntToLongFunction mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -180,7 +198,11 @@ public DoubleStream mapToDouble(IntToDoubleFunction mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -257,7 +279,11 @@ public IntStream peek(IntConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -316,13 +342,16 @@ public IntStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(IntConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(IntConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java index a7fb04e385..19166895f3 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtLongStream.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.LongSummaryStatistics; import java.util.NoSuchElementException; @@ -123,8 +124,13 @@ public LongStream filter(LongPredicate predicate) { int j = 0; for (int i = 0; i < size; i++) { long element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -138,7 +144,11 @@ public LongStream map(LongUnaryOperator mapper) { int size = elementData.end; Long[] mapped = new Long[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsLong(elementData.get(i)); + try { + mapped[i] = mapper.applyAsLong(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(mapped, size); @@ -154,7 +164,11 @@ public Stream mapToObj(LongFunction mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((U[]) mapped, size); @@ -167,7 +181,11 @@ public IntStream mapToInt(LongToIntFunction mapper) { int size = elementData.end; Integer[] mapped = new Integer[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsInt(elementData.get(i)); + try { + mapped[i] = mapper.applyAsInt(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(mapped, size); @@ -180,7 +198,11 @@ public DoubleStream mapToDouble(LongToDoubleFunction mapper) { int size = elementData.end; Double[] mapped = new Double[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.applyAsDouble(elementData.get(i)); + try { + mapped[i] = mapper.applyAsDouble(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(mapped, size); @@ -257,7 +279,11 @@ public LongStream peek(LongConsumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -316,13 +342,16 @@ public LongStream skip(long n) { @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEach(LongConsumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } - @SuppressWarnings("ResultOfMethodCallIgnored") @Override public void forEachOrdered(LongConsumer action) { - peek(action); + forEach(action); } @Override diff --git a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java index 132635a43e..fd50aad64c 100644 --- a/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java +++ b/utbot-framework/src/main/java/org/utbot/engine/overrides/stream/UtStream.java @@ -4,6 +4,7 @@ import org.utbot.engine.overrides.UtArrayMock; import org.utbot.engine.overrides.collections.RangeModifiableUnlimitedArray; import org.utbot.engine.overrides.collections.UtGenericStorage; +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException; import java.util.Comparator; import java.util.Iterator; @@ -120,8 +121,13 @@ public Stream filter(Predicate predicate) { int j = 0; for (int i = 0; i < size; i++) { E element = elementData.get(i); - if (predicate.test(element)) { - filtered[j++] = element; + + try { + if (predicate.test(element)) { + filtered[j++] = element; + } + } catch (Exception e) { + throw new UtStreamConsumingException(e); } } @@ -136,7 +142,11 @@ public Stream map(Function mapper) { int size = elementData.end; Object[] mapped = new Object[size]; for (int i = 0; i < size; i++) { - mapped[i] = mapper.apply(elementData.get(i)); + try { + mapped[i] = mapper.apply(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtStream<>((R[]) mapped, size); @@ -149,7 +159,11 @@ public IntStream mapToInt(ToIntFunction mapper) { int size = elementData.end; Integer[] data = new Integer[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsInt(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsInt(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtIntStream(data, size); @@ -162,7 +176,11 @@ public LongStream mapToLong(ToLongFunction mapper) { int size = elementData.end; Long[] data = new Long[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsLong(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsLong(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtLongStream(data, size); @@ -175,7 +193,11 @@ public DoubleStream mapToDouble(ToDoubleFunction mapper) { int size = elementData.end; Double[] data = new Double[size]; for (int i = 0; i < size; i++) { - data[i] = mapper.applyAsDouble(elementData.getWithoutClassCastExceptionCheck(i)); + try { + data[i] = mapper.applyAsDouble(elementData.getWithoutClassCastExceptionCheck(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } return new UtDoubleStream(data, size); @@ -319,7 +341,11 @@ public Stream peek(Consumer action) { int size = elementData.end; for (int i = 0; i < size; i++) { - action.accept(elementData.get(i)); + try { + action.accept(elementData.get(i)); + } catch (Exception e) { + throw new UtStreamConsumingException(e); + } } // returned stream should be opened, so we "reopen" this stream to return it @@ -389,12 +415,16 @@ public Stream skip(long n) { @Override public void forEach(Consumer action) { - peek(action); + try { + peek(action); + } catch (UtStreamConsumingException e) { + // Since this is a terminal operation, we should throw an original exception + } } @Override public void forEachOrdered(Consumer action) { - peek(action); + forEach(action); } @NotNull diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt index 24876fe55f..2116009cf1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Resolver.kt @@ -90,6 +90,8 @@ import kotlin.math.max import kotlin.math.min import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException +import org.utbot.framework.plugin.api.UtStreamConsumingFailure // hack const val MAX_LIST_SIZE = 10 @@ -370,6 +372,12 @@ class Resolver( */ private fun SymbolicFailure.resolve(): UtExecutionFailure { val exception = concreteException() + + if (exception is UtStreamConsumingException) { + // This exception is artificial and is not really thrown + return UtStreamConsumingFailure(exception) + } + return if (explicit) { UtExplicitlyThrownException(exception, inNestedMethod) } else { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt index f28937fcd0..dda122b03b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/StreamWrappers.kt @@ -18,16 +18,20 @@ import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.doubleArrayClassId import org.utbot.framework.plugin.api.util.doubleClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intArrayClassId import org.utbot.framework.plugin.api.util.intClassId +import org.utbot.framework.plugin.api.util.intStreamClassId import org.utbot.framework.plugin.api.util.isArray import org.utbot.framework.plugin.api.util.isPrimitiveWrapper import org.utbot.framework.plugin.api.util.longArrayClassId import org.utbot.framework.plugin.api.util.longClassId +import org.utbot.framework.plugin.api.util.longStreamClassId import org.utbot.framework.plugin.api.util.methodId import org.utbot.framework.plugin.api.util.objectArrayClassId import org.utbot.framework.plugin.api.util.objectClassId +import org.utbot.framework.plugin.api.util.streamClassId import org.utbot.framework.util.nextModelName /** @@ -58,10 +62,10 @@ enum class UtStreamClass { val overriddenStreamClassId: ClassId get() = when (this) { - UT_STREAM -> java.util.stream.Stream::class.java.id - UT_INT_STREAM -> java.util.stream.IntStream::class.java.id - UT_LONG_STREAM -> java.util.stream.LongStream::class.java.id - UT_DOUBLE_STREAM -> java.util.stream.DoubleStream::class.java.id + UT_STREAM -> streamClassId + UT_INT_STREAM -> intStreamClassId + UT_LONG_STREAM -> longStreamClassId + UT_DOUBLE_STREAM -> doubleStreamClassId } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt index 535c117083..62868da773 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt @@ -8,6 +8,7 @@ import org.utbot.engine.pc.mkAnd import org.utbot.engine.pc.mkEq import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.util.UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE import soot.ArrayType import soot.IntType import soot.NullType @@ -192,7 +193,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy } /** - * Remove wrapper types and, if any other type is available, artificial entities. + * Remove wrapper types, classes from the visible for Soot package and, if any other type is available, artificial entities. */ private fun TypeStorage.removeInappropriateTypes(): TypeStorage { val leastCommonSootClass = (leastCommonType as? RefType)?.sootClass @@ -211,11 +212,15 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy return@filter keepArtificialEntities } - // All wrappers should filtered out because they could not be instantiated + // All wrappers and classes from the visible for Soot package should be filtered out because they could not be instantiated workaround(WorkaroundReason.HACK) { if (leastCommonSootClass == OBJECT_TYPE && sootClass.isOverridden) { return@filter false } + + if (sootClass.packageName == UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE) { + return@filter false + } } return@filter true diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt index 19d1237d98..ab5d94172b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/builtin/UtilMethodBuiltins.kt @@ -9,6 +9,7 @@ import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinConstructorId import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.util.baseStreamClassId import org.utbot.framework.plugin.api.util.booleanClassId import org.utbot.framework.plugin.api.util.builtinConstructorId import org.utbot.framework.plugin.api.util.classClassId @@ -48,6 +49,7 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) { mapsDeepEqualsMethodId, hasCustomEqualsMethodId, getArrayLengthMethodId, + consumeBaseStreamMethodId, buildStaticLambdaMethodId, buildLambdaMethodId, getLookupInMethodId, @@ -165,6 +167,13 @@ internal abstract class UtilMethodProvider(val utilClassId: ClassId) { arguments = arrayOf(objectClassId) ) + val consumeBaseStreamMethodId: MethodId + get() = utilClassId.utilMethodId( + name = "consumeBaseStream", + returnType = voidClassId, + arguments = arrayOf(baseStreamClassId) + ) + val buildStaticLambdaMethodId: MethodId get() = utilClassId.utilMethodId( name = "buildStaticLambda", diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt index fb1d621e6a..4a2f433f36 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt @@ -396,6 +396,9 @@ internal interface CgContextOwner { val getArrayLength: MethodId get() = utilMethodProvider.getArrayLengthMethodId + val consumeBaseStream: MethodId + get() = utilMethodProvider.consumeBaseStreamMethodId + val buildStaticLambda: MethodId get() = utilMethodProvider.buildStaticLambdaMethodId diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt index 8a985e0c39..82b75f8609 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt @@ -17,6 +17,7 @@ import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManage import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManagerImpl.FieldAccessorSuitability.Suitable import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getStatementConstructorBy import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor.CgComponents.getVariableConstructorBy +import org.utbot.framework.codegen.model.constructor.util.arrayInitializer import org.utbot.framework.codegen.model.constructor.util.getAmbiguousOverloadsOf import org.utbot.framework.codegen.model.constructor.util.importIfNeeded import org.utbot.framework.codegen.model.constructor.util.isUtil @@ -611,6 +612,7 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA mapsDeepEqualsMethodId, hasCustomEqualsMethodId, getArrayLengthMethodId, + consumeBaseStreamMethodId, getLambdaCapturedArgumentTypesMethodId, getLambdaCapturedArgumentValuesMethodId, getInstantiatedMethodTypeMethodId, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 2b126861bd..191876b223 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -1,7 +1,9 @@ package org.utbot.framework.codegen.model.constructor.tree import org.utbot.common.PathUtil +import org.utbot.common.WorkaroundReason import org.utbot.common.isStatic +import org.utbot.common.workaround import org.utbot.framework.assemble.assemble import org.utbot.framework.codegen.ForceStaticMocking import org.utbot.framework.codegen.ParametrizedTestSource @@ -146,6 +148,19 @@ import java.lang.reflect.InvocationTargetException import java.security.AccessControlException import java.lang.reflect.ParameterizedType import org.utbot.framework.UtSettings +import org.utbot.framework.plugin.api.UtExecutionResult +import org.utbot.framework.plugin.api.UtStreamConsumingFailure +import org.utbot.framework.plugin.api.util.allSuperTypes +import org.utbot.framework.plugin.api.util.baseStreamClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId +import org.utbot.framework.plugin.api.util.doubleStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.intStreamClassId +import org.utbot.framework.plugin.api.util.intStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.longStreamClassId +import org.utbot.framework.plugin.api.util.longStreamToArrayMethodId +import org.utbot.framework.plugin.api.util.streamClassId +import org.utbot.framework.plugin.api.util.streamToArrayMethodId import org.utbot.framework.plugin.api.util.isStatic private const val DEEP_EQUALS_MAX_DEPTH = 5 // TODO move it to plugin settings? @@ -171,6 +186,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private val fieldsOfExecutionResults = mutableMapOf, MutableList>() + /** + * Contains whether [UtStreamConsumingFailure] is in [CgMethodTestSet] for parametrized tests. + * See [WorkaroundReason.CONSUME_DIRTY_STREAMS]. + */ + private var containsStreamConsumingFailureForParametrizedTests: Boolean = false + private fun setupInstrumentation() { if (currentExecution is UtSymbolicExecution) { val execution = currentExecution as UtSymbolicExecution @@ -288,41 +309,35 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c emptyLineIfNeeded() val method = currentExecutable as MethodId val currentExecution = currentExecution!! + val executionResult = currentExecution.result + // build assertions - currentExecution.result - .onSuccess { result -> + executionResult + .onSuccess { resultModel -> methodType = SUCCESSFUL - // TODO possible engine bug - void method return type and result not UtVoidModel - if (result.isUnit() || method.returnType == voidClassId) { + // TODO possible engine bug - void method return type and result model not UtVoidModel + if (resultModel.isUnit() || method.returnType == voidClassId) { +thisInstance[method](*methodArguments.toTypedArray()) } else { - resultModel = result - val expected = variableConstructor.getOrCreateVariable(result, "expected") + this.resultModel = resultModel + val expected = variableConstructor.getOrCreateVariable(resultModel, "expected") assertEquality(expected, actual) } } - .onFailure { exception -> processExecutionFailure(exception) } + .onFailure { exception -> processExecutionFailure(exception, executionResult) } } else -> {} // TODO: check this specific case } } - private fun processExecutionFailure(exception: Throwable) { - val methodInvocationBlock = { - with(currentExecutable) { - when (this) { - is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() - is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() - else -> {} // TODO: check this specific case - } - } - } + private fun processExecutionFailure(exceptionFromAnalysis: Throwable, executionResult: UtExecutionResult) { + val (methodInvocationBlock, expectedException) = constructExceptionProducingBlock(exceptionFromAnalysis, executionResult) when (methodType) { - SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $exception") + SUCCESSFUL -> error("Unexpected successful without exception method type for execution with exception $expectedException") PASSED_EXCEPTION -> { - testFrameworkManager.expectException(exception::class.id) { + testFrameworkManager.expectException(expectedException::class.id) { methodInvocationBlock() } } @@ -332,23 +347,66 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c methodInvocationBlock() } } - CRASH -> when (exception) { + CRASH -> when (expectedException) { is ConcreteExecutionFailureException -> { writeWarningAboutCrash() methodInvocationBlock() } is AccessControlException -> { // exception from sandbox - writeWarningAboutFailureTest(exception) + writeWarningAboutFailureTest(expectedException) } - else -> error("Unexpected crash suite for failing execution with $exception exception") + else -> error("Unexpected crash suite for failing execution with $expectedException exception") } FAILING -> { - writeWarningAboutFailureTest(exception) + writeWarningAboutFailureTest(expectedException) methodInvocationBlock() } - PARAMETRIZED -> error("Unexpected $PARAMETRIZED method type for failing execution with $exception exception") + PARAMETRIZED -> error("Unexpected $PARAMETRIZED method type for failing execution with $expectedException exception") + } + } + + private fun constructExceptionProducingBlock( + exceptionFromAnalysis: Throwable, + executionResult: UtExecutionResult + ): Pair<() -> Unit, Throwable> { + if (executionResult is UtStreamConsumingFailure) { + return constructStreamConsumingBlock() to executionResult.rootCauseException } + + return { + with(currentExecutable) { + when (this) { + is MethodId -> thisInstance[this](*methodArguments.toTypedArray()).intercepted() + is ConstructorId -> this(*methodArguments.toTypedArray()).intercepted() + else -> {} // TODO: check this specific case + } + } + } to exceptionFromAnalysis + } + + private fun constructStreamConsumingBlock(): () -> Unit { + val executable = currentExecutable + + require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { + "Unexpected non-stream returning executable $executable" + } + + val allSuperTypesOfReturn = executable.returnType.allSuperTypes().toSet() + + val streamConsumingMethodId = when { + // The order is important since all streams implement BaseStream + intStreamClassId in allSuperTypesOfReturn -> intStreamToArrayMethodId + longStreamClassId in allSuperTypesOfReturn -> longStreamToArrayMethodId + doubleStreamClassId in allSuperTypesOfReturn -> doubleStreamToArrayMethodId + streamClassId in allSuperTypesOfReturn -> streamToArrayMethodId + else -> { + // BaseStream, use util method to consume it + return { +utilsClassId[consumeBaseStream](actual) } + } + } + + return { +actual[streamConsumingMethodId]() } } private fun shouldTestPassWithException(execution: UtExecution, exception: Throwable): Boolean { @@ -417,14 +475,16 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c is ConstructorId -> generateConstructorCall(currentExecutable!!, currentExecution!!) is MethodId -> { val method = currentExecutable as MethodId - currentExecution!!.result - .onSuccess { result -> - if (result.isUnit()) { + val executionResult = currentExecution!!.result + + executionResult + .onSuccess { resultModel -> + if (resultModel.isUnit()) { +thisInstance[method](*methodArguments.toTypedArray()) } else { //"generic" expected variable is represented with a wrapper if //actual result is primitive to support cases with exceptions. - resultModel = if (result is UtPrimitiveModel) assemble(result) else result + this.resultModel = if (resultModel is UtPrimitiveModel) assemble(resultModel) else resultModel val expectedVariable = currentMethodParameters[CgParameterKind.ExpectedResult]!! val expectedExpression = CgNotNullAssertion(expectedVariable) @@ -432,7 +492,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c assertEquality(expectedExpression, actual) } } - .onFailure { thisInstance[method](*methodArguments.toTypedArray()).intercepted() } + .onFailure { + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + if (containsStreamConsumingFailureForParametrizedTests) { + constructStreamConsumingBlock().invoke() + } else { + thisInstance[method](*methodArguments.toTypedArray()).intercepted() + } + } + } } else -> {} // TODO: check this specific case } @@ -1164,7 +1232,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c // we cannot generate any assertions for constructor testing // but we need to generate a constructor call val constructorCall = currentExecutableId as ConstructorId - currentExecution.result + val executionResult = currentExecution.result + + executionResult .onSuccess { methodType = SUCCESSFUL @@ -1176,7 +1246,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c constructorCall(*methodArguments.toTypedArray()) } } - .onFailure { exception -> processExecutionFailure(exception) } + .onFailure { exception -> processExecutionFailure(exception, executionResult) } } /** @@ -1197,7 +1267,15 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c actual.type.isPrimitive -> generateDeepEqualsAssertion(expected, actual) else -> ifStatement( CgEqualTo(expected, nullLiteral()), - trueBranch = { +testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement() }, + trueBranch = { + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + if (containsStreamConsumingFailureForParametrizedTests) { + constructStreamConsumingBlock().invoke() + } else { + +testFrameworkManager.assertions[testFramework.assertNull](actual).toStatement() + } + } + }, falseBranch = { +testFrameworkManager.assertions[testFrameworkManager.assertNotNull](actual).toStatement() generateDeepEqualsAssertion(expected, actual) @@ -1226,7 +1304,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } private fun recordActualResult() { - currentExecution!!.result.onSuccess { result -> + val executionResult = currentExecution!!.result + + executionResult.onSuccess { resultModel -> when (val executable = currentExecutable) { is ConstructorId -> { // there is nothing to generate for constructors @@ -1234,13 +1314,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } is BuiltinMethodId -> error("Unexpected BuiltinMethodId $currentExecutable while generating actual result") is MethodId -> { - // TODO possible engine bug - void method return type and result not UtVoidModel - if (result.isUnit() || executable.returnType == voidClassId) return + // TODO possible engine bug - void method return type and result model not UtVoidModel + if (resultModel.isUnit() || executable.returnType == voidClassId) return emptyLineIfNeeded() actual = newVar( - CgClassId(result.classId, isNullable = result is UtNullModel), + CgClassId(resultModel.classId, isNullable = resultModel is UtNullModel), "actual" ) { thisInstance[executable](*methodArguments.toTypedArray()) @@ -1248,6 +1328,32 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c } else -> {} // TODO: check this specific case } + }.onFailure { + processActualInvocationFailure(executionResult) + } + } + + private fun processActualInvocationFailure(executionResult: UtExecutionResult) { + when (executionResult) { + is UtStreamConsumingFailure -> processStreamConsumingException(executionResult.rootCauseException) + else -> {} // Do nothing for now + } + } + + private fun processStreamConsumingException(innerException: Throwable) { + val executable = currentExecutable + + require((executable is MethodId) && (executable.returnType isSubtypeOf baseStreamClassId)) { + "Unexpected exception $innerException during stream consuming in non-stream returning executable $executable" + } + + emptyLineIfNeeded() + + actual = newVar( + CgClassId(executable.returnType, isNullable = false), + "actual" + ) { + thisInstance[executable](*methodArguments.toTypedArray()) } } @@ -1353,6 +1459,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c //may be a heuristic to select a model with minimal number of internal nulls should be used val genericExecution = chooseGenericExecution(testSet.executions) + workaround(WorkaroundReason.CONSUME_DIRTY_STREAMS) { + containsStreamConsumingFailureForParametrizedTests = testSet.executions.any { + it.result is UtStreamConsumingFailure + } + } + val statics = genericExecution.stateBefore.statics return withTestMethodScope(genericExecution) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt index fb51419a41..85ba4dcbe5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/UtilMethods.kt @@ -65,6 +65,7 @@ internal fun UtilMethodProvider.utilMethodTextById( mapsDeepEqualsMethodId -> mapsDeepEquals(visibility, codegenLanguage) hasCustomEqualsMethodId -> hasCustomEquals(visibility, codegenLanguage) getArrayLengthMethodId -> getArrayLength(visibility, codegenLanguage) + consumeBaseStreamMethodId -> consumeBaseStream(visibility, codegenLanguage) buildStaticLambdaMethodId -> buildStaticLambda(visibility, codegenLanguage) buildLambdaMethodId -> buildLambda(visibility, codegenLanguage) // the following methods are used only by other util methods, so they can always be private @@ -828,6 +829,23 @@ private fun getArrayLength(visibility: Visibility, language: CodegenLanguage) = """.trimIndent() } +private fun consumeBaseStream(visibility: Visibility, language: CodegenLanguage) = + when (language) { + CodegenLanguage.JAVA -> + """ + ${visibility by language}static void consumeBaseStream(java.util.stream.BaseStream stream) { + stream.iterator().forEachRemaining(value -> {}); + } + """.trimIndent() + CodegenLanguage.KOTLIN -> { + """ + ${visibility by language}fun consumeBaseStream(stream: java.util.stream.BaseStream<*, *>) { + stream.iterator().forEachRemaining {} + } + """.trimIndent() + } + } + private fun buildStaticLambda(visibility: Visibility, language: CodegenLanguage) = when (language) { CodegenLanguage.JAVA -> @@ -1381,6 +1399,7 @@ private fun TestClassUtilMethodProvider.regularImportsByUtilMethod( } hasCustomEqualsMethodId -> emptyList() getArrayLengthMethodId -> listOf(java.lang.reflect.Array::class.id) + consumeBaseStreamMethodId -> listOf(java.util.stream.BaseStream::class.id) buildStaticLambdaMethodId -> when (codegenLanguage) { CodegenLanguage.JAVA -> listOf( MethodHandles::class.id, Method::class.id, MethodType::class.id, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt new file mode 100644 index 0000000000..82cb2908e5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/StreamConstructors.kt @@ -0,0 +1,123 @@ +package org.utbot.framework.concrete + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.UtArrayModel +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtNullModel +import org.utbot.framework.plugin.api.UtPrimitiveModel +import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.util.defaultValueModel +import org.utbot.framework.plugin.api.util.doubleArrayClassId +import org.utbot.framework.plugin.api.util.doubleStreamClassId +import org.utbot.framework.plugin.api.util.intArrayClassId +import org.utbot.framework.plugin.api.util.intStreamClassId +import org.utbot.framework.plugin.api.util.isPrimitiveWrapper +import org.utbot.framework.plugin.api.util.longArrayClassId +import org.utbot.framework.plugin.api.util.longStreamClassId +import org.utbot.framework.plugin.api.util.methodId +import org.utbot.framework.plugin.api.util.objectArrayClassId +import org.utbot.framework.plugin.api.util.streamClassId +import org.utbot.framework.util.modelIdCounter +import org.utbot.framework.util.valueToClassId + +/** + * Max number of elements in any concrete stream. + */ +private const val STREAM_ELEMENTS_LIMIT: Int = 1_000_000 + +internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, private val elementsClassId: ClassId) : UtAssembleModelConstructorBase() { + private val singleElementClassId: ClassId = elementsClassId.elementClassId + ?: error("Stream $streamClassId elements have to be an array but $elementsClassId found") + + private val elementDefaultValueModel: UtModel = singleElementClassId.defaultValueModel() + + override fun provideInstantiationCall( + internalConstructor: UtModelConstructorInterface, + value: Any, + classId: ClassId + ): UtExecutableCallModel { + value as java.util.stream.BaseStream<*, *> + + // If [value] constructed incorrectly (some inner transient fields are null, etc.) this may fail. + // This value will be constructed as UtCompositeModel. + val models = value + .iterator() + .asSequence() + .map { internalConstructor.construct(it, valueToClassId(it)) } + .take(STREAM_ELEMENTS_LIMIT) + .toList() + + if (models.isEmpty()) { + return UtExecutableCallModel( + instance = null, + executable = emptyMethodId, + params = emptyList() + ) + } + + val varargModelsArray = UtArrayModel( + id = modelIdCounter.incrementAndGet(), + classId = elementsClassId, + length = models.size, + constModel = elementDefaultValueModel, + stores = models.mapIndexed { i, model -> i to model.wrapperModelToPrimitiveModel() }.toMap(mutableMapOf()) + ) + + return UtExecutableCallModel( + instance = null, + executable = ofMethodId, + params = listOf(varargModelsArray) + ) + } + + override fun UtAssembleModel.provideModificationChain( + internalConstructor: UtModelConstructorInterface, + value: Any + ): List = emptyList() + + private val emptyMethodId: MethodId = methodId( + classId = this.streamClassId, + name = "empty", + returnType = this.streamClassId, + arguments = emptyArray() + ) + + private val ofMethodId: MethodId = methodId( + classId = this.streamClassId, + name = "of", + returnType = this.streamClassId, + arguments = arrayOf(elementsClassId) // vararg + ) + + /** + * Transforms [this] to [UtPrimitiveModel] if it is an [UtAssembleModel] for the corresponding wrapper + * (primitive int and wrapper Integer, etc.), and throws an error otherwise. + */ + private fun UtModel.wrapperModelToPrimitiveModel(): UtModel { + if (!classId.isPrimitiveWrapper) { + // We do not need to transform classes other than primitive wrappers + return this + } + + require(this !is UtNullModel) { + "Unexpected null value in wrapper for primitive stream ${this@AbstractStreamConstructor}" + } + + require(this is UtAssembleModel) { + "Unexpected not wrapper assemble model $this for value in wrapper " + + "for primitive stream ${this@AbstractStreamConstructor.streamClassId}" + } + + return (instantiationCall.params.firstOrNull() as? UtPrimitiveModel) + ?: error("No primitive value parameter for wrapper constructor $instantiationCall in model $this " + + "in wrapper for primitive stream ${this@AbstractStreamConstructor.streamClassId}") + } +} + +internal class BaseStreamConstructor : AbstractStreamConstructor(streamClassId, objectArrayClassId) +internal class IntStreamConstructor : AbstractStreamConstructor(intStreamClassId, intArrayClassId) +internal class LongStreamConstructor : AbstractStreamConstructor(longStreamClassId, longArrayClassId) +internal class DoubleStreamConstructor : AbstractStreamConstructor(doubleStreamClassId, doubleArrayClassId) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt index e870870492..82be902336 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtAssembleModelConstructors.kt @@ -4,10 +4,15 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtStatementModel +import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.primitiveWrappers import org.utbot.framework.plugin.api.util.voidWrapperClassId import org.utbot.framework.util.nextModelName +import java.util.stream.BaseStream +import java.util.stream.DoubleStream +import java.util.stream.IntStream +import java.util.stream.LongStream private val predefinedConstructors = mutableMapOf, () -> UtAssembleModelConstructorBase>( /** @@ -27,7 +32,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.List::class.java to { CollectionConstructor() }, java.util.concurrent.CopyOnWriteArrayList::class.java to { CollectionConstructor() }, - /** * Queues, deques */ @@ -40,7 +44,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.Queue::class.java to { CollectionConstructor() }, java.util.Deque::class.java to { CollectionConstructor() }, - /** * Sets */ @@ -50,8 +53,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode java.util.AbstractSet::class.java to { CollectionConstructor() }, java.util.Set::class.java to { CollectionConstructor() }, - - /** * Maps */ @@ -69,7 +70,6 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode */ java.util.Hashtable::class.java to { MapConstructor() }, - /** * String wrapper */ @@ -90,6 +90,14 @@ private val predefinedConstructors = mutableMapOf, () -> UtAssembleMode internal fun findUtAssembleModelConstructor(classId: ClassId): UtAssembleModelConstructorBase? = predefinedConstructors[classId.jClass]?.invoke() +internal fun findStreamConstructor(stream: BaseStream<*, *>): UtAssembleModelConstructorBase = + when (stream) { + is IntStream -> IntStreamConstructor() + is LongStream -> LongStreamConstructor() + is DoubleStream -> DoubleStreamConstructor() + else -> BaseStreamConstructor() + } + internal abstract class UtAssembleModelConstructorBase { fun constructAssembleModel( internalConstructor: UtModelConstructorInterface, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt index 16eed51791..e8da79b550 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt @@ -23,6 +23,8 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation import org.utbot.framework.plugin.api.UtSandboxFailure import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.id @@ -189,7 +191,12 @@ object UtExecutionInstrumentation : Instrumentation { val utModelConstructor = UtModelConstructor(cache, utCompositeModelStrategy) utModelConstructor.run { val concreteUtModelResult = concreteResult.fold({ - UtExecutionSuccess(construct(it, returnClassId)) + try { + val model = construct(it, returnClassId) + UtExecutionSuccess(model) + } catch (e: Exception) { + processExceptionDuringModelConstruction(e) + } }) { sortOutException(it) } @@ -230,6 +237,12 @@ object UtExecutionInstrumentation : Instrumentation { } } + private fun processExceptionDuringModelConstruction(e: Exception): UtExecutionResult = + when (e) { + is UtStreamConsumingException -> UtStreamConsumingFailure(e) + else -> throw e + } + override fun getStaticField(fieldId: FieldId): Result = delegateInstrumentation.getStaticField(fieldId).map { value -> val cache = IdentityHashMap() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt index a2194dedfe..c23fb7d052 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtModelConstructor.kt @@ -14,6 +14,7 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.booleanClassId @@ -33,6 +34,7 @@ import org.utbot.framework.util.isInaccessibleViaReflection import org.utbot.framework.util.valueToClassId import java.lang.reflect.Modifier import java.util.IdentityHashMap +import java.util.stream.BaseStream /** * Represents common interface for model constructors. @@ -111,6 +113,7 @@ class UtModelConstructor( is Array<*> -> constructFromArray(value) is Enum<*> -> constructFromEnum(value) is Class<*> -> constructFromClass(value) + is BaseStream<*, *> -> constructFromStream(value) else -> constructFromAny(value) } } @@ -239,6 +242,22 @@ class UtModelConstructor( utModel } + private fun constructFromStream(stream: BaseStream<*, *>): UtModel = + constructedObjects.getOrElse(stream) { + val streamConstructor = findStreamConstructor(stream) + + try { + streamConstructor.constructAssembleModel(this, stream, valueToClassId(stream), handleId(stream)) { + constructedObjects[stream] = it + } + } catch (e: Exception) { + // An exception occurs during consuming of the stream - + // remove the constructed object and throw this exception as a result + constructedObjects.remove(stream) + throw UtStreamConsumingException(e) + } + } + /** * First tries to construct UtAssembleModel. If failure, constructs UtCompositeModel. */ diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt index a1de0a1bdb..1a2c3da9ee 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/SootUtils.kt @@ -5,6 +5,7 @@ import org.utbot.engine.jimpleBody import org.utbot.engine.pureJavaSignature import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.visible.UtStreamConsumingException import org.utbot.framework.plugin.services.JdkInfo import soot.G import soot.PackManager @@ -98,9 +99,10 @@ private fun initSoot(buildDirs: List, classpath: String?, jdkInfo: JdkInfo val isOverriddenPackage = it.packageName.startsWith(UTBOT_OVERRIDDEN_PACKAGE_PREFIX) val isExamplesPackage = it.packageName.startsWith(UTBOT_EXAMPLES_PACKAGE_PREFIX) val isApiPackage = it.packageName.startsWith(UTBOT_API_PACKAGE_PREFIX) + val isVisiblePackage = it.packageName.startsWith(UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE) - // remove if it is not a part of the examples (CUT), not a part of our API and not an override - if (!isOverriddenPackage && !isExamplesPackage && !isApiPackage) { + // remove if it is not a part of the examples (CUT), not a part of our API, not an override and not from visible for soot + if (!isOverriddenPackage && !isExamplesPackage && !isApiPackage && !isVisiblePackage) { Scene.v().removeClass(it) return@forEach } @@ -183,6 +185,7 @@ private val classesToLoad = arrayOf( org.utbot.engine.overrides.stream.Arrays::class, org.utbot.engine.overrides.collections.Collection::class, org.utbot.engine.overrides.collections.List::class, + UtStreamConsumingException::class, org.utbot.engine.overrides.stream.UtStream::class, org.utbot.engine.overrides.stream.UtIntStream::class, org.utbot.engine.overrides.stream.UtLongStream::class, @@ -200,4 +203,5 @@ private const val UTBOT_PACKAGE_PREFIX = "org.utbot" private const val UTBOT_EXAMPLES_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.examples" private const val UTBOT_API_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.api" private const val UTBOT_OVERRIDDEN_PACKAGE_PREFIX = "$UTBOT_PACKAGE_PREFIX.engine.overrides" +internal const val UTBOT_FRAMEWORK_API_VISIBLE_PACKAGE = "$UTBOT_PACKAGE_PREFIX.framework.plugin.api.visible" private const val SOOT_PACKAGE_PREFIX = "soot." \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java index 8760f36f55..b636394152 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/BaseStreamExample.java @@ -21,16 +21,6 @@ @SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) public class BaseStreamExample { - Stream returningStreamExample(List list) { - UtMock.assume(list != null); - - if (list.isEmpty()) { - return list.stream(); - } else { - return list.stream(); - } - } - Stream returningStreamAsParameterExample(Stream s) { UtMock.assume(s != null); return s; diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java index a86d6f953d..dc8801f498 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/DoubleStreamExample.java @@ -18,19 +18,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class DoubleStreamExample { - DoubleStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToDoubleFunction shortToDoubleFunction = value -> value == null ? 0 : value.doubleValue(); - final DoubleStream doubles = list.stream().mapToDouble(shortToDoubleFunction); - - if (list.isEmpty()) { - return doubles; - } else { - return doubles; - } - } - DoubleStream returningStreamAsParameterExample(DoubleStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java index 58e57e8843..2e3e33f122 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/IntStreamExample.java @@ -21,19 +21,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class IntStreamExample { - IntStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToIntFunction shortToIntFunction = value -> value == null ? 0 : value.intValue(); - final IntStream ints = list.stream().mapToInt(shortToIntFunction); - - if (list.isEmpty()) { - return ints; - } else { - return ints; - } - } - IntStream returningStreamAsParameterExample(IntStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java index 1a071fa69c..c821714069 100644 --- a/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/LongStreamExample.java @@ -20,19 +20,6 @@ @SuppressWarnings("IfStatementWithIdenticalBranches") public class LongStreamExample { - LongStream returningStreamExample(List list) { - UtMock.assume(list != null); - - final ToLongFunction shortToLongFunction = value -> value == null ? 0 : value.longValue(); - final LongStream longs = list.stream().mapToLong(shortToLongFunction); - - if (list.isEmpty()) { - return longs; - } else { - return longs; - } - } - LongStream returningStreamAsParameterExample(LongStream s) { UtMock.assume(s != null); diff --git a/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java b/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java new file mode 100644 index 0000000000..566a57f091 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/stream/StreamsAsMethodResultExample.java @@ -0,0 +1,83 @@ +package org.utbot.examples.stream; + +import org.utbot.api.mock.*; + +import java.util.List; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +@SuppressWarnings({"IfStatementWithIdenticalBranches", "RedundantOperationOnEmptyContainer"}) +public class StreamsAsMethodResultExample { + Stream returningStreamExample(List list) { + UtMock.assume(list != null); + + if (list.isEmpty()) { + return list.stream(); + } + + return list.stream(); + } + + IntStream returningIntStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToInt(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToInt(value -> value); + } + + return list.stream().mapToInt(value -> value); + } + + LongStream returningLongStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToLong(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToLong(value -> value); + } + + return list.stream().mapToLong(value -> value); + } + + DoubleStream returningDoubleStreamExample(List list) { + UtMock.assume(list != null); + + final int size = list.size(); + + if (size == 0) { + return list.stream().mapToDouble(value -> value); + } + + UtMock.assume(size == 1); + + final Integer integer = list.get(0); + + if (integer == null) { + return list.stream().mapToDouble(value -> value); + } + + return list.stream().mapToDouble(value -> value); + } +} + diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt index b67d08e4fd..d05c71a84e 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/Summarization.kt @@ -29,6 +29,7 @@ import org.utbot.framework.plugin.api.UtExplicitlyThrownException import org.utbot.framework.plugin.api.UtImplicitlyThrownException import org.utbot.framework.plugin.api.UtOverflowFailure import org.utbot.framework.plugin.api.UtSandboxFailure +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.humanReadableName import org.utbot.framework.plugin.api.util.jClass @@ -244,12 +245,13 @@ class Summarization(val sourceFile: File?, val invokeDescriptions: List unsuccessfulFuzzerExecutions.add(utExecution) - is UtExplicitlyThrownException -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtImplicitlyThrownException -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtOverflowFailure -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtSandboxFailure -> unsuccessfulFuzzerExecutions.add(utExecution) - is UtTimeoutException -> unsuccessfulFuzzerExecutions.add(utExecution) + is UtConcreteExecutionFailure, + is UtExplicitlyThrownException, + is UtImplicitlyThrownException, + is UtOverflowFailure, + is UtSandboxFailure, + is UtTimeoutException, + is UtStreamConsumingFailure -> unsuccessfulFuzzerExecutions.add(utExecution) is UtExecutionSuccess -> successfulFuzzerExecutions.add(utExecution) } } diff --git a/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt b/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt index 486df9a83f..5bced41064 100644 --- a/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt +++ b/utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt @@ -10,6 +10,7 @@ import org.utbot.framework.plugin.api.UtImplicitlyThrownException import org.utbot.framework.plugin.api.UtOverflowFailure import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.UtSandboxFailure +import org.utbot.framework.plugin.api.UtStreamConsumingFailure import org.utbot.framework.plugin.api.UtTimeoutException import org.utbot.framework.plugin.api.util.humanReadableName import org.utbot.framework.plugin.api.util.isCheckedException @@ -186,6 +187,7 @@ private fun UtExecutionResult.clusterKind() = when (this) { is UtExecutionSuccess -> ExecutionGroup.SUCCESSFUL_EXECUTIONS is UtImplicitlyThrownException -> if (this.exception.isCheckedException) ExecutionGroup.CHECKED_EXCEPTIONS else ExecutionGroup.ERROR_SUITE is UtExplicitlyThrownException -> if (this.exception.isCheckedException) ExecutionGroup.CHECKED_EXCEPTIONS else ExecutionGroup.EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS + is UtStreamConsumingFailure -> ExecutionGroup.ERROR_SUITE is UtOverflowFailure -> ExecutionGroup.OVERFLOWS is UtTimeoutException -> ExecutionGroup.TIMEOUTS is UtConcreteExecutionFailure -> ExecutionGroup.CRASH_SUITE