Skip to content

Commit

Permalink
Merge branch 'main' into alisevych/ui_tests_correction
Browse files Browse the repository at this point in the history
  • Loading branch information
Vassiliy-Kudryashov authored Aug 28, 2023
2 parents fd5429d + b1075f6 commit 2bcf750
Show file tree
Hide file tree
Showing 49 changed files with 888 additions and 484 deletions.
23 changes: 17 additions & 6 deletions utbot-core/src/main/kotlin/org/utbot/common/JarUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,29 @@ import java.io.File
import java.net.URL
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.*

object JarUtils {
private const val UNKNOWN_MODIFICATION_TIME = 0L

fun extractJarFileFromResources(jarFileName: String, jarResourcePath: String, targetDirectoryName: String): File {
val targetDirectory =
Files.createDirectories(utBotTempDirectory.toFile().resolve(targetDirectoryName).toPath()).toFile()
return targetDirectory.resolve(jarFileName).also { jarFile ->
val resource = this::class.java.classLoader.getResource(jarResourcePath)
?: error("Unable to find \"$jarResourcePath\" in resources, make sure it's on the classpath")
updateJarIfRequired(jarFile, resource)
val resource = this::class.java.classLoader.getResource(jarResourcePath)
?: error("Unable to find \"$jarResourcePath\" in resources, make sure it's on the classpath")

val targetDirectory = utBotTempDirectory.toFile().resolve(targetDirectoryName).toPath()
fun extractToSubDir(subDir: String) =
Files.createDirectories(targetDirectory.resolve(subDir)).toFile().resolve(jarFileName).also { jarFile ->
updateJarIfRequired(jarFile, resource)
}

// We attempt to always extract jars to same locations, to avoid eating up drive space with
// every UtBot launch, but we may fail to do so if multiple processes are running in parallel.
repeat(10) { i ->
runCatching {
return extractToSubDir(i.toString())
}
}
return extractToSubDir(UUID.randomUUID().toString())
}

private fun updateJarIfRequired(jarFile: File, resource: URL) {
Expand Down
17 changes: 12 additions & 5 deletions utbot-core/src/main/kotlin/org/utbot/common/StopWatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,19 @@ class StopWatch {
startTime = System.currentTimeMillis()
}
}

fun stop() {

/**
* @param compensationMillis the duration in millis that should be subtracted from [elapsedMillis] to compensate
* for stopping and restarting [StopWatch] taking some time, can also be used to compensate for some activities,
* that are hard to directly detect (e.g. class loading).
*
* NOTE: [compensationMillis] will never cause [elapsedMillis] become negative.
*/
fun stop(compensationMillis: Long = 0) {
lock.withLockInterruptibly {
startTime?.let {
elapsedMillis += (System.currentTimeMillis() - it)
startTime = null
startTime?.let { startTime ->
elapsedMillis += ((System.currentTimeMillis() - startTime) - compensationMillis).coerceAtLeast(0)
this.startTime = null
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
*/
var fuzzingImplementationOfAbstractClasses: Boolean by getBooleanProperty(true)

/**
* Use methods to mutate fields of classes different from class under test or not.
*/
var tryMutateOtherClassesFieldsWithMethods: Boolean by getBooleanProperty(false)

/**
* Generate tests that treat possible overflows in arithmetic operations as errors
* that throw Arithmetic Exception.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ import java.io.File
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import org.utbot.common.isAbstract
import org.utbot.framework.plugin.api.mapper.UtModelMapper
import org.utbot.framework.plugin.api.mapper.map
import org.utbot.framework.plugin.api.mapper.mapPreservingType
import org.utbot.framework.plugin.api.util.SpringModelUtils
import org.utbot.framework.process.OpenModulesContainer
import soot.SootMethod
Expand Down Expand Up @@ -499,7 +502,11 @@ data class UtClassRefModel(
* - calculated field values (models)
* - mocks for methods with return values
* - [canHaveRedundantOrMissingMocks] flag, which is set to `true` for mocks
* created by fuzzer without knowing which methods will actually be called
* created by:
* - fuzzer which doesn't know which methods will actually be called
* - engine which also doesn't know which methods will actually be
* called during concrete execution that may be only **partially**
* backed up by the symbolic analysis
*
* [fields] contains non-static fields
*/
Expand All @@ -509,7 +516,7 @@ data class UtCompositeModel(
val isMock: Boolean,
val fields: MutableMap<FieldId, UtModel> = mutableMapOf(),
val mocks: MutableMap<ExecutableId, List<UtModel>> = mutableMapOf(),
val canHaveRedundantOrMissingMocks: Boolean = false,
val canHaveRedundantOrMissingMocks: Boolean = true,
) : UtReferenceModel(id, classId) {
//TODO: SAT-891 - rewrite toString() method
override fun toString() = withToStringThreadLocalReentrancyGuard {
Expand Down Expand Up @@ -767,15 +774,15 @@ abstract class UtCustomModel(
modelName: String = id.toString(),
override val origin: UtCompositeModel? = null,
) : UtModelWithCompositeOrigin(id, classId, modelName, origin) {
abstract val dependencies: Collection<UtModel>
abstract fun shallowMap(mapper: UtModelMapper): UtCustomModel
}

object UtSpringContextModel : UtCustomModel(
id = null,
classId = SpringModelUtils.applicationContextClassId,
modelName = "applicationContext"
) {
override val dependencies: Collection<UtModel> get() = emptySet()
override fun shallowMap(mapper: UtModelMapper) = this

// NOTE that overriding equals is required just because without it
// we will lose equality for objects after deserialization
Expand All @@ -789,7 +796,7 @@ class UtSpringEntityManagerModel : UtCustomModel(
classId = SpringModelUtils.entityManagerClassIds.first(),
modelName = "entityManager"
) {
override val dependencies: Collection<UtModel> get() = emptySet()
override fun shallowMap(mapper: UtModelMapper) = this

// NOTE that overriding equals is required just because without it
// we will lose equality for objects after deserialization
Expand Down Expand Up @@ -820,7 +827,10 @@ data class UtSpringMockMvcResultActionsModel(
id = id,
modelName = "mockMvcResultActions@$id"
) {
override val dependencies: Collection<UtModel> get() = emptySet()
override fun shallowMap(mapper: UtModelMapper) = copy(
origin = origin?.mapPreservingType<UtCompositeModel>(mapper),
model = model?.map(mapper)
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package org.utbot.framework.plugin.api.mapper

import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtClassRefModel
import org.utbot.framework.plugin.api.UtCompositeModel
import org.utbot.framework.plugin.api.UtCustomModel
import org.utbot.framework.plugin.api.UtEnumConstantModel
import org.utbot.framework.plugin.api.UtLambdaModel
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.UtVoidModel

/**
* Performs deep mapping of [UtModel]s.
*
* NOTE:
* - [shallowMapper] is invoked on models **before** mapping their sub models.
* - [shallowMapper] is responsible for caching own results (it may be called repeatedly on same models).
*/
class UtModelDeepMapper private constructor(
private val shallowMapper: UtModelMapper
) : UtModelMapper {
constructor(shallowMapper: (UtModel) -> UtModel) : this(UtModelSafeCastingCachingShallowMapper(shallowMapper))

/**
* Keys are models that have been shallowly mapped by [shallowMapper].
* Values are models that have been deeply mapped by this [UtModelDeepMapper].
* Models are only associated with models of the same type (i.e. the cache type is actually `MutableMap<T, T>`)
*/
private val cache = mutableMapOf<UtModel, UtModel>()

private val allInputtedModels get() = cache.keys
private val allOutputtedModels get() = cache.values

override fun <T : UtModel> map(model: T, clazz: Class<T>): T =
clazz.cast(mapNestedModels(shallowMapper.map(model, clazz)))

/**
* Maps models contained inside [model], but not the [model] itself.
*/
private fun mapNestedModels(model: UtModel): UtModel = cache.getOrPut(model) {
when (model) {
is UtNullModel,
is UtPrimitiveModel,
is UtEnumConstantModel,
is UtClassRefModel,
is UtVoidModel -> model
is UtArrayModel -> mapNestedModels(model)
is UtCompositeModel -> mapNestedModels(model)
is UtLambdaModel -> mapNestedModels(model)
is UtAssembleModel -> mapNestedModels(model)
is UtCustomModel -> mapNestedModels(model)

// PythonModel, JsUtModel may be here
else -> throw UnsupportedOperationException("UtModel $this cannot be mapped")
}
}

private fun mapNestedModels(model: UtArrayModel): UtReferenceModel {
val mappedModel = UtArrayModel(
id = model.id,
classId = model.classId,
length = model.length,
constModel = model.constModel,
stores = model.stores,
)
cache[model] = mappedModel

mappedModel.constModel = model.constModel.map(this)
mappedModel.stores.putAll(model.stores.mapModelValues(this))

return mappedModel
}

private fun mapNestedModels(model: UtCompositeModel): UtCompositeModel {
val mappedModel = UtCompositeModel(
id = model.id,
classId = model.classId,
isMock = model.isMock,
)
cache[model] = mappedModel

mappedModel.fields.putAll(model.fields.mapModelValues(this))
mappedModel.mocks.putAll(model.mocks.mapValuesTo(mutableMapOf()) { it.value.mapModels(this@UtModelDeepMapper) })

return mappedModel
}

private fun mapNestedModels(model: UtLambdaModel): UtReferenceModel = UtLambdaModel(
id = model.id,
samType = model.samType,
declaringClass = model.declaringClass,
lambdaName = model.lambdaName,
capturedValues = model.capturedValues.mapModels(this@UtModelDeepMapper).toMutableList()
)

private fun mapNestedModels(model: UtAssembleModel): UtReferenceModel = UtAssembleModel(
id = model.id,
classId = model.classId,
modelName = model.modelName,
instantiationCall = model.instantiationCall.mapModels(this),
modificationsChainProvider = {
cache[model] = this@UtAssembleModel
model.modificationsChain.map { it.mapModels(this@UtModelDeepMapper) }
},
origin = model.origin?.mapPreservingType<UtCompositeModel>(this)
)

private fun mapNestedModels(model: UtCustomModel): UtReferenceModel =
model.shallowMap(this)

companion object {
/**
* Creates identity deep mapper, runs [block] on it, and returns the set of all models that
* were mapped (i.e. deeply collects all models reachable from models passed to `collector`).
*/
fun collectAllModels(block: (collector: UtModelDeepMapper) -> Unit): Set<UtModel> =
UtModelDeepMapper(UtModelNoopMapper).also(block).allInputtedModels
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.utbot.framework.plugin.api.mapper

import org.utbot.framework.plugin.api.UtCompositeModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin

interface UtModelMapper {
/**
* Performs depending on the implementation deep or shallow mapping of the [model].
*
* In some cases (e.g. when mapping [UtModelWithCompositeOrigin.origin]) you may want to get result
* of some specific type (e.g. [UtCompositeModel]), only then you should specify specific value for [clazz].
*
* NOTE: if you are fine with result model and [model] having different types, then you should
* use `UtModel::class.java` as a value for [clazz] or just use [UtModel.map].
*/
fun <T : UtModel> map(model: T, clazz: Class<T>): T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.utbot.framework.plugin.api.mapper

import org.utbot.framework.plugin.api.UtModel

object UtModelNoopMapper : UtModelMapper {
override fun <T : UtModel> map(model: T, clazz: Class<T>): T = model
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.utbot.framework.plugin.api.mapper

import org.utbot.framework.plugin.api.UtModel

class UtModelSafeCastingCachingShallowMapper(
val mapper: (UtModel) -> UtModel
) : UtModelMapper {
private val cache = mutableMapOf<UtModel, UtModel>()

override fun <T : UtModel> map(model: T, clazz: Class<T>): T {
val mapped = cache.getOrPut(model) { mapper(model) }
return if (clazz.isInstance(mapped)) clazz.cast(mapped)
else model
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.utbot.framework.plugin.api.mapper

import org.utbot.framework.plugin.api.EnvironmentModels
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtExecution
import org.utbot.framework.plugin.api.UtInstrumentation
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
import org.utbot.framework.plugin.api.UtReferenceModel
import org.utbot.framework.plugin.api.UtStatementCallModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation

inline fun <reified T : UtModel> T.mapPreservingType(mapper: UtModelMapper): T =
mapper.map(this, T::class.java)

fun UtModel.map(mapper: UtModelMapper) = mapPreservingType<UtModel>(mapper)

fun List<UtModel>.mapModels(mapper: UtModelMapper): List<UtModel> =
map { model -> model.map(mapper) }

fun <K> Map<K, UtModel>.mapModelValues(mapper: UtModelMapper): Map<K, UtModel> =
mapValues { (_, model) -> model.map(mapper) }

fun UtStatementModel.mapModels(mapper: UtModelMapper): UtStatementModel =
when(this) {
is UtStatementCallModel -> mapModels(mapper)
is UtDirectSetFieldModel -> UtDirectSetFieldModel(
instance = instance.mapPreservingType<UtReferenceModel>(mapper),
fieldId = fieldId,
fieldModel = fieldModel.map(mapper)
)
}

fun UtStatementCallModel.mapModels(mapper: UtModelMapper): UtStatementCallModel =
when(this) {
is UtDirectGetFieldModel -> UtDirectGetFieldModel(
instance = instance.mapPreservingType<UtReferenceModel>(mapper),
fieldAccess = fieldAccess,
)
is UtExecutableCallModel -> UtExecutableCallModel(
instance = instance?.mapPreservingType<UtReferenceModel>(mapper),
executable = executable,
params = params.mapModels(mapper)
)
}

fun EnvironmentModels.mapModels(mapper: UtModelMapper) = EnvironmentModels(
thisInstance = thisInstance?.map(mapper),
statics = statics.mapModelValues(mapper),
parameters = parameters.mapModels(mapper),
executableToCall = executableToCall,
)

fun UtInstrumentation.mapModels(mapper: UtModelMapper) = when (this) {
is UtNewInstanceInstrumentation -> copy(instances = instances.mapModels(mapper))
is UtStaticMethodInstrumentation -> copy(values = values.mapModels(mapper))
}

fun UtExecution.mapStateBeforeModels(mapper: UtModelMapper) = copy(
stateBefore = stateBefore.mapModels(mapper)
)
Loading

0 comments on commit 2bcf750

Please sign in to comment.