rbx.cp 0.5.39__tar.gz → 0.5.40__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/PKG-INFO +1 -1
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/pyproject.toml +1 -1
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/checkers.py +5 -1
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/main.py +12 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/main.py +9 -0
- rbx_cp-0.5.40/rbx/box/packaging/moj/packager.py +125 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/schema.py +66 -0
- rbx_cp-0.5.40/rbx/box/unit.py +113 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/validators.py +17 -8
- rbx_cp-0.5.40/rbx/resources/packagers/moj/scripts/compare.sh +82 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/LICENSE +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/README.md +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/annotations.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/autoenum.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/builder.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/cd.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/code.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/compile.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/conftest.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/build_contest_statements.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/contest_package.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/contest_utils.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/statements.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/creation.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/deferred.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/download.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/environment.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/extensions.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/formatting.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/generators.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/generators_test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/lazy_importing_main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/lazy_importing_test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/package.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/boca/extension.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/boca/packager.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/contest_main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/packager.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/packager.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/xml_schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/fetch.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/lock_schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/retries.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/sanitizers/warning_stack.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/setter_config.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/solutions.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/solutions_test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/state.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/build_statements.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/builders.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/joiners.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/latex.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/latex_jinja.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stresses.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/finder_parser.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/generator_parser.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcase_extractors.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcase_utils.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcases/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcases/main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/captured_log.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/css/app.tcss +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/run.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/validators_test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/checker.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/clone.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/config.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/conftest.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/console.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/create.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/edit.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/caching.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/conftest.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/cacher.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/digester.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandbox.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/isolate.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/timeit.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/storage.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/testiso.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching_run_test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading_utils.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/hydration.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/main.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/metadata.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/codeforces.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/provider.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/checkers/boilerplate.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_config.json +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.mac.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/envs/default.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/envs/isolate.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/checker.sh +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compare +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/c +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cc +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/java +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/kt +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/pas +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py2 +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py3 +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/c +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cc +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/java +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/kt +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py2 +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py3 +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/preset.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/.gitignore +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/gen.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.txt +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/validator.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/templates/template.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/run.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/schema.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submit.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/__init__.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/codeforces.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/submitor.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/test.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testcase.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testcase_rendering.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/gen1.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/gen2.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/genScript.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/ole.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/problem.rbx.yml +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/re.sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tests/1.in +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tle.sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/validator.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/wa.sol.cpp +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/caching/executable.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/compatible +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testing_utils.py +0 -0
- {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/utils.py +0 -0
@@ -17,11 +17,15 @@ from rbx.grading.steps import (
|
|
17
17
|
Outcome,
|
18
18
|
RunLog,
|
19
19
|
)
|
20
|
+
from rbx.utils import StatusProgress
|
20
21
|
|
21
22
|
|
22
|
-
def compile_checker() -> str:
|
23
|
+
def compile_checker(progress: Optional[StatusProgress] = None) -> str:
|
23
24
|
checker = package.get_checker()
|
24
25
|
|
26
|
+
if progress:
|
27
|
+
progress.update('Compiling checker...')
|
28
|
+
|
25
29
|
try:
|
26
30
|
digest = compile_item(checker, sanitized=SanitizationLevel.PREFER)
|
27
31
|
except Exception as e:
|
@@ -741,6 +741,18 @@ def validate(
|
|
741
741
|
validators.print_validation_report([info])
|
742
742
|
|
743
743
|
|
744
|
+
@app.command(
|
745
|
+
'unit',
|
746
|
+
rich_help_panel='Testing',
|
747
|
+
help='Run unit tests for the validator and checker.',
|
748
|
+
)
|
749
|
+
def unit_tests():
|
750
|
+
from rbx.box import unit
|
751
|
+
|
752
|
+
with utils.StatusProgress('Running unit tests...') as s:
|
753
|
+
unit.run_unit_tests(s)
|
754
|
+
|
755
|
+
|
744
756
|
@app.command(
|
745
757
|
'environment, env',
|
746
758
|
rich_help_panel='Configuration',
|
@@ -70,3 +70,12 @@ def boca(
|
|
70
70
|
from rbx.box.packaging.boca.packager import BocaPackager
|
71
71
|
|
72
72
|
run_packager(BocaPackager, verification=verification)
|
73
|
+
|
74
|
+
|
75
|
+
@app.command('moj', help='Build a package for MOJ.')
|
76
|
+
def moj(
|
77
|
+
verification: environment.VerificationParam,
|
78
|
+
):
|
79
|
+
from rbx.box.packaging.moj.packager import MojPackager
|
80
|
+
|
81
|
+
run_packager(MojPackager, verification=verification)
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import pathlib
|
2
|
+
import shutil
|
3
|
+
from typing import List
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from rbx import console
|
8
|
+
from rbx.box import package
|
9
|
+
from rbx.box.environment import get_extension_or_default
|
10
|
+
from rbx.box.packaging.boca.extension import BocaExtension
|
11
|
+
from rbx.box.packaging.boca.packager import BocaPackager
|
12
|
+
from rbx.box.packaging.packager import BuiltStatement
|
13
|
+
from rbx.config import get_default_app_path
|
14
|
+
from rbx.grading.judge.digester import digest_cooperatively
|
15
|
+
|
16
|
+
|
17
|
+
class MojPackager(BocaPackager):
|
18
|
+
def _get_problem_info(self) -> str:
|
19
|
+
statement = self._get_main_statement()
|
20
|
+
return (
|
21
|
+
f'basename={self._get_problem_name()}\n'
|
22
|
+
f'fullname={statement.title}\n'
|
23
|
+
f'descfile={self._get_problem_name()}.pdf\n'
|
24
|
+
)
|
25
|
+
|
26
|
+
def _get_limits(self) -> str:
|
27
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
28
|
+
|
29
|
+
pkg = package.find_problem_package_or_die()
|
30
|
+
tl = pkg.timeLimit
|
31
|
+
ml = pkg.memoryLimit
|
32
|
+
ol = pkg.outputLimit
|
33
|
+
conf = f'ULIMITS[-f]={ol}\n' f'ULIMITS[-v]={ml}\n' f'TL[default]={tl / 1000}\n'
|
34
|
+
for language in extension.languages:
|
35
|
+
conf += f'TL[{language}]={self._get_pkg_timelimit(language) / 1000}\n'
|
36
|
+
return conf
|
37
|
+
|
38
|
+
def _get_compare(self) -> str:
|
39
|
+
extension = get_extension_or_default('boca', BocaExtension)
|
40
|
+
|
41
|
+
compare_path = (
|
42
|
+
get_default_app_path() / 'packagers' / 'moj' / 'scripts' / 'compare.sh'
|
43
|
+
)
|
44
|
+
if not compare_path.exists():
|
45
|
+
console.console.print(
|
46
|
+
'[error]MOJ template compare script not found.[/error]'
|
47
|
+
)
|
48
|
+
raise typer.Exit(1)
|
49
|
+
with package.get_checker().path.open('rb') as f:
|
50
|
+
checker_hash = digest_cooperatively(f)
|
51
|
+
return (
|
52
|
+
compare_path.read_text()
|
53
|
+
.replace('{{rbxFlags}}', extension.flags_with_defaults()['cc'])
|
54
|
+
.replace('{{checkerHash}}', checker_hash)
|
55
|
+
)
|
56
|
+
|
57
|
+
def _get_checker(self) -> str:
|
58
|
+
return package.get_checker().path.read_text()
|
59
|
+
|
60
|
+
def name(self) -> str:
|
61
|
+
return 'moj'
|
62
|
+
|
63
|
+
def package(
|
64
|
+
self,
|
65
|
+
build_path: pathlib.Path,
|
66
|
+
into_path: pathlib.Path,
|
67
|
+
built_statements: List[BuiltStatement],
|
68
|
+
) -> pathlib.Path:
|
69
|
+
# Prepare dummy files
|
70
|
+
author_path = into_path / 'author'
|
71
|
+
author_path.parent.mkdir(parents=True, exist_ok=True)
|
72
|
+
author_path.write_text('Unknown\n')
|
73
|
+
|
74
|
+
tags_path = into_path / 'tags'
|
75
|
+
tags_path.parent.mkdir(parents=True, exist_ok=True)
|
76
|
+
tags_path.write_text('')
|
77
|
+
|
78
|
+
# Prepare limits
|
79
|
+
limits_path = into_path / 'conf'
|
80
|
+
limits_path.parent.mkdir(parents=True, exist_ok=True)
|
81
|
+
limits_path.write_text(self._get_limits())
|
82
|
+
|
83
|
+
# Prepare compare
|
84
|
+
compare_path = into_path / 'scripts' / 'compare.sh'
|
85
|
+
compare_path.parent.mkdir(parents=True, exist_ok=True)
|
86
|
+
compare_path.write_text(self._get_compare())
|
87
|
+
|
88
|
+
# Prepare checker
|
89
|
+
checker_path = into_path / 'scripts' / 'checker.cpp'
|
90
|
+
checker_path.parent.mkdir(parents=True, exist_ok=True)
|
91
|
+
checker_path.write_text(self._get_checker())
|
92
|
+
|
93
|
+
# Problem statement
|
94
|
+
enunciado_path = into_path / 'docs' / 'enunciado.pdf'
|
95
|
+
enunciado_path.parent.mkdir(parents=True, exist_ok=True)
|
96
|
+
shutil.copyfile(
|
97
|
+
self._get_main_built_statement(built_statements).path,
|
98
|
+
enunciado_path,
|
99
|
+
)
|
100
|
+
|
101
|
+
# Copy solutions
|
102
|
+
solutions_path = into_path / 'solutions'
|
103
|
+
solutions_path.mkdir(parents=True, exist_ok=True)
|
104
|
+
self._copy_solutions(solutions_path)
|
105
|
+
|
106
|
+
# Prepare IO
|
107
|
+
inputs_path = into_path / 'tests' / 'input'
|
108
|
+
inputs_path.mkdir(parents=True, exist_ok=True)
|
109
|
+
outputs_path = into_path / 'tests' / 'output'
|
110
|
+
outputs_path.mkdir(parents=True, exist_ok=True)
|
111
|
+
|
112
|
+
testcases = self.get_flattened_built_testcases()
|
113
|
+
for i, testcase in enumerate(testcases):
|
114
|
+
shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
|
115
|
+
if testcase.outputPath is not None:
|
116
|
+
shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
|
117
|
+
else:
|
118
|
+
(outputs_path / f'{i+1:03d}').touch()
|
119
|
+
|
120
|
+
# Zip all.
|
121
|
+
shutil.make_archive(
|
122
|
+
str(build_path / self._get_problem_name()), 'zip', into_path
|
123
|
+
)
|
124
|
+
|
125
|
+
return (build_path / self._get_problem_name()).with_suffix('.zip')
|
@@ -148,6 +148,14 @@ class ExpectedOutcome(AutoEnum):
|
|
148
148
|
return bool(set(self.get_matches()) & set(rhs.get_matches()))
|
149
149
|
|
150
150
|
|
151
|
+
class ValidatorOutcome(AutoEnum):
|
152
|
+
VALID = alias('valid') # type: ignore
|
153
|
+
"""Expected outcome for valid tests."""
|
154
|
+
|
155
|
+
INVALID = alias('invalid') # type: ignore
|
156
|
+
"""Expected outcome for invalid tests."""
|
157
|
+
|
158
|
+
|
151
159
|
class CodeItem(BaseModel):
|
152
160
|
model_config = ConfigDict(extra='forbid')
|
153
161
|
|
@@ -337,6 +345,59 @@ class LimitModifiers(BaseModel):
|
|
337
345
|
)
|
338
346
|
|
339
347
|
|
348
|
+
class ValidatorTest(BaseModel):
|
349
|
+
model_config = ConfigDict(extra='forbid')
|
350
|
+
|
351
|
+
input: pathlib.Path = Field(
|
352
|
+
description='The input file to be used as unit test input for the validator.'
|
353
|
+
)
|
354
|
+
outcome: ValidatorOutcome = Field(
|
355
|
+
default=ValidatorOutcome.VALID,
|
356
|
+
description='The expected outcome of the validator.',
|
357
|
+
)
|
358
|
+
|
359
|
+
validator: Optional[CodeItem] = Field(
|
360
|
+
default=None,
|
361
|
+
description='The validator to use for this test. If not specified, will use the package-level validator.',
|
362
|
+
)
|
363
|
+
|
364
|
+
|
365
|
+
class CheckerTest(BaseModel):
|
366
|
+
model_config = ConfigDict(extra='forbid')
|
367
|
+
|
368
|
+
input: Optional[pathlib.Path] = Field(
|
369
|
+
default=None,
|
370
|
+
description='The input file to be used as unit test input for the checker. If not specified, will pass an empty file.',
|
371
|
+
)
|
372
|
+
output: Optional[pathlib.Path] = Field(
|
373
|
+
default=None,
|
374
|
+
description='The solution output file to be used as unit test output for the checker. If not specified, will pass an empty file.',
|
375
|
+
)
|
376
|
+
answer: Optional[pathlib.Path] = Field(
|
377
|
+
default=None,
|
378
|
+
description='The answer file to be used as unit test answer for the checker. If not specified, will pass an empty file.',
|
379
|
+
)
|
380
|
+
|
381
|
+
outcome: ExpectedOutcome = Field(
|
382
|
+
default=ExpectedOutcome.ACCEPTED,
|
383
|
+
description='The expected outcome of the checker.',
|
384
|
+
)
|
385
|
+
|
386
|
+
|
387
|
+
class UnitTests(BaseModel):
|
388
|
+
model_config = ConfigDict(extra='forbid')
|
389
|
+
|
390
|
+
validator: List[ValidatorTest] = Field(
|
391
|
+
default=[],
|
392
|
+
description='Unit tests for the validator.',
|
393
|
+
)
|
394
|
+
|
395
|
+
checker: List[CheckerTest] = Field(
|
396
|
+
default=[],
|
397
|
+
description='Unit tests for the checker.',
|
398
|
+
)
|
399
|
+
|
400
|
+
|
340
401
|
class Package(BaseModel):
|
341
402
|
model_config = ConfigDict(extra='forbid')
|
342
403
|
|
@@ -399,6 +460,11 @@ that is correct and used as reference -- and should have the `accepted` outcome.
|
|
399
460
|
default={}, description='Variables to be re-used across the package.'
|
400
461
|
)
|
401
462
|
|
463
|
+
unitTests: UnitTests = Field(
|
464
|
+
default_factory=UnitTests,
|
465
|
+
description='Unit tests for components of this problem.',
|
466
|
+
)
|
467
|
+
|
402
468
|
@property
|
403
469
|
def expanded_vars(self) -> Dict[str, Primitive]:
|
404
470
|
return {key: expand_var(value) for key, value in self.vars.items()}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
|
3
|
+
from rbx import console
|
4
|
+
from rbx.box import checkers, package, validators
|
5
|
+
from rbx.box.schema import CodeItem, Testcase, ValidatorOutcome, ValidatorTest
|
6
|
+
from rbx.utils import StatusProgress
|
7
|
+
|
8
|
+
|
9
|
+
def _get_validator_for_test(test: ValidatorTest) -> Optional[CodeItem]:
|
10
|
+
pkg = package.find_problem_package_or_die()
|
11
|
+
if test.validator is not None:
|
12
|
+
return test.validator
|
13
|
+
return pkg.validator
|
14
|
+
|
15
|
+
|
16
|
+
def run_validator_unit_tests(progress: StatusProgress):
|
17
|
+
pkg = package.find_problem_package_or_die()
|
18
|
+
|
19
|
+
vals: List[CodeItem] = []
|
20
|
+
for test in pkg.unitTests.validator:
|
21
|
+
val = _get_validator_for_test(test)
|
22
|
+
if val is not None:
|
23
|
+
vals.append(val)
|
24
|
+
|
25
|
+
compiled_validators = validators.compile_validators(vals, progress=progress)
|
26
|
+
|
27
|
+
if progress:
|
28
|
+
progress.update('Running validator unit tests...')
|
29
|
+
|
30
|
+
console.console.rule('Validator tests', style='info')
|
31
|
+
|
32
|
+
for i, test in enumerate(pkg.unitTests.validator):
|
33
|
+
val = _get_validator_for_test(test)
|
34
|
+
if val is None:
|
35
|
+
console.console.print(
|
36
|
+
f'[warning]No validator found for test [item]#{i + 1}[/item], skipping.[/warning]'
|
37
|
+
)
|
38
|
+
continue
|
39
|
+
|
40
|
+
compiled_digest = compiled_validators[str(val.path)]
|
41
|
+
info = validators.validate_one_off(
|
42
|
+
test.input,
|
43
|
+
val,
|
44
|
+
compiled_digest,
|
45
|
+
)
|
46
|
+
|
47
|
+
is_valid = test.outcome == ValidatorOutcome.VALID
|
48
|
+
|
49
|
+
markup = (
|
50
|
+
'[success]OK[/success]' if info.ok == is_valid else '[error]FAIL[/error]'
|
51
|
+
)
|
52
|
+
|
53
|
+
console.console.print(
|
54
|
+
f'{markup} Unit test [item]#{i + 1}[/item] for [item]{test.input}[/item]'
|
55
|
+
)
|
56
|
+
console.console.print(f' [status]Expected[/status] {test.outcome.value}')
|
57
|
+
if info.ok != is_valid:
|
58
|
+
if info.ok:
|
59
|
+
console.console.print(' [status]Actual[/status] VALID')
|
60
|
+
else:
|
61
|
+
console.console.print(f' [status]Actual[/status] {info.message}')
|
62
|
+
|
63
|
+
|
64
|
+
def run_checker_unit_tests(progress: StatusProgress):
|
65
|
+
pkg = package.find_problem_package_or_die()
|
66
|
+
if not pkg.unitTests.checker:
|
67
|
+
return
|
68
|
+
|
69
|
+
if not package.get_checker():
|
70
|
+
console.console.print(
|
71
|
+
'[warning]No checker found, skipping checker unit tests.[/warning]'
|
72
|
+
)
|
73
|
+
return
|
74
|
+
|
75
|
+
compiled_digest = checkers.compile_checker(progress=progress)
|
76
|
+
|
77
|
+
if progress:
|
78
|
+
progress.update('Running checker unit tests...')
|
79
|
+
|
80
|
+
console.console.rule('Checker tests', style='info')
|
81
|
+
|
82
|
+
empty_file = package.get_empty_sentinel_path()
|
83
|
+
|
84
|
+
for i, test in enumerate(pkg.unitTests.checker):
|
85
|
+
result = checkers.check(
|
86
|
+
compiled_digest,
|
87
|
+
run_log=None,
|
88
|
+
testcase=Testcase(
|
89
|
+
inputPath=test.input or empty_file,
|
90
|
+
outputPath=test.answer or empty_file,
|
91
|
+
),
|
92
|
+
program_output=test.output or empty_file,
|
93
|
+
skip_run_log=True,
|
94
|
+
)
|
95
|
+
|
96
|
+
markup = (
|
97
|
+
'[success]OK[/success]'
|
98
|
+
if test.outcome.match(result.outcome)
|
99
|
+
else '[error]FAIL[/error]'
|
100
|
+
)
|
101
|
+
|
102
|
+
console.console.print(f'{markup} Unit test [item]#{i + 1}[/item]')
|
103
|
+
console.console.print(f' [status]Expected[/status] {test.outcome.name}')
|
104
|
+
|
105
|
+
if not test.outcome.match(result.outcome):
|
106
|
+
console.console.print(f' [status]Actual[/status] {result.outcome.name}')
|
107
|
+
if result.message:
|
108
|
+
console.console.print(f' [status]Message[/status] {result.message}')
|
109
|
+
|
110
|
+
|
111
|
+
def run_unit_tests(progress: StatusProgress):
|
112
|
+
run_validator_unit_tests(progress)
|
113
|
+
run_checker_unit_tests(progress)
|
@@ -177,15 +177,10 @@ def validate_one_off(
|
|
177
177
|
|
178
178
|
|
179
179
|
def compile_validators(
|
180
|
-
|
180
|
+
validators: List[CodeItem],
|
181
181
|
progress: Optional[StatusProgress] = None,
|
182
182
|
) -> Dict[str, str]:
|
183
|
-
|
184
|
-
|
185
|
-
for entry in validation_entries:
|
186
|
-
if entry.validator is not None:
|
187
|
-
validators.append(entry.validator)
|
188
|
-
validators.extend(entry.extra_validators)
|
183
|
+
validator_to_compiled_digest = {}
|
189
184
|
|
190
185
|
validator_to_compiled_digest = {}
|
191
186
|
|
@@ -202,6 +197,20 @@ def compile_validators(
|
|
202
197
|
return validator_to_compiled_digest
|
203
198
|
|
204
199
|
|
200
|
+
def compile_validators_for_entries(
|
201
|
+
validation_entries: List[GenerationTestcaseEntry],
|
202
|
+
progress: Optional[StatusProgress] = None,
|
203
|
+
) -> Dict[str, str]:
|
204
|
+
validators = []
|
205
|
+
|
206
|
+
for entry in validation_entries:
|
207
|
+
if entry.validator is not None:
|
208
|
+
validators.append(entry.validator)
|
209
|
+
validators.extend(entry.extra_validators)
|
210
|
+
|
211
|
+
return compile_validators(validators, progress=progress)
|
212
|
+
|
213
|
+
|
205
214
|
def validate_testcases(
|
206
215
|
progress: Optional[StatusProgress] = None,
|
207
216
|
groups: Optional[Set[str]] = None,
|
@@ -211,7 +220,7 @@ def validate_testcases(
|
|
211
220
|
progress.step()
|
212
221
|
|
213
222
|
validation_entries = extract_generation_testcases_from_groups(groups)
|
214
|
-
validator_to_compiled_digest =
|
223
|
+
validator_to_compiled_digest = compile_validators_for_entries(
|
215
224
|
validation_entries, progress=progress
|
216
225
|
)
|
217
226
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# ////////////////////////////////////////////////////////////////////////////////
|
3
|
+
# //BOCA Online Contest Administrator
|
4
|
+
# // Copyright (C) 2003-2012 by BOCA Development Team (bocasystem@gmail.com)
|
5
|
+
# //
|
6
|
+
# // This program is free software: you can redistribute it and/or modify
|
7
|
+
# // it under the terms of the GNU General Public License as published by
|
8
|
+
# // the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# // (at your option) any later version.
|
10
|
+
# //
|
11
|
+
# // This program is distributed in the hope that it will be useful,
|
12
|
+
# // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# // GNU General Public License for more details.
|
15
|
+
# // You should have received a copy of the GNU General Public License
|
16
|
+
# // along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
# ////////////////////////////////////////////////////////////////////////////////
|
18
|
+
# // Last modified 21/jul/2012 by cassio@ime.usp.br
|
19
|
+
#
|
20
|
+
# This script receives:
|
21
|
+
# $1 team_output
|
22
|
+
# $2 sol_output
|
23
|
+
# $3 problem_input (might be used by some specific checkers, here it is not)
|
24
|
+
#
|
25
|
+
# BOCA reads the last line of the standard output
|
26
|
+
# and pass it to judges
|
27
|
+
#
|
28
|
+
if [ ! -r "$1" -o ! -r "$2" ]; then
|
29
|
+
echo "Parameter problem"
|
30
|
+
exit 43
|
31
|
+
fi
|
32
|
+
|
33
|
+
CHECKERSOURCE=$(dirname "$0")/checker.cpp
|
34
|
+
|
35
|
+
if [ ! -r "$CHECKERSOURCE" ]; then
|
36
|
+
echo "Checker source not found"
|
37
|
+
exit 47
|
38
|
+
fi
|
39
|
+
|
40
|
+
CHECKERHASH={{checkerHash}}
|
41
|
+
CHECKERPATH=/tmp/boca-chk/$CHECKERHASH
|
42
|
+
|
43
|
+
compile_checker() {
|
44
|
+
mkdir -p /tmp/boca-chk
|
45
|
+
|
46
|
+
cc=$(which g++)
|
47
|
+
[ -x "$cc" ] || cc=/usr/bin/g++
|
48
|
+
if [ ! -x "$cc" ]; then
|
49
|
+
echo "$cc not found or it's not executable"
|
50
|
+
exit 47
|
51
|
+
fi
|
52
|
+
|
53
|
+
$cc {{rbxFlags}} $CHECKERSOURCE -o $CHECKERPATH
|
54
|
+
|
55
|
+
chmod 0755 "$CHECKERPATH"
|
56
|
+
}
|
57
|
+
|
58
|
+
if [ ! -x "$CHECKERPATH" ]; then
|
59
|
+
compile_checker
|
60
|
+
fi
|
61
|
+
|
62
|
+
# Next lines of this script just compares team_output and sol_output,
|
63
|
+
# although it is possible to change them to more complex evaluations.
|
64
|
+
output=$($CHECKERPATH $3 $1 $2 2>&1 >/dev/null)
|
65
|
+
EC=$?
|
66
|
+
|
67
|
+
echo "checker exitcode = $EC"
|
68
|
+
echo "$output"
|
69
|
+
|
70
|
+
if [ $EC -eq 0 ]; then
|
71
|
+
echo "checker found no differences"
|
72
|
+
exit 4
|
73
|
+
elif [ $EC -eq 1 ]; then
|
74
|
+
echo "checker found differences"
|
75
|
+
exit 6
|
76
|
+
elif [ $EC -eq 2 ]; then
|
77
|
+
echo "checker failed"
|
78
|
+
exit 5
|
79
|
+
elif [ $EC -ne 3 ]; then
|
80
|
+
echo "unkown compare error $EC"
|
81
|
+
exit 43
|
82
|
+
fi
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/contest.rbx.tex
RENAMED
File without changes
|
File without changes
|
{rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/template.rbx.tex
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/projecao.png
RENAMED
File without changes
|
{rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/statement.rbx.tex
RENAMED
File without changes
|
{rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/template.rbx.tex
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|