P23-03: 生活の観察
This commit is contained in:
@@ -11,6 +11,8 @@ import hashlib
|
||||
import struct
|
||||
import zlib
|
||||
import tempfile
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
@@ -182,7 +184,12 @@ class Repair28CompositionTests(unittest.TestCase):
|
||||
raise AssertionError(args)
|
||||
|
||||
def verify(self):
|
||||
with mock.patch.object(Path, 'read_bytes', lambda p:self.files[self.t][str(p.relative_to(check.ROOT))]):
|
||||
original = Path.read_bytes
|
||||
def read(path):
|
||||
if path.is_relative_to(check.ROOT):
|
||||
return self.files[self.t][str(path.relative_to(check.ROOT))]
|
||||
return original(path)
|
||||
with mock.patch.object(Path, 'read_bytes', read):
|
||||
return check.verify_composition(check.ROOT,self.roles,self.source,self.inputs,self.git)
|
||||
|
||||
def test_valid_route_b_keeps_unchanged_allowed_path_from_main(self):
|
||||
@@ -245,6 +252,167 @@ class Repair28CompositionTests(unittest.TestCase):
|
||||
self.roles, self.source = original_roles, original_source
|
||||
|
||||
|
||||
class Repair30EntryTests(unittest.TestCase):
|
||||
"""Synthetic labelled Git history; zero native or OS credit."""
|
||||
def setUp(self):
|
||||
runtime = os.environ.get('TMPDIR')
|
||||
self.assertTrue(runtime, 'assigned TMPDIR required')
|
||||
self.temporary = tempfile.TemporaryDirectory(dir=runtime, prefix='p23-repair30-')
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.repo = Path(self.temporary.name)/'repo'
|
||||
self.repo.mkdir()
|
||||
self.build = Path(self.temporary.name)/'build'
|
||||
self.evidence = self.build/'p23-life-observation-evidence'
|
||||
self.evidence.mkdir(parents=True)
|
||||
self.git('init', '-q')
|
||||
self.git('config', 'user.name', 'Synthetic fixture')
|
||||
self.git('config', 'user.email', 'fixture@example.invalid')
|
||||
for path in ('tools/run_all_tests.py', 'tests/fixtures/p23-03/observation.gd',
|
||||
check.PLAN, check.SPEC, 'docs/traceability/original/BASELINE.txt'):
|
||||
self.write(path, (check.ROOT/path).read_bytes())
|
||||
for path in ('project.godot', 'export_presets.cfg', 'Makefile', 'AGENTS.md',
|
||||
'scenes/main/world_preview.gd', 'tests/business/test_required_kitchen.gd',
|
||||
'tools/regression/suite-policy.json'):
|
||||
self.write(path, 'synthetic baseline\n')
|
||||
self.marker = dict(schema_version=1, rows=[dict(test='history-'+str(i), required=True) for i in range(6)])
|
||||
self.write_marker(self.marker)
|
||||
self.base = self.commit('synthetic base')
|
||||
submitted = copy.deepcopy(self.marker)
|
||||
submitted['rows'].insert(0, dict(test='tests/ui/test_p23_life_observation.gd', required=True))
|
||||
self.write_marker(submitted)
|
||||
self.write('scenes/main/world_preview.gd', 'synthetic S\n')
|
||||
self.s = self.commit('synthetic S')
|
||||
self.git('checkout', '-q', '--detach', self.base)
|
||||
main = copy.deepcopy(self.marker)
|
||||
main['rows'].append(dict(test='P18-and-kitchen-QA', required=True))
|
||||
self.write_marker(main)
|
||||
self.write('tests/business/test_required_kitchen.gd', 'synthetic M QA\n')
|
||||
self.m = self.commit('synthetic M')
|
||||
self.t = self.git('merge-tree', '--write-tree', self.s, self.m).splitlines()[0]
|
||||
self.q = self.git('commit-tree', self.t, '-p', self.s, '-m', 'synthetic Q, no evidence')
|
||||
self.c = self.git('commit-tree', self.t, '-p', self.m, '-p', self.s, '-m', 'synthetic C')
|
||||
self.git('checkout', '-q', '--detach', self.q)
|
||||
runner = check.module('p23_synthetic_source_filter', check.ROOT/'tools/run_all_tests.py')
|
||||
sources = {p: row['sha256'] for p, row in runner.source_refs(self.repo).items()}
|
||||
inputs = check.input_map(self.repo)
|
||||
required = set(sources) | {p.split('#')[0] for p in inputs} | {check.PLAN, check.CONTRACT,
|
||||
'project.godot', 'tests/business/test_required_kitchen.gd', 'tools/regression/suite-policy.json'}
|
||||
self.roles = dict(submitted_sha=self.s, main_context_sha=self.m, tested_tree=self.t,
|
||||
tested_commit=self.q, blob_bindings={p:self.git('rev-parse', self.t+':'+p) for p in required})
|
||||
self.index = dict(schema='p23-life-observation-v2', source_map=sources, input_map=inputs,
|
||||
composition={k:self.roles[k] for k in ('submitted_sha','main_context_sha','tested_tree','tested_commit')},
|
||||
commit=self.q, spec_sha256=check.SPEC_SHA,
|
||||
marker_contract_sha256=check.sha(self.repo/check.CONTRACT), artifacts={}, phases=[])
|
||||
self.publish()
|
||||
self.git('checkout', '-q', '--detach', self.s)
|
||||
self.patch = mock.patch.object(check, 'BASE_COMMIT', self.base)
|
||||
self.patch.start()
|
||||
self.addCleanup(self.patch.stop)
|
||||
|
||||
def git(self, *args):
|
||||
result = subprocess.run(['git', '-C', str(self.repo), *args], capture_output=True, check=True)
|
||||
return result.stdout.decode().strip()
|
||||
|
||||
def write(self, path, content):
|
||||
target = self.repo/path
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_bytes(content.encode() if isinstance(content, str) else content)
|
||||
|
||||
def write_marker(self, value):
|
||||
self.write(check.CONTRACT, json.dumps(value, indent=2)+'\n')
|
||||
|
||||
def commit(self, message):
|
||||
self.git('add', '.')
|
||||
self.git('commit', '-q', '-m', message)
|
||||
return self.git('rev-parse', 'HEAD')
|
||||
|
||||
def publish(self):
|
||||
context = self.evidence/'root-composition.json'
|
||||
context.write_text(json.dumps(self.roles))
|
||||
self.index['root_composition_sha256'] = check.sha(context)
|
||||
(self.evidence/'index.json').write_text(json.dumps(self.index))
|
||||
|
||||
def cli(self, verify=True, extra_env=None, extra_args=()):
|
||||
env = dict(BUILD_OUTPUT_DIR=str(self.build))
|
||||
env.update(extra_env or {})
|
||||
with mock.patch.dict(os.environ, env, clear=True), contextlib.redirect_stdout(io.StringIO()) as output:
|
||||
code = check.main((['--verify-only'] if verify else []) + ['--project-root', str(self.repo)] + list(extra_args))
|
||||
self.assertEqual(code, 1)
|
||||
result = json.loads(output.getvalue())
|
||||
self.assertEqual(result['acceptance'], 'pending')
|
||||
return result['deficits']
|
||||
|
||||
def assert_entry(self, verify=True):
|
||||
deficits = self.cli(verify)
|
||||
self.assertTrue(any('native evidence deficit' in d for d in deficits), deficits)
|
||||
self.assertTrue(any('missing OS A-writer process receipt' in d for d in deficits), deficits)
|
||||
for condition in check.CONDITIONS:
|
||||
self.assertTrue(any(d.startswith(condition+':') for d in deficits), deficits)
|
||||
self.assertFalse(any('invalid evidence index' in d for d in deficits), deficits)
|
||||
|
||||
def test_normal_and_verify_only_S_Q_C_keep_all_seven_blockers(self):
|
||||
for head in (self.s, self.q, self.c):
|
||||
self.git('checkout', '-q', '--detach', head)
|
||||
for verify in (False, True):
|
||||
with self.subTest(head=head, verify=verify):
|
||||
self.assert_entry(verify)
|
||||
|
||||
def test_wrong_roles_and_missing_Q(self):
|
||||
for key, value in (('submitted_sha',self.base), ('main_context_sha',self.base),
|
||||
('tested_tree',self.git('rev-parse', self.base+'^{tree}')),
|
||||
('tested_commit','0'*40)):
|
||||
original = self.roles[key]
|
||||
self.roles[key] = value
|
||||
self.publish()
|
||||
with self.subTest(key=key):
|
||||
self.assertTrue(any('invalid evidence index' in d for d in self.cli()))
|
||||
self.roles[key] = original
|
||||
|
||||
def test_dirty_roles_and_source_QA_rollback(self):
|
||||
for head in (self.s,self.q,self.c):
|
||||
self.git('checkout', '-q', '--detach', head)
|
||||
for path in ('scenes/main/world_preview.gd','tests/business/test_required_kitchen.gd'):
|
||||
original = (self.repo/path).read_bytes()
|
||||
self.write(path, 'synthetic rollback\n')
|
||||
with self.subTest(head=head,path=path):
|
||||
self.assertTrue(any('dirty' in d for d in self.cli()))
|
||||
self.write(path, original)
|
||||
|
||||
def test_unexpected_C_parents_and_tree(self):
|
||||
wrong_tree = self.git('rev-parse',self.base+'^{tree}')
|
||||
for tree, parents in ((self.t,[self.s,self.m]), (self.t,[self.m,self.s,self.base]),
|
||||
(self.t,[self.m]), (wrong_tree,[self.m,self.s])):
|
||||
args = ['commit-tree', tree, '-m', 'synthetic invalid C']
|
||||
for parent in parents:
|
||||
args.extend(['-p',parent])
|
||||
head = self.git(*args)
|
||||
self.git('checkout','-q','--detach',head)
|
||||
with self.subTest(tree=tree,parents=parents):
|
||||
self.assertTrue(any('C parents' in d or 'C tree' in d for d in self.cli()))
|
||||
|
||||
def test_context_missing_mismatch_symlink_and_environment_ignored(self):
|
||||
context = self.evidence/'root-composition.json'
|
||||
original = context.read_bytes()
|
||||
context.unlink()
|
||||
self.assertTrue(any('context' in d for d in self.cli(extra_env={'P23_ROOT_COMPOSITION':str(self.repo/'unused')})))
|
||||
context.write_bytes(original+b' ')
|
||||
self.assertTrue(any('SHA mismatch' in d for d in self.cli()))
|
||||
context.unlink()
|
||||
target = Path(self.temporary.name)/'context.json'
|
||||
target.write_bytes(original)
|
||||
context.symlink_to(target)
|
||||
self.assertTrue(any('unsafe' in d for d in self.cli()))
|
||||
context.unlink()
|
||||
context.write_bytes(original)
|
||||
alternate = Path(self.temporary.name)/'alternate.json'
|
||||
alternate.write_text('{}')
|
||||
self.assertTrue(any('disagrees' in d for d in self.cli(extra_args=['--root-composition',str(alternate)])))
|
||||
self.assertTrue(any('native evidence deficit' in d for d in self.cli(extra_env={'P23_ROOT_COMPOSITION':str(alternate)})))
|
||||
self.index.pop('root_composition_sha256')
|
||||
(self.evidence/'index.json').write_text(json.dumps(self.index))
|
||||
self.assertTrue(any('SHA mismatch' in d for d in self.cli()))
|
||||
|
||||
|
||||
class ActorNonoverlapTests(unittest.TestCase):
|
||||
def report(self, second_at):
|
||||
return dict(actor_drawings=[
|
||||
@@ -600,7 +768,7 @@ class SnapshotCellTests(unittest.TestCase):
|
||||
class ImageTests(unittest.TestCase):
|
||||
def test_collector_conversion_is_bound_to_each_cua_screenshot(self):
|
||||
runner = check.module('p23_unit_collector', check.ROOT/'tools/run_p23_life_observation.py')
|
||||
with tempfile.TemporaryDirectory(dir=check.ROOT/'tests/fixtures/p23-03') as temporary:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
pair = root/'os-pair/A'
|
||||
request_dir = pair/'writer-request-0001'
|
||||
|
||||
Reference in New Issue
Block a user