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.
Files changed (179) hide show
  1. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/PKG-INFO +1 -1
  2. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/pyproject.toml +1 -1
  3. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/checkers.py +5 -1
  4. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/main.py +12 -0
  5. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/main.py +9 -0
  6. rbx_cp-0.5.40/rbx/box/packaging/moj/packager.py +125 -0
  7. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/schema.py +66 -0
  8. rbx_cp-0.5.40/rbx/box/unit.py +113 -0
  9. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/validators.py +17 -8
  10. rbx_cp-0.5.40/rbx/resources/packagers/moj/scripts/compare.sh +82 -0
  11. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/LICENSE +0 -0
  12. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/README.md +0 -0
  13. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/__init__.py +0 -0
  14. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/annotations.py +0 -0
  15. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/autoenum.py +0 -0
  16. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/__init__.py +0 -0
  17. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/builder.py +0 -0
  18. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/cd.py +0 -0
  19. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/code.py +0 -0
  20. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/compile.py +0 -0
  21. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/conftest.py +0 -0
  22. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/__init__.py +0 -0
  23. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/build_contest_statements.py +0 -0
  24. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/contest_package.py +0 -0
  25. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/contest_utils.py +0 -0
  26. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/main.py +0 -0
  27. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/schema.py +0 -0
  28. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/contest/statements.py +0 -0
  29. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/creation.py +0 -0
  30. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/deferred.py +0 -0
  31. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/download.py +0 -0
  32. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/environment.py +0 -0
  33. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/extensions.py +0 -0
  34. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/formatting.py +0 -0
  35. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/generators.py +0 -0
  36. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/generators_test.py +0 -0
  37. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/lazy_importing_main.py +0 -0
  38. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/lazy_importing_test.py +0 -0
  39. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/package.py +0 -0
  40. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/boca/extension.py +0 -0
  41. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/boca/packager.py +0 -0
  42. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/contest_main.py +0 -0
  43. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/packager.py +0 -0
  44. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/packager.py +0 -0
  45. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/test.py +0 -0
  46. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  47. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/__init__.py +0 -0
  48. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/fetch.py +0 -0
  49. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/lock_schema.py +0 -0
  50. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/presets/schema.py +0 -0
  51. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/retries.py +0 -0
  52. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/sanitizers/warning_stack.py +0 -0
  53. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/setter_config.py +0 -0
  54. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/solutions.py +0 -0
  55. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/solutions_test.py +0 -0
  56. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/state.py +0 -0
  57. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/__init__.py +0 -0
  58. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/build_statements.py +0 -0
  59. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/builders.py +0 -0
  60. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/joiners.py +0 -0
  61. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/latex.py +0 -0
  62. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/latex_jinja.py +0 -0
  63. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/statements/schema.py +0 -0
  64. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stresses.py +0 -0
  65. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/__init__.py +0 -0
  66. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/finder_parser.py +0 -0
  67. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/stressing/generator_parser.py +0 -0
  68. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcase_extractors.py +0 -0
  69. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcase_utils.py +0 -0
  70. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcases/__init__.py +0 -0
  71. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/testcases/main.py +0 -0
  72. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/__init__.py +0 -0
  73. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/captured_log.py +0 -0
  74. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/css/app.tcss +0 -0
  75. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/main.py +0 -0
  76. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/ui/run.py +0 -0
  77. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/box/validators_test.py +0 -0
  78. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/checker.py +0 -0
  79. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/clone.py +0 -0
  80. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/config.py +0 -0
  81. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/conftest.py +0 -0
  82. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/console.py +0 -0
  83. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/create.py +0 -0
  84. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/edit.py +0 -0
  85. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/__init__.py +0 -0
  86. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/caching.py +0 -0
  87. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/conftest.py +0 -0
  88. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/__init__.py +0 -0
  89. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/cacher.py +0 -0
  90. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/digester.py +0 -0
  91. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandbox.py +0 -0
  92. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  93. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  94. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  95. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  96. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/storage.py +0 -0
  97. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/test.py +0 -0
  98. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/judge/testiso.py +0 -0
  99. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps.py +0 -0
  100. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching.py +0 -0
  101. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading/steps_with_caching_run_test.py +0 -0
  102. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/grading_utils.py +0 -0
  103. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/hydration.py +0 -0
  104. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/main.py +0 -0
  105. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/metadata.py +0 -0
  106. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/__init__.py +0 -0
  107. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/codeforces.py +0 -0
  108. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/providers/provider.py +0 -0
  109. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/checkers/boilerplate.cpp +0 -0
  110. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_config.json +0 -0
  111. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.mac.yml +0 -0
  112. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/default_setter_config.yml +0 -0
  113. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/envs/default.rbx.yml +0 -0
  114. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/envs/isolate.rbx.yml +0 -0
  115. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/checker.sh +0 -0
  116. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compare +0 -0
  117. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/c +0 -0
  118. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cc +0 -0
  119. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/cpp +0 -0
  120. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/java +0 -0
  121. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/kt +0 -0
  122. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/pas +0 -0
  123. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py2 +0 -0
  124. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/compile/py3 +0 -0
  125. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/c +0 -0
  126. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cc +0 -0
  127. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/cpp +0 -0
  128. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/java +0 -0
  129. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/kt +0 -0
  130. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py2 +0 -0
  131. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/packagers/boca/run/py3 +0 -0
  132. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  133. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  134. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  135. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  136. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  137. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/.gitignore +0 -0
  138. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  139. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  140. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.py +0 -0
  141. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/random.txt +0 -0
  142. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  143. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  144. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  145. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  146. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  147. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  148. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  149. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  150. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  151. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  152. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  153. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/resources/templates/template.cpp +0 -0
  154. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/run.py +0 -0
  155. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/schema.py +0 -0
  156. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submit.py +0 -0
  157. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/__init__.py +0 -0
  158. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/codeforces.py +0 -0
  159. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/submitors/submitor.py +0 -0
  160. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/test.py +0 -0
  161. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testcase.py +0 -0
  162. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testcase_rendering.py +0 -0
  163. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/gen1.cpp +0 -0
  164. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/gen2.cpp +0 -0
  165. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/genScript.py +0 -0
  166. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/hard-tle.sol.cpp +0 -0
  167. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/ole.cpp +0 -0
  168. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/problem.rbx.yml +0 -0
  169. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/re.sol.cpp +0 -0
  170. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/sol.cpp +0 -0
  171. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tests/1.in +0 -0
  172. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tle-and-incorrect.sol.cpp +0 -0
  173. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/tle.sol.cpp +0 -0
  174. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/validator.cpp +0 -0
  175. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/box1/wa.sol.cpp +0 -0
  176. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/caching/executable.py +0 -0
  177. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testdata/compatible +0 -0
  178. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/testing_utils.py +0 -0
  179. {rbx_cp-0.5.39 → rbx_cp-0.5.40}/rbx/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.5.39
3
+ Version: 0.5.40
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9,<4.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.5.39"
3
+ version = "0.5.40"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -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
- validation_entries: List[GenerationTestcaseEntry],
180
+ validators: List[CodeItem],
181
181
  progress: Optional[StatusProgress] = None,
182
182
  ) -> Dict[str, str]:
183
- validators = []
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 = compile_validators(
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