diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt index 548bb32b55..5dda9af68b 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt @@ -505,6 +505,20 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS * The behaviour of further analysis if tests generation cancellation is requested. */ var cancellationStrategyType by getEnumProperty(CancellationStrategyType.SAVE_PROCESSED_RESULTS) + + /** + * Depending on this option, sections might be analyzed or not. + * Note that some clinit sections still will be initialized using runtime information. + */ + var enableClinitSectionsAnalysis by getBooleanProperty(true) + + /** + * Process all clinit sections concretely. + * + * If [enableClinitSectionsAnalysis] is false, it disables effect of this option as well. + * Note that values processed concretely won't be replaced with unbounded symbolic variables. + */ + var processAllClinitSectionsConcretely by getBooleanProperty(false) } /** diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/testcheckers/SettingsModificators.kt b/utbot-framework-api/src/main/kotlin/org/utbot/testcheckers/SettingsModificators.kt index 7b2223947b..148eb95204 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/testcheckers/SettingsModificators.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/testcheckers/SettingsModificators.kt @@ -109,6 +109,27 @@ inline fun withoutConcrete(block: () -> T): T { } } +inline fun withProcessingClinitSections(value: Boolean, block: () -> T): T { + val prev = UtSettings.enableClinitSectionsAnalysis + UtSettings.enableClinitSectionsAnalysis = value + try { + return block() + } finally { + UtSettings.enableClinitSectionsAnalysis = prev + } +} + +inline fun withProcessingAllClinitSectionsConcretely(value: Boolean, block: () -> T): T { + val prev = UtSettings.processAllClinitSectionsConcretely + UtSettings.processAllClinitSectionsConcretely = value + try { + return block() + } finally { + UtSettings.processAllClinitSectionsConcretely = prev + } +} + + /** * Run [block] with disabled sandbox in the concrete executor */ diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/objects/ClassForTestClinitSectionsTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/objects/ClassForTestClinitSectionsTest.kt new file mode 100644 index 0000000000..2121db3aef --- /dev/null +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/objects/ClassForTestClinitSectionsTest.kt @@ -0,0 +1,68 @@ +package org.utbot.examples.objects + +import org.junit.jupiter.api.Test +import org.utbot.testcheckers.eq +import org.utbot.testcheckers.withProcessingAllClinitSectionsConcretely +import org.utbot.testcheckers.withoutConcrete +import org.utbot.testcheckers.withProcessingClinitSections +import org.utbot.testing.UtValueTestCaseChecker +import org.utbot.testing.atLeast + +internal class ClassForTestClinitSectionsTest : UtValueTestCaseChecker(testClass = ClassForTestClinitSections::class) { + @Test + fun testClinitWithoutClinitAnalysis() { + withoutConcrete { + withProcessingClinitSections(value = false) { + check( + ClassForTestClinitSections::resultDependingOnStaticSection, + eq(2), + { r -> r == -1 }, + { r -> r == 1 } + ) + } + } + } + + @Test + fun testClinitWithClinitAnalysis() { + withoutConcrete { + check( + ClassForTestClinitSections::resultDependingOnStaticSection, + eq(2), + { r -> r == -1 }, + { r -> r == 1 } + ) + } + } + + @Test + fun testProcessConcretelyWithoutClinitAnalysis() { + withoutConcrete { + withProcessingClinitSections(value = false) { + withProcessingAllClinitSectionsConcretely(value = true) { + check( + ClassForTestClinitSections::resultDependingOnStaticSection, + eq(2), + { r -> r == -1 }, + { r -> r == 1 } + ) + } + } + } + } + + @Test + fun testProcessClinitConcretely() { + withoutConcrete { + withProcessingAllClinitSectionsConcretely(value = true) { + check( + ClassForTestClinitSections::resultDependingOnStaticSection, + eq(1), + { r -> r == -1 }, + coverage = atLeast(71) + ) + } + } + } +} + diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index 8a6350006c..e5c364d9f7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -483,10 +483,23 @@ class Traverser( fieldRef: StaticFieldRef, stmt: Stmt ): Boolean { + // This order of processing options is important. + // First, we should process classes that + // cannot be analyzed without clinit sections, e.g., enums if (shouldProcessStaticFieldConcretely(fieldRef)) { return processStaticFieldConcretely(fieldRef, stmt) } + // Then we should check if we should analyze clinit sections at all + if (!UtSettings.enableClinitSectionsAnalysis) { + return false + } + + // Finally, we decide whether we should analyze clinit sections concretely or not + if (UtSettings.processAllClinitSectionsConcretely) { + return processStaticFieldConcretely(fieldRef, stmt) + } + val field = fieldRef.field val declaringClass = field.declaringClass val declaringClassId = declaringClass.id diff --git a/utbot-sample/src/main/java/org/utbot/examples/objects/ClassForTestClinitSections.java b/utbot-sample/src/main/java/org/utbot/examples/objects/ClassForTestClinitSections.java new file mode 100644 index 0000000000..f037e88283 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/objects/ClassForTestClinitSections.java @@ -0,0 +1,13 @@ +package org.utbot.examples.objects; + +public class ClassForTestClinitSections { + private static int x = 5; + + public int resultDependingOnStaticSection() { + if (x == 5) { + return -1; + } + + return 1; + } +}