Prepare independent successor playtest with spawn recovery and casualty feedback
This commit is contained in:
@@ -47,6 +47,64 @@ class ChoiceScene(Scene):
|
||||
|
||||
|
||||
class SelectionTests(unittest.TestCase):
|
||||
def aligned_pair(self, cls=ChoiceScene):
|
||||
scene=cls();scene.guard_cost=0
|
||||
scene.angle=math.degrees(math.atan2(scene.geometry['context'][0]-scene.camera[0],scene.geometry['context'][2]-scene.camera[2]))
|
||||
scene.snapshot=scene.make();scene.history=[copy.deepcopy(scene.snapshot)]
|
||||
scene.sleep(.1)
|
||||
return scene
|
||||
|
||||
def test_facing_commits_existing_fresh_pair_without_second_drain(self):
|
||||
scene=self.aligned_pair();reads=[];original=scene.observe
|
||||
def observe():reads.append(scene.clock());return original()
|
||||
scene.observe=observe
|
||||
result=scene.facing()
|
||||
self.assertTrue(result['completed']);self.assertEqual(len(reads),1)
|
||||
self.assertEqual(result['facing_samples'],[310,311])
|
||||
self.assertEqual([x['sample'] for x in result['samples']],[310,311])
|
||||
self.assertEqual(result['final_sample'],311)
|
||||
self.assertEqual(result['deadline_monotonic'],39)
|
||||
self.assertEqual(scene.motions,[]);self.assertEqual(scene.wheels,[])
|
||||
# The existing strict alignment path still performs its original reread.
|
||||
strict=self.aligned_pair(Scene);strict_reads=[];original=strict.observe
|
||||
def strict_observe():strict_reads.append(strict.clock());return original()
|
||||
strict.observe=strict_observe
|
||||
self.assertTrue(strict.run()['completed']);self.assertEqual(len(strict_reads),2)
|
||||
|
||||
def test_final_facing_idle_freshness_clock_and_deadline_rechecked(self):
|
||||
for fault in ('not-idle','stale','clock-reversed','deadline'):
|
||||
scene=self.aligned_pair();calls=[]
|
||||
if fault=='deadline':scene.calibration['alignment_deadline_monotonic']=31.15
|
||||
def idle():
|
||||
calls.append(scene.now)
|
||||
if len(calls)==2:
|
||||
if fault=='not-idle':raise alignment.Refused('native keys are not idle')
|
||||
if fault=='stale':scene.sleep(.51)
|
||||
if fault=='clock-reversed':scene.now-=.2
|
||||
if fault=='deadline':scene.sleep(.06)
|
||||
scene.idle=idle
|
||||
with self.subTest(fault=fault),self.assertRaises(alignment.Refused):scene.facing()
|
||||
self.assertEqual(len(calls),2);self.assertFalse(scene.report['facing_proof']['completed'])
|
||||
self.assertEqual(scene.motions,[]);self.assertEqual(scene.wheels,[])
|
||||
|
||||
def test_facing_complete_pair_does_not_hide_intermediate_native_reversal(self):
|
||||
scene=self.aligned_pair();scene.history[1]['real_ms']=scene.history[0]['real_ms']-1
|
||||
scene.history[1]['choices']['real_ms']=scene.history[1]['real_ms']
|
||||
with self.assertRaisesRegex(ValueError,'reversed'):scene.facing()
|
||||
self.assertEqual(scene.motions,[]);self.assertEqual(scene.wheels,[])
|
||||
self.assertFalse(scene.report['facing_proof']['completed'])
|
||||
|
||||
def test_selection_consumes_every_later_record_after_early_facing_commit(self):
|
||||
scene=self.aligned_pair();facing=scene.facing();self.assertEqual(facing['final_sample'],311)
|
||||
scene.sleep(.3)
|
||||
bad=next(x for x in scene.history if x['sample']==312)
|
||||
bad['pressed']=1
|
||||
# Later records are clean: latest-only selection would incorrectly pass.
|
||||
self.assertEqual(scene.history[-1]['pressed'],0)
|
||||
with self.assertRaisesRegex(ValueError,'Action began'):scene.select_action()
|
||||
self.assertEqual(scene.wheels,[])
|
||||
self.assertEqual(scene.report['action_selection_proof']['initial_sample'],311)
|
||||
|
||||
def test_actual_pose_facing_then_one_wheel_selects_with_same_lease(self):
|
||||
s=ChoiceScene();f=s.facing();r=s.select_action()
|
||||
self.assertTrue(f['completed']);self.assertNotIn('alignment_proof',s.report)
|
||||
|
||||
Reference in New Issue
Block a user