Skip to content

Commit

Permalink
Include utbot-executor and fix bugs (#2548)
Browse files Browse the repository at this point in the history
  • Loading branch information
tamarinvs19 authored Aug 31, 2023
1 parent d8d5197 commit b775394
Show file tree
Hide file tree
Showing 53 changed files with 2,629 additions and 49 deletions.
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ if (pythonIde.split(",").contains(ideType)) {
include("utbot-intellij-python")
include("utbot-python-parser")
include("utbot-python-types")
include("utbot-python-executor")
}

include("utbot-spring-sample")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class PythonGenerateTestsCommand : CliktCommand(
help = "File with Python code to generate tests for."
)

private fun absSourceFile() = sourceFile.toAbsolutePath()

private val pythonClass by option(
"-c", "--class",
help = "Specify top-level (ordinary, not nested) class under test. " +
Expand Down Expand Up @@ -132,7 +134,7 @@ class PythonGenerateTestsCommand : CliktCommand(
Success(
topLevelFunctions
.mapNotNull { parseFunctionDefinition(it) }
.map { PythonMethodHeader(it.name.toString(), sourceFile, null) }
.map { PythonMethodHeader(it.name.toString(), absSourceFile(), null) }
)
else {
val topLevelClassMethods = topLevelClasses
Expand All @@ -142,7 +144,7 @@ class PythonGenerateTestsCommand : CliktCommand(
.mapNotNull { parseFunctionDefinition(it) }
.map { function ->
val parsedClassName = PythonClassId(cls.name.toString())
PythonMethodHeader(function.name.toString(), sourceFile, parsedClassName)
PythonMethodHeader(function.name.toString(), absSourceFile(), parsedClassName)
}
}
if (topLevelClassMethods.isNotEmpty()) {
Expand All @@ -154,7 +156,7 @@ class PythonGenerateTestsCommand : CliktCommand(
val pythonMethodsOpt = selectedMethods.map { functionName ->
topLevelFunctions
.mapNotNull { parseFunctionDefinition(it) }
.map { PythonMethodHeader(it.name.toString(), sourceFile, null) }
.map { PythonMethodHeader(it.name.toString(), absSourceFile(), null) }
.find { it.name == functionName }
?.let { Success(it) }
?: Fail("Couldn't find top-level function $functionName in the source file.")
Expand All @@ -174,7 +176,7 @@ class PythonGenerateTestsCommand : CliktCommand(
val fineMethods = methods
.filter { !forbiddenMethods.contains(it.name.toString()) }
.map {
PythonMethodHeader(it.name.toString(), sourceFile, parsedClassId)
PythonMethodHeader(it.name.toString(), absSourceFile(), parsedClassId)
}
if (fineMethods.isNotEmpty())
Success(fineMethods)
Expand All @@ -201,8 +203,8 @@ class PythonGenerateTestsCommand : CliktCommand(

@Suppress("UNCHECKED_CAST")
private fun calculateValues(): Optional<Unit> {
val currentPythonModuleOpt = findCurrentPythonModule(directoriesForSysPath, sourceFile)
sourceFileContent = File(sourceFile).readText()
val currentPythonModuleOpt = findCurrentPythonModule(directoriesForSysPath, absSourceFile())
sourceFileContent = File(absSourceFile()).readText()
val pythonMethodsOpt = bind(currentPythonModuleOpt) { getPythonMethods() }

return bind(pack(currentPythonModuleOpt, pythonMethodsOpt)) {
Expand Down Expand Up @@ -232,7 +234,7 @@ class PythonGenerateTestsCommand : CliktCommand(

val config = PythonTestGenerationConfig(
pythonPath = pythonPath,
testFileInformation = TestFileInformation(sourceFile.toAbsolutePath(), sourceFileContent, currentPythonModule.dropInitFile()),
testFileInformation = TestFileInformation(absSourceFile(), sourceFileContent, currentPythonModule.dropInitFile()),
sysPathDirectories = directoriesForSysPath.map { it.toAbsolutePath() } .toSet(),
testedMethods = pythonMethods,
timeout = timeout,
Expand Down
26 changes: 26 additions & 0 deletions utbot-python-executor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
dist/
__pycache__/
build/
develop-eggs/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.pytest_cache/
poetry.lock
.env/
.venv/
env/
venv/
.mypy_cache/
.dmypy.json
dmypy.json
109 changes: 109 additions & 0 deletions utbot-python-executor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
val kotlinLoggingVersion: String? by rootProject

val utbotExecutorVersion = File(project.projectDir, "src/main/resources/utbot_executor_version").readText()
// these two properties --- from GRADLE_USER_HOME/gradle.properties
val pypiToken: String? by project
val pythonInterpreter: String? by project
val utbotExecutorPath = File(project.projectDir, "src/main/python/utbot_executor")
val localUtbotExecutorPath = File(utbotExecutorPath, "dist")

tasks.register("cleanDist") {
group = "python"
delete(localUtbotExecutorPath.canonicalPath)
}

val installPoetry =
if (pythonInterpreter != null) {
tasks.register<Exec>("installPoetry") {
group = "python"
workingDir = utbotExecutorPath
commandLine(pythonInterpreter, "-m", "pip", "install", "poetry")
}
} else {
null
}

val setExecutorVersion =
if (pythonInterpreter != null) {
tasks.register<Exec>("setVersion") {
dependsOn(installPoetry!!)
group = "python"
workingDir = utbotExecutorPath
commandLine(pythonInterpreter, "-m", "poetry", "version", utbotExecutorVersion)
}
} else {
null
}

val buildExecutor =
if (pythonInterpreter != null) {
tasks.register<Exec>("buildUtbotExecutor") {
dependsOn(setExecutorVersion!!)
group = "python"
workingDir = utbotExecutorPath
commandLine(pythonInterpreter, "-m", "poetry", "build")
}
} else {
null
}

if (pythonInterpreter != null && pypiToken != null) {
tasks.register<Exec>("publishUtbotExecutor") {
dependsOn(buildExecutor!!)
group = "python"
workingDir = utbotExecutorPath
commandLine(
pythonInterpreter,
"-m",
"poetry",
"publish",
"-u",
"__token__",
"-p",
pypiToken
)
}
}

val installExecutor =
if (pythonInterpreter != null) {
tasks.register<Exec>("installUtbotExecutor") {
dependsOn(buildExecutor!!)
group = "python"
environment("PIP_FIND_LINKS" to localUtbotExecutorPath.canonicalPath)
commandLine(
pythonInterpreter,
"-m",
"pip",
"install",
"utbot_executor==$utbotExecutorVersion"
)
}
} else {
null
}

val installPytest =
if (pythonInterpreter != null) {
tasks.register<Exec>("installPytest") {
group = "pytest"
workingDir = utbotExecutorPath
commandLine(pythonInterpreter, "-m", "pip", "install", "pytest")
}
} else {
null
}

if (pythonInterpreter != null) {
tasks.register<Exec>("runTests") {
dependsOn(installExecutor!!)
dependsOn(installPytest!!)
group = "pytest"
workingDir = utbotExecutorPath
commandLine(
pythonInterpreter,
"-m",
"pytest",
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.utbot.python

class UtbotExecutor {
}
127 changes: 127 additions & 0 deletions utbot-python-executor/src/main/python/utbot_executor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# UtBot Executor

Util for python code execution and state serialization.

## Installation

You can install module from [PyPI](https://pypi.org/project/utbot-executor/):

```bash
python -m pip install utbot-executor
```

## Usage

### From console with socket listener

Run with your `<hostname>` and `<port>` for socket connection
```bash
$ python -m utbot_executor <hostname> <port> <logfile> [<loglevel DEBUG | INFO | ERROR>] <coverage_hostname> <coverage_port>
```

### Request format
```json
{
"functionName": "f",
"functionModule": "my_module.submod1",
"imports": ["sys", "math", "json"],
"syspaths": ["/home/user/my_project/"],
"argumentsIds": ["1", "2"],
"kwargumentsIds": ["4", "5"],
"serializedMemory": "string",
"filepath": ["/home/user/my_project/my_module/submod1.py"],
"coverageId": "1"
}
```

* `functionName` - name of the tested function
* `functionModule` - name of the module of the tested function
* `imports` - all modules which need to run function with current arguments
* `syspaths` - all syspaths which need to import modules (usually it is a project root)
* `argumentsIds` - list of argument's ids
* `kwargumentsIds` - list of keyword argument's ids
* `serializedMemory` - serialized memory throw `deep_serialization` algorithm
* `filepath` - path to the tested function's containing file
* `coverageId` - special id witch will be used for sending information about covered lines

### Response format:

If execution is successful:
```json
{
"status": "success",
"isException": false,
"statements": [1, 2, 3],
"missedStatements": [4, 5],
"stateInit": "string",
"stateBefore": "string",
"stateAfter": "string",
"diffIds": ["3", "4"],
"argsIds": ["1", "2", "3"],
"kwargs": ["4", "5", "6"],
"resultId": "7"
}
```

* `status` - always "success"
* `isException` - boolean value, if it is `true`, execution ended with an exception
* `statements` - list of the numbers of covered rows
* `missedStatements` - list of numbers of uncovered rows
* `stateInit` - serialized states from request
* `stateBefore` - serialized states of arguments before execution
* `stateAfter` - serialized states of arguments after execution
* `diffIds` - ids of the objects which have been changed
* `argsIds` - ids of the function's arguments
* `kwargsIds` - ids of the function's keyword arguments
* `resultId` - id of the returned value

or error format if there was exception in running algorith:

```json
{
"status": "fail",
"exception": "stacktrace"
}
```
* `status` - always "fail"
* `exception` - string representation of the exception stack trace

### Submodule `deep_serialization`

JSON serializer and deserializer for python objects

#### States memory json-format

```json
{
"objects": {
"id": {
"id": "1",
"strategy": "strategy name",
"typeinfo": {
"module": "builtins",
"kind": "int"
},
"comparable": true,

// iff strategy is 'repr'
"value": "1",

// iff strategy is 'list' or 'dict'
"items": ["3", "2"],

// iff strategy = 'reduce'
"constructor": "mymod.A.__new__",
"args": ["mymod.A"],
"state": {"a": "4", "b": "5"},
"listitems": ["7", "8"],
"dictitems": {"ka": "10"}
}
}
}
```


## Source

GitHub [repository](https://github.com/tamarinvs19/utbot_executor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[tool.poetry]
name = "utbot-executor"
version = "1.4.41"
description = ""
authors = ["Vyacheslav Tamarin <[email protected]>"]
readme = "README.md"
packages = [{include = "utbot_executor"}]

[tool.poetry.dependencies]
python = "^3.8"

[tool.poetry.dev-dependencies]
pytest = "^7.3"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
utbot-executor = "utbot_executor:utbot_executor"

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
python_files = test_*.py *_test.py *_tests.py
Loading

0 comments on commit b775394

Please sign in to comment.