Skip to content

Commit 9ab079f

Browse files
authored
Python fix bugs (#2492)
1 parent 92b9f75 commit 9ab079f

File tree

5 files changed

+46
-34
lines changed

5 files changed

+46
-34
lines changed

utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonCliProcessor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class PythonCliProcessor(
2323
}
2424

2525
override fun processCoverageInfo(testSets: List<PythonTestSet>) {
26-
val coverageReport = getCoverageInfo(testSets)
26+
val coverageReport = getStringCoverageInfo(testSets)
2727
val output = coverageOutput ?: return
2828
writeToFileAndSave(output, coverageReport)
2929
}

utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,12 @@ class PythonGenerateTestsCommand : CliktCommand(
6262
).required()
6363

6464
private val output by option(
65-
"-o", "--output",
66-
help = "(required) File for generated tests."
67-
).required()
65+
"-o", "--output", help = "(required) File for generated tests."
66+
)
67+
.required()
68+
.check("Must end with .py suffix") {
69+
it.endsWith(".py")
70+
}
6871

6972
private val coverageOutput by option(
7073
"--coverage",
@@ -230,22 +233,22 @@ class PythonGenerateTestsCommand : CliktCommand(
230233
val config = PythonTestGenerationConfig(
231234
pythonPath = pythonPath,
232235
testFileInformation = TestFileInformation(sourceFile.toAbsolutePath(), sourceFileContent, currentPythonModule.dropInitFile()),
233-
sysPathDirectories = directoriesForSysPath.toSet(),
236+
sysPathDirectories = directoriesForSysPath.map { it.toAbsolutePath() } .toSet(),
234237
testedMethods = pythonMethods,
235238
timeout = timeout,
236239
timeoutForRun = timeoutForRun,
237240
testFramework = testFramework,
238-
testSourceRootPath = Paths.get(output).parent.toAbsolutePath(),
241+
testSourceRootPath = Paths.get(output.toAbsolutePath()).parent.toAbsolutePath(),
239242
withMinimization = !doNotMinimize,
240243
isCanceled = { false },
241244
runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf(runtimeExceptionTestsBehaviour)
242245
)
243246

244247
val processor = PythonCliProcessor(
245248
config,
246-
output,
249+
output.toAbsolutePath(),
247250
logger,
248-
coverageOutput,
251+
coverageOutput?.toAbsolutePath(),
249252
)
250253

251254
logger.info("Loading information about Python types...")

utbot-python/samples/run_tests.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,32 @@
99
import argparse
1010
import json
1111
import os
12+
import shutil
1213
import typing
1314
import pathlib
1415

1516

1617
def parse_arguments():
1718
parser = argparse.ArgumentParser(
1819
prog='UtBot Python test',
19-
description='Generage tests for example files'
20+
description='Generate tests for example files'
2021
)
2122
subparsers = parser.add_subparsers(dest="command")
2223
parser_generate = subparsers.add_parser('generate', help='Generate tests')
23-
parser_generate.add_argument('java')
24-
parser_generate.add_argument('jar')
25-
parser_generate.add_argument('path_to_test_dir')
26-
parser_generate.add_argument('-c', '--config_file')
27-
parser_generate.add_argument('-p', '--python_path')
28-
parser_generate.add_argument('-o', '--output_dir')
29-
parser_generate.add_argument('-i', '--coverage_output_dir')
24+
parser_generate.add_argument('java', required=True)
25+
parser_generate.add_argument('jar', required=True)
26+
parser_generate.add_argument('path_to_test_dir', required=True)
27+
parser_generate.add_argument('-c', '--config_file', required=True)
28+
parser_generate.add_argument('-p', '--python_path', required=True)
29+
parser_generate.add_argument('-o', '--output_dir', required=True)
30+
parser_generate.add_argument('-i', '--coverage_output_dir', required=True)
3031
parser_run = subparsers.add_parser('run', help='Run tests')
31-
parser_run.add_argument('-p', '--python_path')
32-
parser_run.add_argument('-t', '--test_directory')
33-
parser_run.add_argument('-c', '--code_directory')
32+
parser_run.add_argument('-p', '--python_path', required=True)
33+
parser_run.add_argument('-t', '--test_directory', required=True)
34+
parser_run.add_argument('-c', '--code_directory', required=True)
3435
parser_coverage = subparsers.add_parser('check_coverage', help='Check coverage')
35-
parser_coverage.add_argument('-i', '--coverage_output_dir')
36-
parser_coverage.add_argument('-c', '--config_file')
36+
parser_coverage.add_argument('-i', '--coverage_output_dir', required=True)
37+
parser_coverage.add_argument('-c', '--config_file', required=True)
3738
return parser.parse_args()
3839

3940

@@ -61,7 +62,6 @@ def generate_tests(
6162
command += f" -m {','.join(method_names)}"
6263
print(command)
6364
code = os.system(command)
64-
print(code)
6565
return code
6666

6767

@@ -89,12 +89,15 @@ def check_coverage(
8989
expected_coverage = group.get('coverage', 0)
9090

9191
file_suffix = f"{part['path'].replace('/', '_')}_{file['name']}"
92-
coverage_output_file = pathlib.PurePath(coverage_output_dir, f"coverage_{file_suffix}.json")
93-
with open(coverage_output_file, "rt") as fin:
94-
actual_coverage_json = json.loads(fin.readline())
95-
actual_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['covered'])
96-
actual_not_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['notCovered'])
97-
actual_coverage = round(actual_covered / (actual_not_covered + actual_covered) * 100)
92+
coverage_output_file = pathlib.Path(coverage_output_dir, f"coverage_{file_suffix}.json")
93+
if coverage_output_file.exists():
94+
with open(coverage_output_file, "rt") as fin:
95+
actual_coverage_json = json.loads(fin.readline())
96+
actual_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['covered'])
97+
actual_not_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['notCovered'])
98+
actual_coverage = round(actual_covered / (actual_not_covered + actual_covered) * 100)
99+
else:
100+
actual_coverage = 0
98101

99102
coverage[file_suffix] = (actual_coverage, expected_coverage)
100103
report[file_suffix] = actual_coverage >= expected_coverage
@@ -111,6 +114,7 @@ def check_coverage(
111114

112115
def main_test_generation(args):
113116
config = parse_config(args.config_file)
117+
shutil.rmtree(args.coverage_output_dir)
114118
for part in config['parts']:
115119
for file in part['files']:
116120
for group in file['groups']:

utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ abstract class PythonTestGenerationProcessor {
269269
else
270270
acc + listOf(InstructionSet(lineNumber, lineNumber))
271271
}
272-
protected fun getCoverageInfo(testSets: List<PythonTestSet>): String {
272+
protected fun getCoverageInfo(testSets: List<PythonTestSet>): CoverageInfo {
273273
val covered = mutableSetOf<Int>()
274274
val missed = mutableSetOf<Set<Int>>()
275275
testSets.forEach { testSet ->
@@ -286,11 +286,15 @@ abstract class PythonTestGenerationProcessor {
286286
else
287287
getInstructionSetList(missed.reduce { a, b -> a intersect b })
288288

289+
return CoverageInfo(
290+
coveredInstructionSets,
291+
missedInstructionSets
292+
)
293+
}
294+
295+
protected fun getStringCoverageInfo(testSets: List<PythonTestSet>): String {
289296
return jsonAdapter.toJson(
290-
CoverageInfo(
291-
coveredInstructionSets,
292-
missedInstructionSets
293-
)
297+
getCoverageInfo(testSets)
294298
)
295299
}
296300

utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class PythonCodeGenerator(
9191
val printer = CgPrinterImpl()
9292
val renderer = CgPythonRenderer(cgRendererContext, printer)
9393

94+
val importOs = PythonSystemImport("os")
9495
val importSys = PythonSystemImport("sys")
9596
val importTyping = PythonSystemImport("typing")
9697
val importSysPaths = directoriesForSysPath.map { PythonSysPathImport(it) }
@@ -100,7 +101,7 @@ class PythonCodeGenerator(
100101

101102
val additionalModules = methodAnnotations.values.flatMap { it.pythonModules() }.map { PythonUserImport(it) }
102103
val imports =
103-
(listOf(importSys, importTyping) + importSysPaths + (importsFromModule + additionalModules)).toSet()
104+
(listOf(importOs, importSys, importTyping) + importSysPaths + (importsFromModule + additionalModules)).toSet()
104105
.toList()
105106

106107
imports.forEach { renderer.renderPythonImport(it) }

0 commit comments

Comments
 (0)