diff --git a/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonCliProcessor.kt b/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonCliProcessor.kt index 801686e242..d49b2895a9 100644 --- a/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonCliProcessor.kt +++ b/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonCliProcessor.kt @@ -23,7 +23,7 @@ class PythonCliProcessor( } override fun processCoverageInfo(testSets: List) { - val coverageReport = getCoverageInfo(testSets) + val coverageReport = getStringCoverageInfo(testSets) val output = coverageOutput ?: return writeToFileAndSave(output, coverageReport) } diff --git a/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt b/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt index 03361d44f7..c82cf08bb1 100644 --- a/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt +++ b/utbot-cli-python/src/main/kotlin/org/utbot/cli/language/python/PythonGenerateTestsCommand.kt @@ -62,9 +62,12 @@ class PythonGenerateTestsCommand : CliktCommand( ).required() private val output by option( - "-o", "--output", - help = "(required) File for generated tests." - ).required() + "-o", "--output", help = "(required) File for generated tests." + ) + .required() + .check("Must end with .py suffix") { + it.endsWith(".py") + } private val coverageOutput by option( "--coverage", @@ -230,12 +233,12 @@ class PythonGenerateTestsCommand : CliktCommand( val config = PythonTestGenerationConfig( pythonPath = pythonPath, testFileInformation = TestFileInformation(sourceFile.toAbsolutePath(), sourceFileContent, currentPythonModule.dropInitFile()), - sysPathDirectories = directoriesForSysPath.toSet(), + sysPathDirectories = directoriesForSysPath.map { it.toAbsolutePath() } .toSet(), testedMethods = pythonMethods, timeout = timeout, timeoutForRun = timeoutForRun, testFramework = testFramework, - testSourceRootPath = Paths.get(output).parent.toAbsolutePath(), + testSourceRootPath = Paths.get(output.toAbsolutePath()).parent.toAbsolutePath(), withMinimization = !doNotMinimize, isCanceled = { false }, runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf(runtimeExceptionTestsBehaviour) @@ -243,9 +246,9 @@ class PythonGenerateTestsCommand : CliktCommand( val processor = PythonCliProcessor( config, - output, + output.toAbsolutePath(), logger, - coverageOutput, + coverageOutput?.toAbsolutePath(), ) logger.info("Loading information about Python types...") diff --git a/utbot-python/samples/run_tests.py b/utbot-python/samples/run_tests.py index 7cb0bf4acd..05fd6e0a11 100644 --- a/utbot-python/samples/run_tests.py +++ b/utbot-python/samples/run_tests.py @@ -9,6 +9,7 @@ import argparse import json import os +import shutil import typing import pathlib @@ -16,24 +17,24 @@ def parse_arguments(): parser = argparse.ArgumentParser( prog='UtBot Python test', - description='Generage tests for example files' + description='Generate tests for example files' ) subparsers = parser.add_subparsers(dest="command") parser_generate = subparsers.add_parser('generate', help='Generate tests') - parser_generate.add_argument('java') - parser_generate.add_argument('jar') - parser_generate.add_argument('path_to_test_dir') - parser_generate.add_argument('-c', '--config_file') - parser_generate.add_argument('-p', '--python_path') - parser_generate.add_argument('-o', '--output_dir') - parser_generate.add_argument('-i', '--coverage_output_dir') + parser_generate.add_argument('java', required=True) + parser_generate.add_argument('jar', required=True) + parser_generate.add_argument('path_to_test_dir', required=True) + parser_generate.add_argument('-c', '--config_file', required=True) + parser_generate.add_argument('-p', '--python_path', required=True) + parser_generate.add_argument('-o', '--output_dir', required=True) + parser_generate.add_argument('-i', '--coverage_output_dir', required=True) parser_run = subparsers.add_parser('run', help='Run tests') - parser_run.add_argument('-p', '--python_path') - parser_run.add_argument('-t', '--test_directory') - parser_run.add_argument('-c', '--code_directory') + parser_run.add_argument('-p', '--python_path', required=True) + parser_run.add_argument('-t', '--test_directory', required=True) + parser_run.add_argument('-c', '--code_directory', required=True) parser_coverage = subparsers.add_parser('check_coverage', help='Check coverage') - parser_coverage.add_argument('-i', '--coverage_output_dir') - parser_coverage.add_argument('-c', '--config_file') + parser_coverage.add_argument('-i', '--coverage_output_dir', required=True) + parser_coverage.add_argument('-c', '--config_file', required=True) return parser.parse_args() @@ -61,7 +62,6 @@ def generate_tests( command += f" -m {','.join(method_names)}" print(command) code = os.system(command) - print(code) return code @@ -89,12 +89,15 @@ def check_coverage( expected_coverage = group.get('coverage', 0) file_suffix = f"{part['path'].replace('/', '_')}_{file['name']}" - coverage_output_file = pathlib.PurePath(coverage_output_dir, f"coverage_{file_suffix}.json") - with open(coverage_output_file, "rt") as fin: - actual_coverage_json = json.loads(fin.readline()) - actual_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['covered']) - actual_not_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['notCovered']) - actual_coverage = round(actual_covered / (actual_not_covered + actual_covered) * 100) + coverage_output_file = pathlib.Path(coverage_output_dir, f"coverage_{file_suffix}.json") + if coverage_output_file.exists(): + with open(coverage_output_file, "rt") as fin: + actual_coverage_json = json.loads(fin.readline()) + actual_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['covered']) + actual_not_covered = sum(lines['end'] - lines['start'] + 1 for lines in actual_coverage_json['notCovered']) + actual_coverage = round(actual_covered / (actual_not_covered + actual_covered) * 100) + else: + actual_coverage = 0 coverage[file_suffix] = (actual_coverage, expected_coverage) report[file_suffix] = actual_coverage >= expected_coverage @@ -111,6 +114,7 @@ def check_coverage( def main_test_generation(args): config = parse_config(args.config_file) + shutil.rmtree(args.coverage_output_dir) for part in config['parts']: for file in part['files']: for group in file['groups']: diff --git a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt index fd7df3a128..23795404bd 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/PythonTestGenerationProcessor.kt @@ -269,7 +269,7 @@ abstract class PythonTestGenerationProcessor { else acc + listOf(InstructionSet(lineNumber, lineNumber)) } - protected fun getCoverageInfo(testSets: List): String { + protected fun getCoverageInfo(testSets: List): CoverageInfo { val covered = mutableSetOf() val missed = mutableSetOf>() testSets.forEach { testSet -> @@ -286,11 +286,15 @@ abstract class PythonTestGenerationProcessor { else getInstructionSetList(missed.reduce { a, b -> a intersect b }) + return CoverageInfo( + coveredInstructionSets, + missedInstructionSets + ) + } + + protected fun getStringCoverageInfo(testSets: List): String { return jsonAdapter.toJson( - CoverageInfo( - coveredInstructionSets, - missedInstructionSets - ) + getCoverageInfo(testSets) ) } diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt index e396b48586..97ac0a8dbb 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt @@ -91,6 +91,7 @@ class PythonCodeGenerator( val printer = CgPrinterImpl() val renderer = CgPythonRenderer(cgRendererContext, printer) + val importOs = PythonSystemImport("os") val importSys = PythonSystemImport("sys") val importTyping = PythonSystemImport("typing") val importSysPaths = directoriesForSysPath.map { PythonSysPathImport(it) } @@ -100,7 +101,7 @@ class PythonCodeGenerator( val additionalModules = methodAnnotations.values.flatMap { it.pythonModules() }.map { PythonUserImport(it) } val imports = - (listOf(importSys, importTyping) + importSysPaths + (importsFromModule + additionalModules)).toSet() + (listOf(importOs, importSys, importTyping) + importSysPaths + (importsFromModule + additionalModules)).toSet() .toList() imports.forEach { renderer.renderPythonImport(it) }