P08-03: 配置の通常操作受入
This commit is contained in:
@@ -582,3 +582,13 @@ not claimed
|
|||||||
R09 history and pins remain unrewritten. Low findings (empty cell, audio, idempotency, English Runtime messages) remain deferred.
|
R09 history and pins remain unrewritten. Low findings (empty cell, audio, idempotency, English Runtime messages) remain deferred.
|
||||||
|
|
||||||
Repair18 local validation: python3 -B tools/test_p08_normal_placement_evidence.py ran 131 tests in 88.476s, exit 1: one existing R09 raw-proof read PermissionError; no weakening. py_compile initially failed on default cache permissions, then passed with PYTHONPYCACHEPREFIX inside allowed fixture scratch. JSON load and git diff --check passed. Scope adapter passed including boundary equality. Exact commands and source SHA256 are in implementation.json repair18. Root/Runner raw-proof access remains required.
|
Repair18 local validation: python3 -B tools/test_p08_normal_placement_evidence.py ran 131 tests in 88.476s, exit 1: one existing R09 raw-proof read PermissionError; no weakening. py_compile initially failed on default cache permissions, then passed with PYTHONPYCACHEPREFIX inside allowed fixture scratch. JSON load and git diff --check passed. Scope adapter passed including boundary equality. Exact commands and source SHA256 are in implementation.json repair18. Root/Runner raw-proof access remains required.
|
||||||
|
|
||||||
|
## Repair20 — submitted candidate
|
||||||
|
|
||||||
|
Frozen input f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49; registered validate failure f2d59df6a3c34c0e93b15b1a05cfa533. Historical pins selected the R09 route for fresh proof; the checker now selects from the explicit index commit, retaining R09 applicability and fresh HEAD/source/input checks. Both routes bind screenshot paths to the request; fresh proof uses strict PNG/JPEG magic dispatch and equal positive dimensions. Historical R09 pins, dimensions and runtime_preview.patch remain unchanged.
|
||||||
|
|
||||||
|
Root full176 was interrupted after 13 PASS, at phase013, guards 0: an interruption, not a product defect and not acceptance. No OS collection was started. New HEAD: to be bound by Runner. Conditions (1)-(5) remain pending Root native/OS.
|
||||||
|
|
||||||
|
Pending: Root exact-final local/native/checker applicability; one complete final full regression on the derived roster; fresh contiguous all-5 normal OS plus native collection; independent Opus review of all five conditions; integration/current final verification/ledger; Windows same-distribution/device, with no Mac substitution.
|
||||||
|
|
||||||
|
Local verification: final Python rerun 153 tests / 127.709s, 152 passed, one existing R09 original raw-proof PermissionError (exit 1); all 11 repair20 cases passed. py_compile failed on default external cache permission (exit 1). JSON load, diff whitespace and scope self-check passed. No test skipped or weakened; Root must rerun with required evidence/cache access.
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
"status": "submitted",
|
"status": "submitted",
|
||||||
"acceptance": "not claimed; all five conditions pending",
|
"acceptance": "not claimed; all five conditions pending",
|
||||||
"diagnosis": "R15 reveal published no geometry and verified neither visibility nor scroll direction; deferred quit allowed subsequent requests and secondary failures after a terminal ACK error.",
|
"diagnosis": "R15 reveal published no geometry and verified neither visibility nor scroll direction; deferred quit allowed subsequent requests and secondary failures after a terminal ACK error.",
|
||||||
"owner_rulings": ["Root31 ACK is invalid", "R15 earns no credit"],
|
"owner_rulings": [
|
||||||
|
"Root31 ACK is invalid",
|
||||||
|
"R15 earns no credit"
|
||||||
|
],
|
||||||
"source_map": {
|
"source_map": {
|
||||||
"tests/fixtures/p08-03/passive_placement.gd": {
|
"tests/fixtures/p08-03/passive_placement.gd": {
|
||||||
"at_M": "cdbd9a09b535d00a79aec7e2b7219f4daa4cf235449602b3cd02c6f12d9df88c",
|
"at_M": "cdbd9a09b535d00a79aec7e2b7219f4daa4cf235449602b3cd02c6f12d9df88c",
|
||||||
@@ -2037,5 +2040,51 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"context_gap": "R09 original raw-proof read access denied in this environment; Root/Runner must execute with required raw-proof access."
|
"context_gap": "R09 original raw-proof read access denied in this environment; Root/Runner must execute with required raw-proof access."
|
||||||
|
},
|
||||||
|
"repair20": {
|
||||||
|
"input": "f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49",
|
||||||
|
"registered_validate_failure": "f2d59df6a3c34c0e93b15b1a05cfa533",
|
||||||
|
"routing": "Historical pins incorrectly selected reuse for fresh HEAD. Route now follows explicit index.commit == R09_COMMIT; fresh uses current source and HEAD, never R09 functions or dimensions.",
|
||||||
|
"screenshots": "Both routes bind request paths; fresh dispatches strict PNG/JPEG by magic and checks equal positive dimensions.",
|
||||||
|
"full176": "Root interrupted full176: 13 PASS, phase013 interrupted, guards 0. Interruption, not a product defect or acceptance.",
|
||||||
|
"OS_collection": "No OS collection was started.",
|
||||||
|
"HEAD": "to be bound by Runner",
|
||||||
|
"conditions": "(1)-(5) pending Root native/OS; no acceptance claimed",
|
||||||
|
"pending": [
|
||||||
|
"Root exact-final local, native and checker applicability",
|
||||||
|
"one final complete full regression on derived roster",
|
||||||
|
"fresh contiguous all-5 normal OS plus native collection",
|
||||||
|
"independent Opus review of all 5 conditions",
|
||||||
|
"integration, current final verification and ledger",
|
||||||
|
"Windows same-distribution/device remains open; no Mac substitution"
|
||||||
|
],
|
||||||
|
"local_source_sha256": {
|
||||||
|
"tools/check_p08_normal_placement_evidence.py": "53acc2e29fc36f7c7e3b2a5f6d3f97ba935b9052e4b6e095b071ec240f4ff68a",
|
||||||
|
"tools/test_p08_normal_placement_evidence.py": "8a29e3b996e2f1d15c69283faeae54555654c3cf89df99d8ce7f9eed38cd5edc",
|
||||||
|
"tests/fixtures/p08-03/checker_fixture.py": "80b9ad74b04b8d235b3ff152ca2cba720085ec420c7dc7cbf07274d534f8fdfb"
|
||||||
|
},
|
||||||
|
"validation": [
|
||||||
|
{
|
||||||
|
"command": "python3 -B tools/test_p08_normal_placement_evidence.py",
|
||||||
|
"initial_result": "153 tests in 128.538s; one obsolete screenshot target failure corrected; one existing R09 raw-proof PermissionError. Final rerun pending.",
|
||||||
|
"result": "Final rerun exit 1: 153 tests in 127.709s; 152 passed, one existing R09 raw-proof PermissionError; no skips or weakened tests. All 11 repair20 cases passed."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "python3 -m py_compile tools/check_p08_normal_placement_evidence.py tools/test_p08_normal_placement_evidence.py tests/fixtures/p08-03/checker_fixture.py",
|
||||||
|
"result": "exit 1 on both attempts: PermissionError writing default external Python cache; no bypass"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "python3 -c 'import json; json.load(open(\"docs/plans/original-completion-2026-10-02/execution/p08-03/implementation.json\")); print(\"json.load: PASS\")'",
|
||||||
|
"result": "exit 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "git diff --check",
|
||||||
|
"result": "exit 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "scope self-check: git diff --name-only f5f85ae08c1c6c9c1ec511f13a7215170d7d8d49 plus git ls-files --others --exclude-standard; fnmatch against unchanged contract allowed_paths",
|
||||||
|
"result": "exit 0; final self-check found exactly five changed paths, no untracked files, all within unchanged allowed_paths"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -235,13 +235,16 @@ def make(base, source, checker):
|
|||||||
'events':[{'synthetic':True}]})
|
'events':[{'synthetic':True}]})
|
||||||
put(out/(prefix+'ack.json'), {'target_pid':pid,'sequence':sequence,
|
put(out/(prefix+'ack.json'), {'target_pid':pid,'sequence':sequence,
|
||||||
'request_sha256':sha(out/(prefix+'request.json')), 'input_receipt':receipt,
|
'request_sha256':sha(out/(prefix+'request.json')), 'input_receipt':receipt,
|
||||||
'screen_before':directory+'/screen.png','screen_after':directory+'/screen.png'})
|
'screen_before':prefix+'screen-before.png','screen_after':prefix+'screen-after.png'})
|
||||||
row['requests'].append(prefix+'ack.json')
|
row['requests'].append(prefix+'ack.json')
|
||||||
def chunk(kind, body):
|
def chunk(kind, body):
|
||||||
return struct.pack('>I',len(body))+kind+body+struct.pack('>I',zlib.crc32(kind+body))
|
return struct.pack('>I',len(body))+kind+body+struct.pack('>I',zlib.crc32(kind+body))
|
||||||
(folder/'screen.png').write_bytes(b'\x89PNG\r\n\x1a\n'+
|
screenshot = (b'\x89PNG\r\n\x1a\n'+
|
||||||
chunk(b'IHDR',struct.pack('>IIBBBBB',1,1,8,2,0,0,0))+
|
chunk(b'IHDR',struct.pack('>IIBBBBB',1,1,8,2,0,0,0))+
|
||||||
chunk(b'IDAT',zlib.compress(b'\0\0\0\0'))+chunk(b'IEND',b''))
|
chunk(b'IDAT',zlib.compress(b'\0\0\0\0'))+chunk(b'IEND',b''))
|
||||||
|
for ack_name in row['requests']:
|
||||||
|
for side in ('before', 'after'):
|
||||||
|
(out/Path(ack_name).parent/('screen-'+side+'.png')).write_bytes(screenshot)
|
||||||
|
|
||||||
actions = []
|
actions = []
|
||||||
for label in checker.WRITER:
|
for label in checker.WRITER:
|
||||||
|
|||||||
@@ -116,6 +116,9 @@ def expected_operation(formal, result, event_type):
|
|||||||
recent['items'] = recent['items'][-256:]
|
recent['items'] = recent['items'][-256:]
|
||||||
return expected
|
return expected
|
||||||
|
|
||||||
|
def r09_reuse(index):
|
||||||
|
return index.get('commit') == R09_COMMIT
|
||||||
|
|
||||||
def png(data):
|
def png(data):
|
||||||
require(data.startswith(b'\x89PNG\r\n\x1a\n'), 'missing OS screenshot')
|
require(data.startswith(b'\x89PNG\r\n\x1a\n'), 'missing OS screenshot')
|
||||||
offset = 8; chunks = []; payload = b''
|
offset = 8; chunks = []; payload = b''
|
||||||
@@ -135,6 +138,7 @@ def png(data):
|
|||||||
require(bool(zlib.decompress(payload)), 'empty OS screenshot')
|
require(bool(zlib.decompress(payload)), 'empty OS screenshot')
|
||||||
except zlib.error as exc:
|
except zlib.error as exc:
|
||||||
raise ValueError('invalid OS screenshot pixels') from exc
|
raise ValueError('invalid OS screenshot pixels') from exc
|
||||||
|
return struct.unpack('>II', data[16:24])
|
||||||
|
|
||||||
def input_map(root):
|
def input_map(root):
|
||||||
names = [SPEC, CONTRACT, 'AGENTS.md', RECORDER, RECORDER+'.uid',
|
names = [SPEC, CONTRACT, 'AGENTS.md', RECORDER, RECORDER+'.uid',
|
||||||
@@ -633,21 +637,25 @@ def check(project_root, evidence_dir):
|
|||||||
require(index['schema'] == 'p08-normal-placement-v1' and index['acceptance'] == 'pending', 'schema/acceptance')
|
require(index['schema'] == 'p08-normal-placement-v1' and index['acceptance'] == 'pending', 'schema/acceptance')
|
||||||
require(index['collection_error'] is None, 'collection failed')
|
require(index['collection_error'] is None, 'collection failed')
|
||||||
require(index['conditions'] == CONDITIONS, 'missing condition')
|
require(index['conditions'] == CONDITIONS, 'missing condition')
|
||||||
actual = {n: v['sha256'] for n, v in runner.source_refs(root).items()}
|
|
||||||
original = evidence
|
original = evidence
|
||||||
pins_path = root/R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json'
|
pins_path = root/R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json'
|
||||||
if pins_path.exists():
|
reuse = r09_reuse(index)
|
||||||
|
dimensions_path = root/R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json'
|
||||||
|
if reuse:
|
||||||
|
require(pins_path.is_file() and dimensions_path.is_file(), 'R09 reuse candidate')
|
||||||
r09_applicability(root, index)
|
r09_applicability(root, index)
|
||||||
r09_runner_binding(evidence, index, load(pins_path))
|
r09_runner_binding(evidence, index, load(pins_path))
|
||||||
original = r09_collection(evidence, index, load(pins_path))
|
original = r09_collection(evidence, index, load(pins_path))
|
||||||
r09_images(evidence, index, load(root/R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json'), load(pins_path))
|
r09_images(evidence, index, load(dimensions_path), load(pins_path))
|
||||||
actual = index['source_map']
|
actual = index['source_map']
|
||||||
|
else:
|
||||||
|
actual = {n: v['sha256'] for n, v in runner.source_refs(root).items()}
|
||||||
require(index['source_map'] == actual, 'stale source')
|
require(index['source_map'] == actual, 'stale source')
|
||||||
require(index['inputs'] == input_map(root), 'stale input')
|
require(index['inputs'] == input_map(root), 'stale input')
|
||||||
require(index['spec_sha256'] == SPEC_SHA == hashlib.sha256((root/SPEC).read_text().strip().encode()).hexdigest(), 'immutable spec')
|
require(index['spec_sha256'] == SPEC_SHA == hashlib.sha256((root/SPEC).read_text().strip().encode()).hexdigest(), 'immutable spec')
|
||||||
require(index['marker_contract_sha256'] == sha(root/CONTRACT), 'stale marker')
|
require(index['marker_contract_sha256'] == sha(root/CONTRACT), 'stale marker')
|
||||||
require(re.fullmatch('[0-9a-f]{40}', index['commit']) is not None, 'commit')
|
require(re.fullmatch('[0-9a-f]{40}', index['commit']) is not None, 'commit')
|
||||||
if not pins_path.exists():
|
if not reuse:
|
||||||
require(index['commit'] == subprocess.check_output(['git','-C',str(root),'rev-parse','HEAD'],text=True).strip(), 'stale commit')
|
require(index['commit'] == subprocess.check_output(['git','-C',str(root),'rev-parse','HEAD'],text=True).strip(), 'stale commit')
|
||||||
def artifact(name):
|
def artifact(name):
|
||||||
require(isinstance(name, str) and name and not Path(name).is_absolute() and '..' not in Path(name).parts, 'unsafe/missing artifact')
|
require(isinstance(name, str) and name and not Path(name).is_absolute() and '..' not in Path(name).parts, 'unsafe/missing artifact')
|
||||||
@@ -969,13 +977,20 @@ def check(project_root, evidence_dir):
|
|||||||
and raw['tool_name'] and raw['tool_call_id'], 'fake OS provenance')
|
and raw['tool_name'] and raw['tool_call_id'], 'fake OS provenance')
|
||||||
require(raw['events'] and observed['events'], 'missing OS events')
|
require(raw['events'] and observed['events'], 'missing OS events')
|
||||||
require(raw['collector_pid'] > 0 and raw['collector_pid'] not in os_pids, 'fake OS collector')
|
require(raw['collector_pid'] > 0 and raw['collector_pid'] not in os_pids, 'fake OS collector')
|
||||||
|
screen_dimensions = []
|
||||||
for key in ('screen_before', 'screen_after'):
|
for key in ('screen_before', 'screen_after'):
|
||||||
|
require(ack[key] == folder+key.replace('_', '-')+'.png', 'R09 screenshot request binding')
|
||||||
data = artifact(ack[key]).read_bytes()
|
data = artifact(ack[key]).read_bytes()
|
||||||
if pins_path.exists():
|
if reuse:
|
||||||
require(ack[key] == folder+key.replace('_', '-')+'.png', 'R09 screenshot request binding')
|
dimensions = jpeg_dimensions(data)
|
||||||
jpeg_dimensions(data)
|
elif data.startswith(b'\x89PNG\r\n\x1a\n'):
|
||||||
|
dimensions = png(data)
|
||||||
|
elif data.startswith(b'\xff\xd8'):
|
||||||
|
dimensions = jpeg_dimensions(data)
|
||||||
else:
|
else:
|
||||||
png(data)
|
raise ValueError('missing OS screenshot')
|
||||||
|
screen_dimensions.append(dimensions)
|
||||||
|
require(screen_dimensions[0] == screen_dimensions[1] and all(v > 0 for v in screen_dimensions[0]), 'OS screenshot dimension mismatch')
|
||||||
if request['action'] == 'cell' and 'position' in row:
|
if request['action'] == 'cell' and 'position' in row:
|
||||||
dispatch_gate(row['position'], observed['events'])
|
dispatch_gate(row['position'], observed['events'])
|
||||||
if row['id'] == 'pan_zoom':
|
if row['id'] == 'pan_zoom':
|
||||||
|
|||||||
@@ -598,7 +598,7 @@ class EvidenceTests(unittest.TestCase):
|
|||||||
self.reject('operation kind mismatch')
|
self.reject('operation kind mismatch')
|
||||||
|
|
||||||
def test_png_signature_is_not_a_screenshot(self):
|
def test_png_signature_is_not_a_screenshot(self):
|
||||||
(self.out/'os-pair/screen.png').write_bytes(b'\x89PNG\r\n\x1a\nFAKE')
|
next((self.out/'os-pair').rglob('screen-before.png')).write_bytes(b'\x89PNG\r\n\x1a\nFAKE')
|
||||||
F.repin(self.out)
|
F.repin(self.out)
|
||||||
self.reject('invalid OS screenshot')
|
self.reject('invalid OS screenshot')
|
||||||
|
|
||||||
@@ -722,6 +722,92 @@ def refresh_images(out, index, measurement):
|
|||||||
measurement['original_index_sha256'] = hashlib.sha256((out/'index.json').read_bytes()).hexdigest()
|
measurement['original_index_sha256'] = hashlib.sha256((out/'index.json').read_bytes()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
class FreshRoutingTests(unittest.TestCase):
|
||||||
|
setUp = EvidenceTests.setUp
|
||||||
|
edit = EvidenceTests.edit
|
||||||
|
reject = EvidenceTests.reject
|
||||||
|
# Reuse the whole synthetic proof, not a boolean checker substitute.
|
||||||
|
def historical(self):
|
||||||
|
F.put(self.root/C.R09_DOCS/'ROOT_NATIVE_R09_COLLECTION_PINS.json', {'synthetic': True})
|
||||||
|
|
||||||
|
def fresh_check(self):
|
||||||
|
commit = C.load(self.out/'index.json')['commit']
|
||||||
|
original_load = C.load
|
||||||
|
def guarded_load(path):
|
||||||
|
self.assertNotEqual(Path(path).name, 'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json')
|
||||||
|
return original_load(path)
|
||||||
|
with contextlib.ExitStack() as stack:
|
||||||
|
stack.enter_context(patch.object(C.subprocess, 'check_output', return_value=commit+'\n'))
|
||||||
|
stack.enter_context(patch.object(C, 'load', side_effect=guarded_load))
|
||||||
|
for name in ('r09_applicability', 'r09_runner_binding', 'r09_collection', 'r09_images'):
|
||||||
|
stack.enter_context(patch.object(C, name, side_effect=AssertionError('fresh used '+name)))
|
||||||
|
return C.check(self.root, self.out)
|
||||||
|
|
||||||
|
def test_historical_pins_fresh_positive(self):
|
||||||
|
self.historical()
|
||||||
|
self.assertEqual([], self.fresh_check())
|
||||||
|
|
||||||
|
def test_historical_pins_stale_commit(self):
|
||||||
|
self.historical()
|
||||||
|
self.edit('index.json', lambda d: d.update(commit='0'*40))
|
||||||
|
with patch.object(C.subprocess, 'check_output', return_value='1'*40+'\n'):
|
||||||
|
self.reject('stale commit')
|
||||||
|
|
||||||
|
def test_historical_pins_stale_source(self):
|
||||||
|
self.historical()
|
||||||
|
self.edit('index.json', lambda d: d['source_map'].update({'project.godot':'0'*64}))
|
||||||
|
self.assertIn('stale source', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
def test_historical_pins_stale_input(self):
|
||||||
|
self.historical()
|
||||||
|
self.edit('index.json', lambda d: d['inputs'].update({'AGENTS.md':'0'*64}))
|
||||||
|
self.assertIn('stale input', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
def test_reuse_missing_pins(self):
|
||||||
|
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
|
||||||
|
self.reject('R09 reuse candidate')
|
||||||
|
|
||||||
|
def test_reuse_wrong_applicability(self):
|
||||||
|
self.historical()
|
||||||
|
F.put(self.root/C.R09_DOCS/'ROOT_R09_READONLY_IMAGE_DIMENSIONS.json', {'synthetic':True})
|
||||||
|
self.edit('index.json', lambda d: d.update(commit=C.R09_COMMIT))
|
||||||
|
with patch.object(C.subprocess, 'check_output', return_value=C.R09_COMMIT+'\n'), patch.object(
|
||||||
|
C.subprocess, 'run', return_value=SimpleNamespace(returncode=1)):
|
||||||
|
self.reject('R09_COMMIT not an ancestor')
|
||||||
|
|
||||||
|
def screenshots(self, data):
|
||||||
|
for path in self.out.rglob('screen-*.png'):
|
||||||
|
path.write_bytes(data)
|
||||||
|
F.repin(self.out)
|
||||||
|
|
||||||
|
def test_fresh_jpeg_positive(self):
|
||||||
|
self.historical()
|
||||||
|
self.screenshots(negative_jpeg())
|
||||||
|
self.assertEqual([], self.fresh_check())
|
||||||
|
|
||||||
|
def test_fresh_truncated_jpeg(self):
|
||||||
|
self.screenshots(negative_jpeg()[:-2])
|
||||||
|
self.assertIn('invalid JPEG SOI/EOI', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
def test_fresh_garbage(self):
|
||||||
|
self.screenshots(b'garbage')
|
||||||
|
self.assertIn('missing OS screenshot', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
def test_fresh_dimension_mismatch(self):
|
||||||
|
self.screenshots(negative_jpeg())
|
||||||
|
next(self.out.rglob('screen-after.png')).write_bytes(negative_jpeg(width=640))
|
||||||
|
F.repin(self.out)
|
||||||
|
self.assertIn('OS screenshot dimension mismatch', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
def test_fresh_unbound_path(self):
|
||||||
|
ack = next(self.out.rglob('ack.json'))
|
||||||
|
data = C.load(ack)
|
||||||
|
data['screen_before'] = data['screen_after']
|
||||||
|
F.put(ack, data)
|
||||||
|
F.repin(self.out)
|
||||||
|
self.assertIn('R09 screenshot request binding', ' '.join(self.fresh_check()))
|
||||||
|
|
||||||
|
|
||||||
class R09ImageTests(unittest.TestCase):
|
class R09ImageTests(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.temp = tempfile.TemporaryDirectory(dir=ROOT/'tests/fixtures/p08-03/.scratch')
|
self.temp = tempfile.TemporaryDirectory(dir=ROOT/'tests/fixtures/p08-03/.scratch')
|
||||||
|
|||||||
Reference in New Issue
Block a user