Skip to content

Commit 83d8d4d

Browse files
committed
Add competition template
1 parent b65597f commit 83d8d4d

File tree

14 files changed

+1667
-2
lines changed

14 files changed

+1667
-2
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
/results/

README.md

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,41 @@
1-
# UTBotPythonSBFT2024
2-
UTBot-Python-SBFT-Competitions-2024
1+
# Python Tool Competition Implementation Using UTBotPython
2+
3+
Uses the python-tool-competition-2024 to generate tests using the UTBotPython.
4+
5+
For more information see
6+
<https://github.com/ThunderKey/python-tool-competition-2024/>.
7+
8+
## Requirements
9+
10+
* Python 3.11, implementation: CPython
11+
* poetry
12+
* OS: Linux
13+
* Java 17, accessible in command line under name `java`
14+
* glibc>=2.31
15+
16+
## Installation
17+
18+
* Install [poetry](https://python-poetry.org/)
19+
* Run `poetry install`
20+
21+
### Check the installation success
22+
* Run `poetry check`, this command runs simple test generation and shows error messages
23+
* Successful logger messages should end with the following lines:
24+
```
25+
Exit status: 0
26+
............ | INFO | GlobalPythonEngine | Symbolic: stop receiver
27+
............ | INFO | PythonTestCaseGenerator | Collect all test executions for some_method
28+
............ | INFO | SbftGenerateTestsCommand | Saving tests...
29+
```
30+
31+
## Development
32+
33+
The entry point called by `python-tool-competition-2024` is the `build_test`
34+
method in `utbot_python_sbft_2024/generator.py`.
35+
36+
## Calculating Metrics
37+
38+
Run `poetry run python-tool-competition-2024 run utbot-python`.
39+
40+
With `poetry run python-tool-competition-2024 run -h` you can find out what
41+
generators were detected.

poetry.lock

+1,379
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[tool.poetry]
2+
name = "python-tool-competition-2024-utbot_python"
3+
version = "0.1.0"
4+
description = "Python Tool Competition 2024 implementation using UTBotPython"
5+
authors = ["Vyacheslav Tamarin, Ekaterina Tochilina"]
6+
license = "Apache 2.0"
7+
readme = "README.md"
8+
packages = [{include = "python_tool_competition_2024_utbot_python"}]
9+
10+
[tool.poetry.dependencies]
11+
python = "^3.11"
12+
python-tool-competition-2024 = "^0.2.0"
13+
utbot-executor = "1.9.2"
14+
utbot-mypy-runner = "0.2.15"
15+
utbot_dist = [
16+
{ url = "https://github.com/UnitTestBot/UTBotPythonSBFT2024/releases/download/v1/utbot_dist-0.1.0-py3-none-manylinux_2_31_x86_64.whl", platform = "linux" }
17+
]
18+
19+
[build-system]
20+
requires = ["poetry-core"]
21+
build-backend = "poetry.core.masonry.api"
22+
23+
[tool.poetry.plugins."python_tool_competition_2024.test_generators"]
24+
utbot-python = "python_tool_competition_2024_utbot_python.generator:UTBotPythonTestGenerator"
25+
26+
[tool.poetry.scripts]
27+
check = "python_tool_competition_2024_utbot_python.check_usvm:main"

python_tool_competition_2024_utbot_python/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from python_tool_competition_2024_utbot_python.generator import UTBotPythonTestGenerator
2+
3+
4+
def check_usvm():
5+
UTBotPythonTestGenerator.check_run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pathlib
2+
import site
3+
import sys
4+
5+
6+
class UTBotPythonConfig:
7+
JAVA_PATH = "java"
8+
SITE_DIR = pathlib.Path(site.getsitepackages()[0])
9+
JAR_FILE = SITE_DIR / "utbot_dist" / "utbot-cli-python-2023.11-SNAPSHOT.jar"
10+
USVM_PATH = SITE_DIR / "utbot_dist" / "usvm-python"
11+
PYTHON_PATH = sys.executable
12+
TIMEOUT = 90_000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""A test generator using UTBotPython."""
2+
import pathlib
3+
import site
4+
import subprocess
5+
import sys
6+
import tempfile
7+
import time
8+
9+
import rich
10+
from python_tool_competition_2024.config import get_config, GeneratorName
11+
from python_tool_competition_2024.generation_results import (
12+
TestGenerationFailure,
13+
TestGenerationResult,
14+
TestGenerationSuccess,
15+
FailureReason,
16+
)
17+
from python_tool_competition_2024.generators import FileInfo, TestGenerator
18+
19+
from python_tool_competition_2024_utbot_python.config import UTBotPythonConfig
20+
21+
GENERATION_TIMES: list[float] = []
22+
23+
24+
class UTBotPythonTestGenerator(TestGenerator):
25+
"""A test generator using UTBotPython."""
26+
27+
def build_test(self, target_file_info: FileInfo) -> TestGenerationResult:
28+
"""
29+
Generate a test for the specific target file.
30+
31+
Args:
32+
target_file_info: The `FileInfo` of the file to generate a test for.
33+
34+
Returns:
35+
Either a `TestGenerationSuccess` if it was successful, or a
36+
`TestGenerationFailure` otherwise.
37+
"""
38+
return _build_test(target_file_info)
39+
40+
@staticmethod
41+
def check_run() -> None:
42+
"""
43+
Try to run UTBotPython and shows error message.
44+
"""
45+
target_file_info = FileInfo(
46+
pathlib.Path("./targets/example1.py").resolve().absolute(),
47+
"example1",
48+
get_config(
49+
GeneratorName("UTBotPython"),
50+
pathlib.Path("./targets/").resolve().absolute(),
51+
pathlib.Path("").absolute(),
52+
rich.console.Console(),
53+
show_commands=True,
54+
show_failures=True,
55+
),
56+
)
57+
_build_test(target_file_info, check_usvm=True)
58+
59+
60+
def _build_test(
61+
target_file_info: FileInfo, check_usvm: bool = False
62+
) -> TestGenerationResult:
63+
if not sys.platform.startswith("linux"):
64+
sys.stderr.write("ERROR: This script works only on Linux\n")
65+
exit(1)
66+
67+
sys_paths = [target_file_info.config.targets_dir]
68+
69+
with tempfile.TemporaryDirectory() as tempdir:
70+
output_dir = pathlib.Path(tempdir)
71+
module_name = target_file_info.module_name.replace(".", "_")
72+
output_file = output_dir / f"test_{module_name}.py"
73+
_run_utbot(
74+
target_file_info.absolute_path,
75+
sys_paths,
76+
output_file,
77+
UTBotPythonConfig.JAR_FILE,
78+
UTBotPythonConfig.USVM_PATH,
79+
UTBotPythonConfig.TIMEOUT,
80+
UTBotPythonConfig.PYTHON_PATH,
81+
UTBotPythonConfig.JAVA_PATH,
82+
check_usvm=check_usvm,
83+
)
84+
85+
utbot_tests = _read_generated_tests(str(output_file))
86+
if utbot_tests == "":
87+
return TestGenerationFailure(tuple(), FailureReason.NOTHING_GENERATED)
88+
89+
return TestGenerationSuccess(utbot_tests)
90+
91+
92+
def _run_utbot(
93+
source_file: pathlib.Path,
94+
sys_paths: list[pathlib.Path],
95+
output_file: pathlib.Path,
96+
jar_path: pathlib.Path,
97+
usvm_dir: pathlib.Path,
98+
timeout: int,
99+
python_path: str,
100+
java_cmd: str,
101+
check_usvm: bool = False,
102+
):
103+
command = (
104+
f"{java_cmd} -jar {jar_path}"
105+
f" generate_python {source_file}"
106+
f" -p {python_path}"
107+
f" -o {output_file}"
108+
f" -s {','.join(map(str, sys_paths))}"
109+
f" -t {timeout}"
110+
f" --java-cmd {java_cmd}"
111+
f" --usvm-dir {usvm_dir}"
112+
f" --runtime-exception-behaviour PASS"
113+
f" --prohibited-exceptions -"
114+
)
115+
if check_usvm:
116+
command += " --check-usvm"
117+
118+
print(command)
119+
start = time.time()
120+
121+
p = subprocess.Popen(command.split(), close_fds=True)
122+
while p.poll() is None:
123+
time.sleep(1)
124+
125+
cur_time = time.time() - start
126+
GENERATION_TIMES.append(cur_time)
127+
print("Process finished in", cur_time)
128+
print("Mean time:", sum(GENERATION_TIMES) / len(GENERATION_TIMES))
129+
print("Max time:", max(GENERATION_TIMES))
130+
131+
132+
def _read_generated_tests(output_file: str) -> str:
133+
try:
134+
with open(output_file, "r") as f:
135+
return f.read()
136+
except:
137+
return ""

targets/__init__.py

Whitespace-only changes.

targets/example1.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""First example file."""
2+
3+
4+
def some_method(number: int) -> str:
5+
"""Return a string of the manipulated number."""
6+
if number > 0:
7+
return f"{number * 5}"
8+
return f"{number - 3}"
9+
10+
11+
def other_method(number: int) -> int:
12+
"""Return the number calculated by a for loop."""
13+
total = 0
14+
for i in range(number):
15+
total += i
16+
return total

targets/example2.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Second example file."""
2+
3+
4+
def my_method(n: int) -> int:
5+
"""Calculate the square."""
6+
return n**2

targets/sub_example/__init__.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""The sub example pacakge."""
2+
3+
from __future__ import annotations
4+
5+
6+
def helper(text: str) -> str | None:
7+
"""Add an exclamation mark if the text is not empty, `None` otherwise."""
8+
if text:
9+
return text + "!"
10+
return None

targets/sub_example/example3.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Third example file."""
2+
3+
4+
def example(text: str) -> str:
5+
"""Return an extended string."""
6+
result = f"Got: {text}"
7+
return result + "!"

targets/sub_example/example4.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import gzip
2+
3+
4+
def compress_string(s):
5+
"""
6+
Compress a string using gzip.
7+
8+
:param s: String to be compressed
9+
:type s: str
10+
:return: Compressed string
11+
:rtype: bytes
12+
"""
13+
return gzip.compress(s.encode("utf-8"))
14+
15+
16+
def decompress_string(compressed):
17+
"""
18+
Decompress a gzipped string.
19+
20+
:param compressed: Compressed string
21+
:type compressed: bytes
22+
:return: Decompressed string
23+
:rtype: str
24+
"""
25+
return gzip.decompress(compressed).decode("utf-8")

0 commit comments

Comments
 (0)