Skip to content

Commit

Permalink
Fix return of JavaScript std structures from functions under test (#2167
Browse files Browse the repository at this point in the history
)

* Fix JavaScript Map, Set and Array test generation + imports optimization

* Better error handling in ValueUtil.kt

* Fix arrays, maps and sets as function return types

* Better error handling in ValueUtil.kt

* Uncommented crucial code

---------

Co-authored-by: Vladislav Kasimov <[email protected]>
  • Loading branch information
zishkaz and rudolf101 authored May 4, 2023
1 parent d18b2ac commit a9e2fe8
Show file tree
Hide file tree
Showing 22 changed files with 257 additions and 90 deletions.
14 changes: 11 additions & 3 deletions utbot-js/samples/arrays.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
///region Simple array test
function simpleArray(arr) {
if (arr[0] === 5) {
return 5
}
return 1
}

simpleArray([0, 2])

///endregion
///region Array of objects test
class ObjectParameter {

constructor(a) {
Expand All @@ -27,4 +28,11 @@ function arrayOfObjects(arr) {

let arr = []
arr[0] = new ObjectParameter(10)
arrayOfObjects(arr)
arrayOfObjects(arr)
///endregion
///region Function returns array test
function returnsArray(num) {
return [num]
}
returnsArray(5)
///endregion
12 changes: 11 additions & 1 deletion utbot-js/samples/mapStructure.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Maps in JavaScript are untyped, so only maps with basic key/value types are feasible to support
///region Simple Map test
function simpleMap(map, compareValue) {
if (map.get("a") === compareValue) {
return 5
Expand All @@ -8,4 +9,13 @@ function simpleMap(map, compareValue) {

const map1 = new Map()
map1.set("b", 3.0)
simpleMap(map1, 5)
simpleMap(map1, 5)
///endregion
///region Function returns Map test
function returnsMap(name, value) {
let temp = new Map()
return temp.set(name, value)
}

returnsMap("a", 5)
///endregion
16 changes: 13 additions & 3 deletions utbot-js/samples/setStructure.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
// Sets in JavaScript are untyped, so only sets with basic value types are feasible to support
///region Simple Set test
function setTest(set, checkValue) {
if (set.has(checkValue)) {
return 5
return set
}
return 1
return set
}

let s = new Set()
s.add(5)
s.add(6)
setTest(s, 4)
setTest(s, 4)
///endregion
///region Function returns Set test
function returnsSet(num) {
let temp = new Set()
return temp.add(num)
}

returnsSet(5)
///endregion
10 changes: 5 additions & 5 deletions utbot-js/src/main/kotlin/api/JsTestGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import fuzzer.JsStatement
import fuzzer.JsTimeoutExecution
import fuzzer.JsValidExecution
import fuzzer.runFuzzing
import java.io.File
import java.util.concurrent.CancellationException
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import mu.KotlinLogging
Expand Down Expand Up @@ -54,17 +56,15 @@ import service.TernService
import service.coverage.CoverageMode
import service.coverage.CoverageServiceProvider
import settings.JsDynamicSettings
import settings.JsExportsSettings.endComment
import settings.JsExportsSettings.startComment
import settings.JsTestGenerationSettings.fileUnderTestAliases
import settings.JsTestGenerationSettings.fuzzingThreshold
import settings.JsTestGenerationSettings.fuzzingTimeout
import utils.PathResolver
import utils.constructClass
import utils.data.ResultData
import utils.toJsAny
import java.io.File
import java.util.concurrent.CancellationException
import settings.JsExportsSettings.endComment
import settings.JsExportsSettings.startComment

private val logger = KotlinLogging.logger {}

Expand Down Expand Up @@ -214,7 +214,7 @@ class JsTestGenerator(
fuzzedValues: List<UtModel>
): UtExecutionResult {
if (resultData.isError && resultData.rawString == "Timeout") return UtTimeoutException(
TimeoutException(" Timeout in generating test for ${
TimeoutException("Timeout in generating test for ${
execId.parameters
.zip(fuzzedValues)
.joinToString(
Expand Down
89 changes: 84 additions & 5 deletions utbot-js/src/main/kotlin/api/JsUtModelConstructor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ package api

import framework.api.js.JsClassId
import framework.api.js.JsEmptyClassId
import framework.api.js.JsMethodId
import framework.api.js.JsNullModel
import framework.api.js.JsPrimitiveModel
import framework.api.js.JsUndefinedModel
import framework.api.js.util.defaultJsValueModel
import framework.api.js.util.isJsArray
import framework.api.js.util.isJsMap
import framework.api.js.util.isJsSet
import framework.api.js.util.jsErrorClassId
import framework.api.js.util.jsUndefinedClassId
import fuzzer.JsIdProvider
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
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
Expand All @@ -20,10 +27,7 @@ class JsUtModelConstructor : UtModelConstructorInterface {
@Suppress("NAME_SHADOWING")
override fun construct(value: Any?, classId: ClassId): UtModel {
val classId = classId as JsClassId
when (classId) {
jsUndefinedClassId -> return JsUndefinedModel(classId)
jsErrorClassId -> return UtModel(jsErrorClassId)
}
if (classId == jsErrorClassId) return UtModel(jsErrorClassId)
return when (value) {
null -> JsNullModel(classId)
is Byte,
Expand All @@ -35,7 +39,9 @@ class JsUtModelConstructor : UtModelConstructorInterface {
is Double,
is String,
is Boolean -> JsPrimitiveModel(value)

is List<*> -> {
constructStructure(classId, value)
}
is Map<*, *> -> {
constructObject(classId, value)
}
Expand All @@ -44,6 +50,79 @@ class JsUtModelConstructor : UtModelConstructorInterface {
}
}

private fun constructStructure(classId: JsClassId, values: List<Any?>): UtModel {
return when {
classId.isJsSet -> {
UtAssembleModel(
id = JsIdProvider.createId(),
classId = classId,
modelName = "",
instantiationCall = UtExecutableCallModel(
null,
ConstructorId(classId, emptyList()),
emptyList()
),
modificationsChainProvider = { mutableListOf() }
).apply {
this.modificationsChain as MutableList += values.map { value ->
UtExecutableCallModel(
this,
JsMethodId(
classId = classId,
name = "add",
returnTypeNotLazy = jsUndefinedClassId,
parametersNotLazy = listOf(jsUndefinedClassId)
),
listOf(construct(value, jsUndefinedClassId))
)
}
}
}
classId.isJsArray -> {
UtArrayModel(
id = JsIdProvider.createId(),
classId = classId,
stores = buildMap {
putAll(values.indices.zip(values.map {
construct(it, jsUndefinedClassId)
}))
} as MutableMap<Int, UtModel>,
length = values.size,
constModel = jsUndefinedClassId.defaultJsValueModel()
)
}
classId.isJsMap -> {
UtAssembleModel(
id = JsIdProvider.createId(),
classId = classId,
modelName = "",
instantiationCall = UtExecutableCallModel(
null,
ConstructorId(classId, emptyList()),
emptyList()
),
modificationsChainProvider = { mutableListOf() }
).apply {
this.modificationsChain as MutableList += values.map { value ->
UtExecutableCallModel(
this,
JsMethodId(
classId = classId,
name = "set",
returnTypeNotLazy = jsUndefinedClassId,
parametersNotLazy = listOf(jsUndefinedClassId, jsUndefinedClassId)
),
(value as Pair<Any?, Any?>).toList().map { construct(it, jsUndefinedClassId) }
)
}
}
}
else -> throw UnsupportedOperationException(
"Can't make UtModel from JavaScript structure with ${classId.name} type"
)
}
}

@Suppress("UNCHECKED_CAST")
private fun constructObject(classId: JsClassId, value: Any?): UtModel {
val constructor = classId.allConstructors.first()
Expand Down
2 changes: 1 addition & 1 deletion utbot-js/src/main/kotlin/framework/api/js/JsApi.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package framework.api.js

import framework.api.js.util.toJsClassId
import java.lang.reflect.Modifier
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.primitiveModelValueToClassId
import java.lang.reflect.Modifier

open class JsClassId(
private val jsName: String,
Expand Down
8 changes: 4 additions & 4 deletions utbot-js/src/main/kotlin/framework/api/js/util/JsIdUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ fun JsClassId.defaultJsValueModel(): UtModel = when (this) {
}

val JsClassId.isJsBasic: Boolean
get() = this in jsBasic || this is JsMultipleClassId
get() = this in jsBasic || this.isJsStdStructure

val JsClassId.isExportable: Boolean
get() = !(this.isJsBasic || this == jsErrorClassId || this.isJsStdStructure)
get() = !(this.isJsBasic || this == jsErrorClassId || this is JsMultipleClassId)

val JsClassId.isClass: Boolean
get() = !(this.isJsBasic || this == jsErrorClassId || this.isJsStdStructure)
get() = !(this.isJsBasic || this == jsErrorClassId || this is JsMultipleClassId)

val JsClassId.isUndefined: Boolean
get() = this == jsUndefinedClassId

val JsClassId.isJsArray: Boolean
get() = this.name == "array" && this.elementClassId is JsClassId
get() = this.name == "Array"

val JsClassId.isJsMap: Boolean
get() = this.name == "Map"
Expand Down
6 changes: 3 additions & 3 deletions utbot-js/src/main/kotlin/framework/codegen/JsDomain.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package framework.codegen

import org.utbot.framework.plugin.api.BuiltinClassId
import org.utbot.framework.plugin.api.BuiltinMethodId
import org.utbot.framework.plugin.api.ClassId
import framework.api.js.JsClassId
import framework.api.js.util.jsErrorClassId
import framework.api.js.util.jsUndefinedClassId
import org.utbot.framework.codegen.domain.Import
import org.utbot.framework.codegen.domain.TestFramework
import org.utbot.framework.plugin.api.BuiltinClassId
import org.utbot.framework.plugin.api.BuiltinMethodId
import org.utbot.framework.plugin.api.ClassId
import service.PackageJson


Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package framework.codegen.model.constructor.tree

import framework.api.js.JsClassId
import org.utbot.framework.codegen.domain.context.CgContext
import org.utbot.framework.codegen.domain.models.CgTestMethod
import org.utbot.framework.codegen.domain.models.CgTestMethodType
import org.utbot.framework.codegen.domain.models.CgValue
import org.utbot.framework.codegen.domain.models.CgVariable
import org.utbot.framework.codegen.tree.CgMethodConstructor
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtExecution
import framework.api.js.JsClassId
import org.utbot.framework.plugin.api.onFailure
import org.utbot.framework.plugin.api.onSuccess
import org.utbot.framework.plugin.api.util.voidClassId
import org.utbot.framework.util.isUnit
import org.utbot.framework.codegen.domain.context.CgContext
import org.utbot.framework.codegen.domain.models.CgTestMethod
import org.utbot.framework.codegen.domain.models.CgTestMethodType
import org.utbot.framework.codegen.domain.models.CgValue
import org.utbot.framework.codegen.domain.models.CgVariable
import org.utbot.framework.codegen.tree.CgMethodConstructor

class JsCgMethodConstructor(ctx: CgContext) : CgMethodConstructor(ctx) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class JsCgVariableConstructor(ctx: CgContext) : CgVariableConstructor(ctx) {
}

private fun constructArray(arrayModel: UtArrayModel, baseName: String?): CgVariable {
val elementType = arrayModel.classId.elementClassId!! as JsClassId
val elementType = (arrayModel.classId.elementClassId ?: jsUndefinedClassId) as JsClassId
val elementModels = (0 until arrayModel.length).map {
arrayModel.stores.getOrDefault(it, arrayModel.constModel)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import org.utbot.framework.codegen.renderer.CgAbstractRenderer
import org.utbot.framework.codegen.renderer.CgPrinter
import org.utbot.framework.codegen.renderer.CgPrinterImpl
import org.utbot.framework.codegen.renderer.CgRendererContext
import org.utbot.framework.codegen.services.language.isLanguageKeyword
import org.utbot.framework.codegen.tree.VisibilityModifier
import org.utbot.framework.plugin.api.BuiltinMethodId
import org.utbot.framework.plugin.api.ClassId
Expand Down
4 changes: 2 additions & 2 deletions utbot-js/src/main/kotlin/fuzzer/JsFuzzerApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package fuzzer
import framework.api.js.JsClassId
import framework.api.js.JsUtFuzzedExecution
import framework.api.js.util.isClass
import java.util.concurrent.atomic.AtomicInteger
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtTimeoutException
import org.utbot.fuzzing.Description
import org.utbot.fuzzing.Control
import org.utbot.fuzzing.Description
import org.utbot.fuzzing.Feedback
import org.utbot.fuzzing.utils.Trie
import java.util.concurrent.atomic.AtomicInteger

sealed interface JsFuzzingExecutionFeedback
class JsValidExecution(val utFuzzedExecution: JsUtFuzzedExecution) : JsFuzzingExecutionFeedback
Expand Down
3 changes: 1 addition & 2 deletions utbot-js/src/main/kotlin/parser/JsFunctionAstVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package parser

import com.google.javascript.jscomp.NodeUtil
import com.google.javascript.rhino.Node
import java.lang.IllegalStateException
import parser.JsParserUtils.getAbstractFunctionName
import parser.JsParserUtils.getClassName

Expand Down Expand Up @@ -38,4 +37,4 @@ class JsFunctionAstVisitor(
}
}
}
}
}
8 changes: 4 additions & 4 deletions utbot-js/src/main/kotlin/service/InstrumentationService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ package service
import com.google.javascript.jscomp.CodePrinter
import com.google.javascript.jscomp.NodeUtil
import com.google.javascript.rhino.Node
import java.io.File
import java.nio.file.Paths
import org.apache.commons.io.FileUtils
import parser.JsFunctionAstVisitor
import parser.JsParserUtils.getAnyValue
import parser.JsParserUtils.getModuleImportText
import parser.JsParserUtils.getRequireImportText
import parser.JsParserUtils.isRequireImport
import parser.JsParserUtils.runParser
import providers.exports.IExportsProvider
import utils.JsCmdExec
import utils.PathResolver.getRelativePath
import java.io.File
import java.nio.file.Paths
import parser.JsParserUtils.getModuleImportText
import providers.exports.IExportsProvider
import kotlin.io.path.pathString
import kotlin.math.roundToInt

Expand Down
Loading

0 comments on commit a9e2fe8

Please sign in to comment.