Skip to content

Commit 00e13f6

Browse files
authored
Fix extra files handling (#29)
* Fix extra files handling * Add tests * Run formatters
1 parent 4f2c40d commit 00e13f6

File tree

20 files changed

+171
-12
lines changed

20 files changed

+171
-12
lines changed

src/sio3pack/packages/package/configuration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def detect(cls) -> dict[str, "CompilerConfig"]:
1313
# TODO: implement this properly
1414
return {
1515
"cpp": cls("g++", "g++-12.2", "g++", ["-std=c++20", "-O3"]),
16-
"py": cls("python", "python3.10", "python", []),
16+
"python": cls("python", "python3.10", "python", []),
1717
}
1818

1919

src/sio3pack/packages/package/model.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,6 @@ def get_title(self, lang: str | None = None) -> str:
172172
def get_statement(self, lang: str | None = None) -> File | None:
173173
pass
174174

175-
@wrap_exceptions
176-
def get_tests(self) -> list[Test]:
177-
pass
178-
179175
@wrap_exceptions
180176
def get_test(self, test_id: str) -> Test:
181177
pass

src/sio3pack/packages/sinolpack/model.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ def _get_special_file_path(self, file_type: str) -> str | None:
482482
return self.special_files[file_type].path
483483
return None
484484

485+
def get_ingen_path(self) -> str | None:
486+
return self._get_special_file_path("ingen")
487+
485488
def get_inwer_path(self) -> str | None:
486489
return self._get_special_file_path("inwer")
487490

@@ -543,7 +546,7 @@ def get_extra_execution_files(self) -> list[File]:
543546
return [
544547
LocalFile(os.path.join(self.rootdir, "prog", f))
545548
for f in self.config.get("extra_execution_files", [])
546-
if os.path.isfile(os.path.join(self.rootdir, f))
549+
if os.path.isfile(os.path.join(self.rootdir, "prog", f))
547550
]
548551

549552
def get_extra_compilation_files(self) -> list[File]:
@@ -559,7 +562,7 @@ def get_extra_compilation_files(self) -> list[File]:
559562
return [
560563
LocalFile(os.path.join(self.rootdir, "prog", f))
561564
for f in self.config.get("extra_compilation_files", [])
562-
if os.path.isfile(os.path.join(self.rootdir, f))
565+
if os.path.isfile(os.path.join(self.rootdir, "prog", f))
563566
]
564567

565568
def _get_limit(self, test: Test, language: str, type: str) -> int:

src/sio3pack/packages/sinolpack/workflows.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def get_compile_file_workflow(self, file: File | str) -> tuple[Workflow, str]:
5252

5353
def _get_ingen_workflow(self) -> Workflow:
5454
workflow = Workflow(
55-
"ingen",
55+
"Run ingen",
5656
observable_registers=1,
5757
)
5858
ingen_path = self.package.get_ingen_path()

src/sio3pack/workflow/execution/mount_namespace.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,9 @@ def to_json(self) -> list[dict]:
139139
Convert the mount namespace manager to a dictionary.
140140
"""
141141
return [mount_namespace.to_json() for mount_namespace in self.mount_namespaces]
142+
143+
def len(self) -> int:
144+
"""
145+
Get the number of mount namespaces.
146+
"""
147+
return len(self.mount_namespaces)

src/sio3pack/workflow/object.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ def __init__(self, handle: str):
1717
def __str__(self):
1818
return f"<Object {self.handle}>"
1919

20+
def __repr__(self):
21+
return f"<Object {self.handle}>"
22+
2023
def replace_templates(self, replacements: dict[str, str]):
2124
"""
2225
Replace strings in the object with the given replacements.
@@ -113,7 +116,7 @@ def __str__(self):
113116
114117
:return: The string representation of the list.
115118
"""
116-
return f"<ObjectList {len(self.objects)} objects>"
119+
return f"<ObjectList {self.objects}>"
117120

118121
def union(self, other: "ObjectList"):
119122
"""

tests/packages/sinolpack/test_workflows.py

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sio3pack.packages.package.configuration import SIO3PackConfig
88
from sio3pack.workflow import ExecutionTask, ScriptTask, Workflow
99
from sio3pack.workflow.execution import ObjectReadStream, ObjectWriteStream
10+
from sio3pack.workflow.execution.filesystems import ObjectFilesystem
1011
from tests.fixtures import PackageInfo, get_package
1112
from tests.packages.sinolpack.utils import common_checks
1213

@@ -25,9 +26,10 @@ def test_unpack_workflows(get_package):
2526

2627
if package_info.task_id == "abc":
2728
# We expect 2 workflows: compile checker and outgen
28-
assert len(workflows) == 2
29+
assert len(workflows) == 3
2930
assert workflows[0].name == "Compile checker"
30-
assert workflows[1].name == "Outgen tests"
31+
assert workflows[1].name == "Run ingen"
32+
assert workflows[2].name == "Outgen tests"
3133
elif package_info.task_id == "wer":
3234
# We expect 3 workflows: compile checker, outgen and inwer
3335
assert len(workflows) == 3
@@ -166,3 +168,141 @@ def test_custom_workflow(get_package):
166168

167169
assert num_custom == 3, "Should have 3 custom execution tasks"
168170
assert num_custom_scripts == 3, "Should have 3 custom script tasks"
171+
172+
173+
@pytest.mark.parametrize("get_package", ["simple"], indirect=True)
174+
def test_user_out_workflow(get_package):
175+
package_info: PackageInfo = get_package()
176+
package = sio3pack.from_file(package_info.path, SIO3PackConfig.detect())
177+
common_checks(package_info, package)
178+
179+
program = LocalFile(os.path.join(package.rootdir, "prog", "abc.cpp"))
180+
test = package.tests[0]
181+
182+
op = package.get_user_out_operation(program, test)
183+
workflows = [wf for wf in op.get_workflow()]
184+
assert len(workflows) == 1
185+
workflow = workflows[0]
186+
187+
assert len(workflow.observable_objects) == 1, "Should have just user out object"
188+
assert workflow.observable_objects[0].handle.startswith("user_out_"), "User out object should be user_out_"
189+
190+
num_compiles = 0
191+
num_runs = 0
192+
for task in workflow.tasks:
193+
if task.name == f"Compile {program.path} using g++-12.2":
194+
assert len(task.processes) == 1, "Compile task should have one process"
195+
assert task.processes[0].image == "compiler:g++-12.2", "Compile task should use g++-12.2 image"
196+
num_compiles += 1
197+
elif task.name.startswith("Run solution for test"):
198+
num_runs += 1
199+
assert len(task.processes) == 1, "Run task should have one process"
200+
proc = task.processes[0]
201+
stdin_fd = proc.descriptor_manager.get(0)
202+
assert isinstance(stdin_fd, ObjectReadStream), "Run task should have stdin stream"
203+
stdout_fd = proc.descriptor_manager.get(1)
204+
assert isinstance(stdout_fd, ObjectWriteStream), "Run task should have stdout stream"
205+
assert stdout_fd.object.handle.startswith("user_out"), "Run task should have user_out stream"
206+
207+
assert num_compiles == 1, "Should have one compile task"
208+
assert num_runs == 1, "Should have one run task"
209+
210+
211+
@pytest.mark.parametrize("get_package", ["simple"], indirect=True)
212+
def test_test_run_workflow(get_package):
213+
package_info: PackageInfo = get_package()
214+
package = sio3pack.from_file(package_info.path, SIO3PackConfig.detect())
215+
common_checks(package_info, package)
216+
217+
program = LocalFile(os.path.join(package.rootdir, "prog", "abc.cpp"))
218+
test = LocalFile(os.path.join(package.rootdir, "in", "abc0.in"))
219+
220+
op = package.get_test_run_operation(program, test)
221+
workflows = [wf for wf in op.get_workflow()]
222+
assert len(workflows) == 1
223+
workflow = workflows[0]
224+
225+
assert len(workflow.observable_objects) == 1, "Should have just user out object"
226+
out_obj = workflow.observable_objects[0]
227+
assert out_obj.handle == f"test_run_{program.filename}", "User out object should be user_out_<filename>"
228+
229+
assert len(workflow.tasks) == 2, "Should have two tasks"
230+
assert workflow.tasks[0].name == f"Compile {program.path} using g++-12.2", "First task should be compile"
231+
assert workflow.tasks[1].name == f"Run solution for test", "Second task should be run"
232+
exec_task = workflow.tasks[1]
233+
assert isinstance(exec_task, ExecutionTask), "Second task should be an execution task"
234+
assert len(exec_task.processes) == 1, "Run task should have one process"
235+
proc = exec_task.processes[0]
236+
stdin_fd = proc.descriptor_manager.get(0)
237+
assert isinstance(stdin_fd, ObjectReadStream), "Run task should have stdin stream"
238+
assert stdin_fd.object.handle == test.path, "Run task should have stdin stream"
239+
stdout_fd = proc.descriptor_manager.get(1)
240+
assert isinstance(stdout_fd, ObjectWriteStream), "Run task should have stdout stream"
241+
assert stdout_fd.object.handle == out_obj.handle, "Run task should have stdout stream"
242+
243+
244+
@pytest.mark.parametrize("get_package", ["extra_files"], indirect=True)
245+
def test_extra_files(get_package):
246+
package_info: PackageInfo = get_package()
247+
package = sio3pack.from_file(package_info.path, SIO3PackConfig.detect())
248+
common_checks(package_info, package)
249+
250+
program = LocalFile(os.path.join(package.rootdir, "prog", "ext.cpp"))
251+
252+
op = package.get_run_operation(program)
253+
workflows = [wf for wf in op.get_workflow()]
254+
assert len(workflows) == 1
255+
workflow = workflows[0]
256+
257+
assert len(workflow.external_objects) == 5
258+
extlib_h = None
259+
extlib_py = None
260+
for obj in workflow.external_objects:
261+
if obj.handle.endswith("extlib.h"):
262+
extlib_h = obj
263+
elif obj.handle.endswith("extlib.py"):
264+
extlib_py = obj
265+
assert extlib_h is not None, "Should have extlib.h as external object"
266+
assert extlib_py is not None, "Should have extlib.py as external object"
267+
268+
for task in workflow.tasks:
269+
if isinstance(task, ExecutionTask):
270+
if task.name == f"Compile {program.path} using g++-12.2":
271+
assert task.filesystem_manager.len() == 2
272+
ext_fs = task.filesystem_manager.get_by_id(1)
273+
assert isinstance(ext_fs, ObjectFilesystem), "Should have object filesystem with external file"
274+
assert ext_fs.object.handle == extlib_h.handle, "Should have extlib.h as external file"
275+
276+
assert task.mountnamespace_manager.len() == 1, "Should have one mount namespace"
277+
assert len(task.mountnamespace_manager.get_by_id(0).mountpoints) == 2, "Should have two mount points"
278+
279+
proc = task.processes[0]
280+
assert "extlib.h" in proc.arguments, "Should have extlib.h in arguments"
281+
elif task.name.startswith("Run solution for test"):
282+
assert task.filesystem_manager.len() == 2
283+
ext_fs = task.filesystem_manager.get_by_id(1)
284+
assert isinstance(ext_fs, ObjectFilesystem), "Should have object filesystem with external file"
285+
assert ext_fs.object.handle == extlib_py.handle, "Should have extlib.py as external file"
286+
287+
assert task.mountnamespace_manager.len() == 1, "Should have one mount namespace"
288+
assert len(task.mountnamespace_manager.get_by_id(0).mountpoints) == 2, "Should have two mount points"
289+
290+
# Check that python compilation doesnt have extlib.h in compilation args.
291+
program = LocalFile(os.path.join(package.rootdir, "prog", "ext.py"))
292+
op = package.get_run_operation(program)
293+
workflows = [wf for wf in op.get_workflow()]
294+
assert len(workflows) == 1
295+
296+
for task in workflows[0].tasks:
297+
if isinstance(task, ExecutionTask):
298+
if task.name == f"Compile {program.path} using python":
299+
assert task.filesystem_manager.len() == 2
300+
ext_fs = task.filesystem_manager.get_by_id(1)
301+
assert isinstance(ext_fs, ObjectFilesystem), "Should have object filesystem with external file"
302+
assert ext_fs.object.handle == extlib_py.handle, "Should have extlib.py as external file"
303+
304+
assert task.mountnamespace_manager.len() == 1, "Should have one mount namespace"
305+
assert len(task.mountnamespace_manager.get_by_id(0).mountpoints) == 2, "Should have two mount points"
306+
307+
proc = task.processes[0]
308+
assert "extlib.h" not in proc.arguments, "Should not have extlib.h in arguments"

tests/test_django/test_sio3pack/test_sinolpack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def test_additional_files(get_archived_package):
8282
package, db_package = _save_and_test_simple(package_info)
8383
additional_files = package.additional_files
8484
db_additional_files = SinolpackAdditionalFile.objects.filter(package=db_package)
85-
assert db_additional_files.count() == 1
85+
assert db_additional_files.count() == 2
8686
assert len(additional_files) == db_additional_files.count()
8787

8888
for file in additional_files:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TASK_ID = "ext"
2+
TYPE = "sinolpack"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
title: Package with extra files
2+
extra_compilation_files: ['extlib.h']
3+
extra_compilation_args:
4+
cpp:
5+
- 'extlib.h'
6+
extra_execution_files: ['extlib.py']

0 commit comments

Comments
 (0)