#!/usr/bin/env python3 """Behavioral checks for ordering, semantics, timing, geometry and CLI diagnostics.""" import copy import json import subprocess import sys import tempfile import unittest import xml.etree.ElementTree as ET from pathlib import Path from render_map import Renderer, smooth, overlaps ROOT=Path(__file__).resolve().parents[1] def demo(name): return json.loads((ROOT/'assets'/f'{name}-demo.json').read_text()) class MapTests(unittest.TestCase): def test_examples_are_valid_deterministic_and_labels_clear(self): for name in ('metro','timeline'): with self.subTest(name=name): d=demo(name) renderer=Renderer(copy.deepcopy(d)) svg,report=renderer.render() svg2,report2=Renderer(copy.deepcopy(d)).render() self.assertEqual(svg,svg2) self.assertEqual(report,report2) root=ET.fromstring(svg) self.assertEqual(len(root.findall('.//{*}g[@data-node]')),len(d['nodes'])) self.assertEqual(len(root.findall('.//{*}g[@data-route]')),len(d['routes'])) self.assertFalse([w for w in report['warnings'] if w['kind']!='overpass']) labels=[b for b in renderer.boxes if b[-1]=='label'] for i,a in enumerate(labels): self.assertFalse(any(overlaps(a[:4],b[:4]) for b in labels[i+1:])) for a in labels: self.assertFalse(any(overlaps(a[:4],b[:4]) for b in renderer.boxes if b[-1] in ('station','endpoint'))) def test_metro_order_respects_all_route_edges(self): d=demo('metro') _,report=Renderer(d).render() for route in d['routes']: ys=[report['positions'][n]['y'] for n in route['stops']] self.assertTrue(all(a',svg) def test_cli_strict_preserves_reviewable_output_and_returns_failure(self): d=demo('metro') d['nodes'][0]['label']='超长标签'*100 with tempfile.TemporaryDirectory() as tmp: src=Path(tmp)/'map.json' out=Path(tmp)/'map.svg' src.write_text(json.dumps(d)) result=subprocess.run([sys.executable,str(ROOT/'scripts'/'render_map.py'),str(src),'-o',str(out),'--strict'],capture_output=True,text=True) self.assertEqual(result.returncode,2,result.stderr) self.assertTrue(out.exists()) report=json.loads(out.with_suffix('.report.json').read_text()) self.assertTrue(any(w['kind']=='label_needs_review' for w in report['warnings'])) def test_single_node_route_and_extra_legend_rows(self): d={'routes':[{'id':str(i),'stops':['x']} for i in range(5)],'nodes':[{'id':'x','label':'共享'}]} r=Renderer(d) svg,report=r.render() ET.fromstring(svg) self.assertEqual(report['positions']['x']['y'],r.top) self.assertEqual(len(report['positions']['x']['xs']),5) if __name__=='__main__': unittest.main()