rbx.cp 0.13.3__py3-none-any.whl → 0.13.5__py3-none-any.whl

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 (73) hide show
  1. rbx/annotations.py +5 -5
  2. rbx/box/checkers.py +26 -22
  3. rbx/box/cli.py +0 -4
  4. rbx/box/code.py +27 -80
  5. rbx/box/contest/build_contest_statements.py +16 -3
  6. rbx/box/contest/schema.py +1 -2
  7. rbx/box/environment.py +16 -6
  8. rbx/box/fields.py +25 -1
  9. rbx/box/generators.py +31 -5
  10. rbx/box/global_package.py +6 -2
  11. rbx/box/header.py +31 -11
  12. rbx/box/package.py +3 -15
  13. rbx/box/presets/__init__.py +2 -2
  14. rbx/box/schema.py +4 -25
  15. rbx/box/setter_config.py +11 -0
  16. rbx/box/solutions.py +12 -4
  17. rbx/box/statements/build_statements.py +5 -1
  18. rbx/box/statements/builders.py +7 -7
  19. rbx/box/statements/schema.py +11 -2
  20. rbx/box/tasks.py +9 -4
  21. rbx/box/testcase_utils.py +2 -0
  22. rbx/box/testing/__init__.py +0 -0
  23. rbx/box/testing/testing_package.py +246 -0
  24. rbx/box/testing/testing_preset.py +36 -0
  25. rbx/box/testing/testing_shared.py +81 -0
  26. rbx/box/ui/screens/run_explorer.py +0 -8
  27. rbx/box/ui/utils/run_ui.py +7 -3
  28. rbx/box/ui/widgets/test_output_box.py +1 -1
  29. rbx/box/validators.py +5 -2
  30. rbx/grading/caching.py +67 -16
  31. rbx/grading/judge/program.py +268 -0
  32. rbx/grading/judge/sandbox.py +30 -193
  33. rbx/grading/judge/sandboxes/stupid_sandbox.py +232 -241
  34. rbx/grading/judge/sandboxes/tee.py +31 -0
  35. rbx/grading/steps.py +87 -199
  36. rbx/grading/steps_with_caching.py +15 -6
  37. rbx/resources/presets/default/problem/problem.rbx.yml +0 -2
  38. rbx/resources/presets/default/shared/contest_template.rbx.tex +1 -1
  39. rbx/resources/presets/default/shared/problem_template.rbx.tex +5 -1
  40. rbx/resources/templates/rbx.h +43 -2
  41. rbx/testing_utils.py +8 -1
  42. rbx/utils.py +59 -1
  43. {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/METADATA +2 -1
  44. {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/RECORD +47 -67
  45. rbx/box/conftest.py +0 -42
  46. rbx/box/generators_test.py +0 -67
  47. rbx/box/lazy_importing_test.py +0 -25
  48. rbx/box/solutions_test.py +0 -47
  49. rbx/box/validators_test.py +0 -15
  50. rbx/checker.py +0 -128
  51. rbx/clone.py +0 -197
  52. rbx/conftest.py +0 -38
  53. rbx/create.py +0 -37
  54. rbx/edit.py +0 -24
  55. rbx/grading/conftest.py +0 -33
  56. rbx/grading/judge/sandboxes/isolate.py +0 -695
  57. rbx/grading/judge/testiso.py +0 -54
  58. rbx/grading/steps_with_caching_run_test.py +0 -707
  59. rbx/grading_utils.py +0 -148
  60. rbx/hydration.py +0 -101
  61. rbx/main.py +0 -118
  62. rbx/metadata.py +0 -105
  63. rbx/resources/envs/isolate.rbx.yml +0 -36
  64. rbx/resources/presets/default/problem/sols/slow.cpp +0 -15
  65. rbx/run.py +0 -45
  66. rbx/schema.py +0 -64
  67. rbx/submit.py +0 -61
  68. rbx/test.py +0 -349
  69. rbx/testcase.py +0 -70
  70. rbx/testcase_rendering.py +0 -79
  71. {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/LICENSE +0 -0
  72. {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/WHEEL +0 -0
  73. {rbx_cp-0.13.3.dist-info → rbx_cp-0.13.5.dist-info}/entry_points.txt +0 -0
@@ -1,707 +0,0 @@
1
- import os
2
- import pathlib
3
- import sys
4
-
5
- import pytest
6
-
7
- from rbx.grading import grading_context, steps_with_caching
8
- from rbx.grading.caching import DependencyCache
9
- from rbx.grading.judge.cacher import FileCacher
10
- from rbx.grading.judge.digester import digest_cooperatively
11
- from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
12
- from rbx.grading.steps import (
13
- DigestOrSource,
14
- GradingArtifacts,
15
- GradingFileInput,
16
- GradingFileOutput,
17
- RunLogMetadata,
18
- )
19
-
20
-
21
- async def test_run_from_digest(
22
- cleandir: pathlib.Path,
23
- dependency_cache: DependencyCache,
24
- sandbox: SandboxBase,
25
- file_cacher: FileCacher,
26
- ):
27
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
28
- artifacts = GradingArtifacts()
29
- artifacts.inputs.append(
30
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
31
- )
32
- artifacts.outputs.append(
33
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=pathlib.Path('out.txt'))
34
- )
35
- await steps_with_caching.run(
36
- f'{sys.executable} executable.py',
37
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
38
- sandbox=sandbox,
39
- artifacts=artifacts,
40
- dependency_cache=dependency_cache,
41
- metadata=RunLogMetadata(),
42
- )
43
-
44
- assert (cleandir / 'out.txt').read_text().strip() == '5'
45
- assert artifacts.logs is not None
46
- assert artifacts.logs.run is not None
47
- assert artifacts.logs.run.metadata is not None
48
- assert not artifacts.logs.cached
49
-
50
-
51
- async def test_run_from_disk(
52
- cleandir: pathlib.Path,
53
- dependency_cache: DependencyCache,
54
- sandbox: SandboxBase,
55
- ):
56
- pathlib.Path('executable.py').write_text('print(42)')
57
-
58
- executable = DigestOrSource.create(pathlib.Path('executable.py'))
59
- artifacts = GradingArtifacts()
60
- artifacts.inputs.append(
61
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
62
- )
63
- artifacts.outputs.append(
64
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=pathlib.Path('out.txt'))
65
- )
66
- await steps_with_caching.run(
67
- f'{sys.executable} executable.py',
68
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
69
- sandbox=sandbox,
70
- artifacts=artifacts,
71
- dependency_cache=dependency_cache,
72
- )
73
-
74
- assert (cleandir / 'out.txt').read_text().strip() == '42'
75
- assert artifacts.logs is not None
76
- assert artifacts.logs.run is not None
77
- assert not artifacts.logs.cached
78
-
79
-
80
- async def test_run_reruns_if_cache_disabled(
81
- cleandir: pathlib.Path,
82
- dependency_cache: DependencyCache,
83
- sandbox: SandboxBase,
84
- file_cacher: FileCacher,
85
- ):
86
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
87
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
88
- artifacts = GradingArtifacts()
89
- artifacts.inputs.append(
90
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
91
- )
92
- artifacts.outputs.append(
93
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
94
- )
95
- with grading_context.cache_level(grading_context.CacheLevel.NO_CACHE):
96
- await steps_with_caching.run(
97
- f'{sys.executable} executable.py',
98
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
99
- sandbox=sandbox,
100
- artifacts=artifacts,
101
- dependency_cache=dependency_cache,
102
- )
103
- return artifacts
104
-
105
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
106
- assert (cleandir / 'out.txt').read_text().strip() == '5'
107
- assert artifacts.logs is not None
108
- assert not artifacts.logs.cached
109
-
110
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
111
- assert (cleandir / 'out.txt').read_text().strip() == '5'
112
- assert another_artifacts.logs is not None
113
- assert not another_artifacts.logs.cached
114
-
115
-
116
- async def test_run_reruns_if_first_run_cache_disabled(
117
- cleandir: pathlib.Path,
118
- dependency_cache: DependencyCache,
119
- sandbox: SandboxBase,
120
- file_cacher: FileCacher,
121
- ):
122
- async def configure_and_run_with_dest(
123
- dest: pathlib.Path, cache: bool = True
124
- ) -> GradingArtifacts:
125
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
126
- artifacts = GradingArtifacts()
127
- artifacts.inputs.append(
128
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
129
- )
130
- artifacts.outputs.append(
131
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
132
- )
133
- with grading_context.cache_level(
134
- grading_context.CacheLevel.NO_CACHE, when=cache
135
- ):
136
- await steps_with_caching.run(
137
- f'{sys.executable} executable.py',
138
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
139
- sandbox=sandbox,
140
- artifacts=artifacts,
141
- dependency_cache=dependency_cache,
142
- )
143
- return artifacts
144
-
145
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'), cache=False)
146
- assert (cleandir / 'out.txt').read_text().strip() == '5'
147
- assert artifacts.logs is not None
148
- assert not artifacts.logs.cached
149
-
150
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
151
- assert (cleandir / 'out.txt').read_text().strip() == '5'
152
- assert another_artifacts.logs is not None
153
- assert not another_artifacts.logs.cached
154
-
155
-
156
- async def test_run_reruns_if_second_run_cache_disabled(
157
- cleandir: pathlib.Path,
158
- dependency_cache: DependencyCache,
159
- sandbox: SandboxBase,
160
- file_cacher: FileCacher,
161
- ):
162
- async def configure_and_run_with_dest(
163
- dest: pathlib.Path, cache: bool = True
164
- ) -> GradingArtifacts:
165
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
166
- artifacts = GradingArtifacts()
167
- artifacts.inputs.append(
168
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
169
- )
170
- artifacts.outputs.append(
171
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
172
- )
173
- with grading_context.cache_level(
174
- grading_context.CacheLevel.NO_CACHE, when=cache
175
- ):
176
- await steps_with_caching.run(
177
- f'{sys.executable} executable.py',
178
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
179
- sandbox=sandbox,
180
- artifacts=artifacts,
181
- dependency_cache=dependency_cache,
182
- )
183
- return artifacts
184
-
185
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
186
- assert (cleandir / 'out.txt').read_text().strip() == '5'
187
- assert artifacts.logs is not None
188
- assert not artifacts.logs.cached
189
-
190
- another_artifacts = await configure_and_run_with_dest(
191
- pathlib.Path('out.txt'), cache=False
192
- )
193
- assert (cleandir / 'out.txt').read_text().strip() == '5'
194
- assert another_artifacts.logs is not None
195
- assert not another_artifacts.logs.cached
196
-
197
-
198
- async def test_run_caches_intermediate_digest_if_dest_changes(
199
- cleandir: pathlib.Path,
200
- dependency_cache: DependencyCache,
201
- sandbox: SandboxBase,
202
- file_cacher: FileCacher,
203
- ):
204
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
205
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
206
- artifacts = GradingArtifacts()
207
- artifacts.inputs.append(
208
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
209
- )
210
- artifacts.outputs.append(
211
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
212
- )
213
- await steps_with_caching.run(
214
- f'{sys.executable} executable.py',
215
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
216
- sandbox=sandbox,
217
- artifacts=artifacts,
218
- dependency_cache=dependency_cache,
219
- )
220
- return artifacts
221
-
222
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
223
- assert (cleandir / 'out.txt').read_text().strip() == '5'
224
- assert artifacts.logs is not None
225
- assert not artifacts.logs.cached
226
-
227
- another_artifacts = await configure_and_run_with_dest(
228
- pathlib.Path('another-out.txt')
229
- )
230
- assert (cleandir / 'another-out.txt').read_text().strip() == '5'
231
- assert another_artifacts.logs is not None
232
- assert another_artifacts.logs.cached
233
-
234
-
235
- async def test_run_caches_intermediate_digest_if_dest_changes_and_cache_transient(
236
- cleandir: pathlib.Path,
237
- dependency_cache: DependencyCache,
238
- sandbox: SandboxBase,
239
- file_cacher: FileCacher,
240
- ):
241
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
242
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
243
- artifacts = GradingArtifacts()
244
- artifacts.inputs.append(
245
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
246
- )
247
- artifacts.outputs.append(
248
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
249
- )
250
-
251
- with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
252
- await steps_with_caching.run(
253
- f'{sys.executable} executable.py',
254
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
255
- sandbox=sandbox,
256
- artifacts=artifacts,
257
- dependency_cache=dependency_cache,
258
- )
259
- return artifacts
260
-
261
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
262
- assert (cleandir / 'out.txt').read_text().strip() == '5'
263
- assert artifacts.logs is not None
264
- assert not artifacts.logs.cached
265
-
266
- another_artifacts = await configure_and_run_with_dest(
267
- pathlib.Path('another-out.txt')
268
- )
269
- assert (cleandir / 'another-out.txt').read_text().strip() == '5'
270
- assert another_artifacts.logs is not None
271
- assert another_artifacts.logs.cached
272
-
273
-
274
- async def test_run_overwrites_changed_file_when_storage_value_is_changed_and_cache_transient(
275
- cleandir: pathlib.Path,
276
- dependency_cache: DependencyCache,
277
- sandbox: SandboxBase,
278
- file_cacher: FileCacher,
279
- ):
280
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
281
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
282
- artifacts = GradingArtifacts()
283
- artifacts.inputs.append(
284
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
285
- )
286
- artifacts.outputs.append(
287
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
288
- )
289
- with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
290
- await steps_with_caching.run(
291
- f'{sys.executable} executable.py',
292
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
293
- sandbox=sandbox,
294
- artifacts=artifacts,
295
- dependency_cache=dependency_cache,
296
- )
297
- return artifacts
298
-
299
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
300
- assert (cleandir / 'out.txt').read_text().strip() == '5'
301
- assert artifacts.logs is not None
302
- assert not artifacts.logs.cached
303
-
304
- pathlib.Path('out.txt').write_text('42')
305
-
306
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
307
- assert (cleandir / 'out.txt').read_text().strip() == '5'
308
- assert another_artifacts.logs is not None
309
- assert another_artifacts.logs.cached
310
-
311
-
312
- async def test_run_overwrites_changed_file_when_file_deleted_and_cache_transient(
313
- cleandir: pathlib.Path,
314
- dependency_cache: DependencyCache,
315
- sandbox: SandboxBase,
316
- file_cacher: FileCacher,
317
- ):
318
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
319
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
320
- artifacts = GradingArtifacts()
321
- artifacts.inputs.append(
322
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
323
- )
324
- artifacts.outputs.append(
325
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
326
- )
327
- with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
328
- await steps_with_caching.run(
329
- f'{sys.executable} executable.py',
330
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
331
- sandbox=sandbox,
332
- artifacts=artifacts,
333
- dependency_cache=dependency_cache,
334
- )
335
- return artifacts
336
-
337
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
338
- assert (cleandir / 'out.txt').read_text().strip() == '5'
339
- assert artifacts.logs is not None
340
- assert not artifacts.logs.cached
341
-
342
- pathlib.Path('out.txt').unlink()
343
-
344
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
345
- assert (cleandir / 'out.txt').read_text().strip() == '5'
346
- assert another_artifacts.logs is not None
347
- assert another_artifacts.logs.cached
348
-
349
-
350
- async def test_run_fails_when_storage_value_is_changed_and_integrity_check_is_enabled(
351
- cleandir: pathlib.Path,
352
- dependency_cache: DependencyCache,
353
- sandbox: SandboxBase,
354
- file_cacher: FileCacher,
355
- ):
356
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
357
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
358
- artifacts = GradingArtifacts()
359
- artifacts.inputs.append(
360
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
361
- )
362
- artifacts.outputs.append(
363
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
364
- )
365
- await steps_with_caching.run(
366
- f'{sys.executable} executable.py',
367
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
368
- sandbox=sandbox,
369
- artifacts=artifacts,
370
- dependency_cache=dependency_cache,
371
- )
372
- return artifacts
373
-
374
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
375
- assert (cleandir / 'out.txt').read_text().strip() == '5'
376
- assert artifacts.logs is not None
377
- assert not artifacts.logs.cached
378
-
379
- pathlib.Path('out.txt').write_text('42')
380
-
381
- with pytest.raises(ValueError) as exc_info:
382
- await configure_and_run_with_dest(pathlib.Path('out.txt'))
383
- assert 'rbx clean' in str(exc_info.value)
384
- assert (cleandir / 'out.txt').read_text().strip() == '42'
385
-
386
-
387
- async def test_run_evicts_and_recreates_deleted_file_with_storage_value(
388
- cleandir: pathlib.Path,
389
- dependency_cache: DependencyCache,
390
- sandbox: SandboxBase,
391
- file_cacher: FileCacher,
392
- ):
393
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
394
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
395
- artifacts = GradingArtifacts()
396
- artifacts.inputs.append(
397
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
398
- )
399
- artifacts.outputs.append(
400
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
401
- )
402
- await steps_with_caching.run(
403
- f'{sys.executable} executable.py',
404
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
405
- sandbox=sandbox,
406
- artifacts=artifacts,
407
- dependency_cache=dependency_cache,
408
- )
409
- return artifacts
410
-
411
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
412
- assert (cleandir / 'out.txt').read_text().strip() == '5'
413
- assert artifacts.logs is not None
414
- assert not artifacts.logs.cached
415
-
416
- pathlib.Path('out.txt').unlink()
417
-
418
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
419
- assert (cleandir / 'out.txt').read_text().strip() == '5'
420
- assert another_artifacts.logs is not None
421
- assert another_artifacts.logs.cached
422
-
423
-
424
- async def test_run_evicts_when_storage_value_deleted(
425
- cleandir: pathlib.Path,
426
- dependency_cache: DependencyCache,
427
- sandbox: SandboxBase,
428
- file_cacher: FileCacher,
429
- ):
430
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
431
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
432
- artifacts = GradingArtifacts()
433
- artifacts.inputs.append(
434
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
435
- )
436
- artifacts.outputs.append(
437
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
438
- )
439
- await steps_with_caching.run(
440
- f'{sys.executable} executable.py',
441
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
442
- sandbox=sandbox,
443
- artifacts=artifacts,
444
- dependency_cache=dependency_cache,
445
- )
446
- return artifacts
447
-
448
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
449
- assert (cleandir / 'out.txt').read_text().strip() == '5'
450
- assert artifacts.logs is not None
451
- assert not artifacts.logs.cached
452
-
453
- # Delete the file from the cache
454
- with pathlib.Path('out.txt').open('rb') as f:
455
- digest = digest_cooperatively(f)
456
- file_cacher.delete(digest)
457
-
458
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
459
- assert (cleandir / 'out.txt').read_text().strip() == '5'
460
- assert another_artifacts.logs is not None
461
- assert not another_artifacts.logs.cached
462
-
463
-
464
- async def test_run_overwrite_exec_bit_when_changed(
465
- cleandir: pathlib.Path,
466
- dependency_cache: DependencyCache,
467
- sandbox: SandboxBase,
468
- file_cacher: FileCacher,
469
- ):
470
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
471
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
472
- artifacts = GradingArtifacts()
473
- artifacts.inputs.append(
474
- GradingFileInput(
475
- **executable.expand(),
476
- dest=pathlib.Path('executable.py'),
477
- )
478
- )
479
- artifacts.outputs.append(
480
- GradingFileOutput(
481
- src=pathlib.Path('box-out.txt'), dest=dest, executable=True
482
- )
483
- )
484
- await steps_with_caching.run(
485
- f'{sys.executable} executable.py',
486
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
487
- sandbox=sandbox,
488
- artifacts=artifacts,
489
- dependency_cache=dependency_cache,
490
- )
491
- return artifacts
492
-
493
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
494
- assert (cleandir / 'out.txt').read_text().strip() == '5'
495
- assert artifacts.logs is not None
496
- assert not artifacts.logs.cached
497
- assert os.access('out.txt', os.X_OK)
498
-
499
- pathlib.Path('out.txt').chmod(0o644)
500
-
501
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
502
- assert (cleandir / 'out.txt').read_text().strip() == '5'
503
- assert another_artifacts.logs is not None
504
- assert another_artifacts.logs.cached
505
- assert os.access('out.txt', os.X_OK)
506
-
507
-
508
- async def test_run_evicts_when_changed_file_and_no_hash(
509
- cleandir: pathlib.Path,
510
- dependency_cache: DependencyCache,
511
- sandbox: SandboxBase,
512
- file_cacher: FileCacher,
513
- ):
514
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
515
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
516
- artifacts = GradingArtifacts()
517
- artifacts.inputs.append(
518
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
519
- )
520
- artifacts.outputs.append(
521
- GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest, hash=False)
522
- )
523
- await steps_with_caching.run(
524
- f'{sys.executable} executable.py',
525
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
526
- sandbox=sandbox,
527
- artifacts=artifacts,
528
- dependency_cache=dependency_cache,
529
- )
530
- return artifacts
531
-
532
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
533
- assert (cleandir / 'out.txt').read_text().strip() == '5'
534
- assert artifacts.logs is not None
535
- assert not artifacts.logs.cached
536
-
537
- pathlib.Path('out.txt').write_text('42')
538
-
539
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
540
- assert (cleandir / 'out.txt').read_text().strip() == '5'
541
- assert another_artifacts.logs is not None
542
- assert not another_artifacts.logs.cached
543
-
544
-
545
- async def test_run_evicts_when_exec_bit_different_and_no_hash(
546
- cleandir: pathlib.Path,
547
- dependency_cache: DependencyCache,
548
- sandbox: SandboxBase,
549
- file_cacher: FileCacher,
550
- ):
551
- async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
552
- executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
553
- artifacts = GradingArtifacts()
554
- artifacts.inputs.append(
555
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
556
- )
557
- artifacts.outputs.append(
558
- GradingFileOutput(
559
- src=pathlib.Path('box-out.txt'), dest=dest, hash=False, executable=True
560
- )
561
- )
562
- await steps_with_caching.run(
563
- f'{sys.executable} executable.py',
564
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
565
- sandbox=sandbox,
566
- artifacts=artifacts,
567
- dependency_cache=dependency_cache,
568
- )
569
- return artifacts
570
-
571
- artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
572
- assert (cleandir / 'out.txt').read_text().strip() == '5'
573
- assert artifacts.logs is not None
574
- assert not artifacts.logs.cached
575
-
576
- pathlib.Path('out.txt').chmod(0o0644)
577
-
578
- another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
579
- assert (cleandir / 'out.txt').read_text().strip() == '5'
580
- assert another_artifacts.logs is not None
581
- assert not another_artifacts.logs.cached
582
-
583
-
584
- async def test_run_evicts_when_input_fingerprint_changes(
585
- cleandir: pathlib.Path,
586
- dependency_cache: DependencyCache,
587
- sandbox: SandboxBase,
588
- ):
589
- async def configure_and_run() -> GradingArtifacts:
590
- executable = DigestOrSource.create(pathlib.Path('executable.py'))
591
- artifacts = GradingArtifacts()
592
- artifacts.inputs.append(
593
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
594
- )
595
- artifacts.outputs.append(
596
- GradingFileOutput(
597
- src=pathlib.Path('box-out.txt'),
598
- dest=pathlib.Path('out.txt'),
599
- )
600
- )
601
- await steps_with_caching.run(
602
- f'{sys.executable} executable.py',
603
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
604
- sandbox=sandbox,
605
- artifacts=artifacts,
606
- dependency_cache=dependency_cache,
607
- )
608
- return artifacts
609
-
610
- pathlib.Path('executable.py').write_text('print(5)')
611
-
612
- artifacts = await configure_and_run()
613
- assert (cleandir / 'out.txt').read_text().strip() == '5'
614
- assert artifacts.logs is not None
615
- assert not artifacts.logs.cached
616
-
617
- pathlib.Path('executable.py').write_text('print(42)')
618
-
619
- another_artifacts = await configure_and_run()
620
- assert (cleandir / 'out.txt').read_text().strip() == '42'
621
- assert another_artifacts.logs is not None
622
- assert not another_artifacts.logs.cached
623
-
624
-
625
- async def test_run_evicts_when_output_is_deleted_and_no_hash(
626
- cleandir: pathlib.Path,
627
- dependency_cache: DependencyCache,
628
- sandbox: SandboxBase,
629
- ):
630
- async def configure_and_run() -> GradingArtifacts:
631
- executable = DigestOrSource.create(pathlib.Path('executable.py'))
632
- artifacts = GradingArtifacts()
633
- artifacts.inputs.append(
634
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
635
- )
636
- artifacts.outputs.append(
637
- GradingFileOutput(
638
- src=pathlib.Path('box-out.txt'),
639
- dest=pathlib.Path('out.txt'),
640
- hash=False,
641
- )
642
- )
643
- await steps_with_caching.run(
644
- f'{sys.executable} executable.py',
645
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
646
- sandbox=sandbox,
647
- artifacts=artifacts,
648
- dependency_cache=dependency_cache,
649
- )
650
- return artifacts
651
-
652
- pathlib.Path('executable.py').write_text('print(5)')
653
-
654
- artifacts = await configure_and_run()
655
- assert (cleandir / 'out.txt').read_text().strip() == '5'
656
- assert artifacts.logs is not None
657
- assert not artifacts.logs.cached
658
-
659
- pathlib.Path('out.txt').unlink()
660
-
661
- another_artifacts = await configure_and_run()
662
- assert (cleandir / 'out.txt').read_text().strip() == '5'
663
- assert another_artifacts.logs is not None
664
- assert not another_artifacts.logs.cached
665
-
666
-
667
- async def test_run_misses_when_input_file_changes(
668
- cleandir: pathlib.Path,
669
- dependency_cache: DependencyCache,
670
- sandbox: SandboxBase,
671
- file_cacher: FileCacher,
672
- ):
673
- async def configure_and_run(number: int) -> GradingArtifacts:
674
- executable = DigestOrSource.create(
675
- file_cacher.put_file_text(f'print({number})')
676
- )
677
- artifacts = GradingArtifacts()
678
- artifacts.inputs.append(
679
- GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
680
- )
681
- artifacts.outputs.append(
682
- GradingFileOutput(
683
- src=pathlib.Path('box-out.txt'),
684
- dest=pathlib.Path('out.txt'),
685
- hash=False,
686
- )
687
- )
688
- await steps_with_caching.run(
689
- f'{sys.executable} executable.py',
690
- params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
691
- sandbox=sandbox,
692
- artifacts=artifacts,
693
- dependency_cache=dependency_cache,
694
- )
695
- return artifacts
696
-
697
- artifacts = await configure_and_run(5)
698
- assert (cleandir / 'out.txt').read_text().strip() == '5'
699
- assert artifacts.logs is not None
700
- assert not artifacts.logs.cached
701
-
702
- pathlib.Path('out.txt').write_text('42')
703
-
704
- another_artifacts = await configure_and_run(42)
705
- assert (cleandir / 'out.txt').read_text().strip() == '42'
706
- assert another_artifacts.logs is not None
707
- assert not another_artifacts.logs.cached