This commit is contained in:
nq
2026-10-19 08:52:24 +00:00
parent eb3268cf78
commit 3433135790
13 changed files with 2480 additions and 2321 deletions
+16
View File
@@ -0,0 +1,16 @@
import unittest, tempfile, csv, pathlib, sys
sys.path.insert(0,str(pathlib.Path(__file__).resolve().parents[1]/"tools"))
import audit_osmand_poi_mapping as a
class TestPoiRegistry(unittest.TestCase):
def test_registry_and_semantics(self):
with tempfile.TemporaryDirectory() as t:
p=pathlib.Path(t)/'types.xml'
p.write_text('''<poi_types><poi_type name="water" tag="amenity" value="drinking_water"/><poi_type name="locality" tag="place" value="locality" no_indx="true"/><poi_additional name="dwater" tag="drinking_water" value="yes"/></poi_types>''')
reg,ex=a.registry(p)
base={'code':'0x6414','source_count':'32922','priority':'critical','osm_tags':'amenity=drinking_water;drinking_water=yes'}
self.assertEqual(a.interpret(base,reg,ex)['status'],'native_indexable_type')
self.assertEqual(a.interpret({**base,'osm_tags':'place=locality'},reg,ex)['status'],'only_nonindexed_type')
self.assertEqual(a.interpret({**base,'osm_tags':'drinking_water=yes'},reg,ex)['status'],'additional_without_primary_type')
self.assertEqual(a.interpret({**base,'osm_tags':'man_made=water_intake'},reg,ex)['status'],'no_matching_native_type')
self.assertEqual(a.interpret({**base,'code':'0x6600','priority':'terrain','osm_tags':''},reg,ex)['severity'],'terrain_exemption')
if __name__=='__main__':unittest.main()
+8 -8
View File
@@ -4,7 +4,7 @@ import csv,importlib.util,json,pathlib,subprocess,sys,tempfile,unittest,xml.etre
ROOT=pathlib.Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('translator',ROOT/'scripts/native_translate.py')
t=importlib.util.module_from_spec(spec);spec.loader.exec_module(t)
POI=ROOT/'config/poi-mapping.csv';LINES=ROOT/'config/line-mapping.csv'
POI=ROOT/'config/BGMountains-native-poi-mapping-v094.csv';LINES=ROOT/'config/line-mapping.csv'
class Native092(unittest.TestCase):
def test_native_source_semantics(self):
rows=t.mapping(POI)
@@ -15,17 +15,17 @@ class Native092(unittest.TestCase):
self.assertIn(rows['0x6600']['action'],('append','raw'))
self.assertFalse(any('leisure=marina' in r['osm_tags'] for r in rows.values()))
self.assertFalse(any('tourism=information;information=board' in r['osm_tags'] for r in rows.values()))
self.assertTrue(all('garmin' not in r['osm_tags'] for r in rows.values()))
self.assertTrue(all('garmin:type' not in r['osm_tags'] for r in rows.values()))
self.assertTrue(all(int(rows[k]['zoom_hint'])<=12 for k in ('0x6414','0x6511','0x6500','0x660a')))
def test_config_only_two_and_types(self):
self.assertEqual(set(p.name for p in (ROOT/'config').glob('*.csv')),{'poi-mapping.csv','line-mapping.csv'})
self.assertTrue(any(x.get('route_mode')=='relation' for x in t.mapping(LINES).values()))
for line in t.mapping(LINES).values():
self.assertEqual(set(p.name for p in (ROOT/'config').glob('*.csv')),{'BGMountains-native-poi-mapping-v094.csv','line-mapping.csv'})
self.assertTrue(any(x.get('route_mode')=='relation' for x in t.line_mapping(LINES).values()))
for line in t.line_mapping(LINES).values():
if line['route_mode']=='relation':self.assertTrue(line['osmc_symbol'])
def test_native_relation_members_and_poi_provenance(self):
with tempfile.TemporaryDirectory() as directory:
d=pathlib.Path(directory);src=d/'sample.osm';dst=d/'native.osm';report=d/'report.json'
line=next(r for r in t.mapping(LINES).values() if r['route_mode']=='relation')
line=next(r for r in t.line_mapping(LINES).values() if r['route_mode']=='relation')
typ=line['code']
xml=['<?xml version="1.0" encoding="UTF-8"?><osm version="0.6">']
for i in range(1,5):xml.append(f'<node id="{i}" lat="41.{i}" lon="23.{i}"/>')
@@ -33,7 +33,7 @@ class Native092(unittest.TestCase):
for i,refs in [(10,[1,2]),(11,[2,3])]:
xml.append(f'<way id="{i}">'+''.join(f'<nd ref="{n}"/>' for n in refs)+f'<tag k="garmin:feature" v="extended_polyline"/><tag k="garmin:type" v="{typ}"/><tag k="bgm_route" v="type_{typ}"/></way>')
xml.append('</osm>');src.write_text('\n'.join(xml))
result=t.transform(src,dst,t.mapping(POI),t.mapping(LINES),report)
result=t.transform(src,dst,t.mapping(POI),t.line_mapping(LINES),report)
self.assertEqual(result['route_candidate_ways'],2);self.assertEqual(result['route_relations'],1)
root=ET.parse(dst).getroot();rel=root.find('relation');tags=t.tagdict(rel)
self.assertEqual(tags['route'],'hiking');self.assertTrue(tags['osmc:symbol'].endswith('_bar'))
@@ -45,7 +45,7 @@ class Native092(unittest.TestCase):
def test_route_attachment_to_real_way_only(self):
with tempfile.TemporaryDirectory() as directory:
d=pathlib.Path(directory);src=d/'src.osm';dst=d/'out.osm';rep=d/'report.json'
typ=next(row['code'] for row in t.mapping(LINES).values() if row['route_mode']=='relation')
typ=next(row['code'] for row in t.line_mapping(LINES).values() if row['route_mode']=='relation')
parts=['<osm version="0.6">','<node id="1" lat="41" lon="23"/>','<node id="2" lat="41.1" lon="23.1"/>',
'<node id="3" lat="41.2" lon="23.2"/>','<way id="22"><nd ref="1"/><nd ref="2"/><nd ref="3"/><tag k="highway" v="path"/></way>',
'<way id="33"><nd ref="1"/><nd ref="2"/><nd ref="3"/><tag k="garmin:type" v="'+typ+'"/><tag k="garmin:feature" v="extended_polyline"/></way>',
+8 -8
View File
@@ -10,25 +10,25 @@ class Reliability093(unittest.TestCase):
out=pathlib.Path(d)
src=out/'input.osm';dst=out/'output.osm';report=out/'report.json'
src.write_text('''<osm version="0.6"><node id="5" lat="42.1345678" lon="24.2345678"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x9999"/><tag k="garmin:mapset" v="1234"/><tag k="bgm:generated_name" v="yes"/><tag k="name" v="Garmin обект 0x9999 - Q3ABCDEFG9"/></node><node id="6" lat="42.2" lon="24.2"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6600"/><tag k="ele" v="176"/><tag k="name" v="176"/></node></osm>''')
t.transform(src,dst,t.mapping(ROOT/'config/poi-mapping.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
t.transform(src,dst,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
nodes=ET.parse(dst).getroot().findall('node');a=t.tagdict(nodes[0]);b=t.tagdict(nodes[1])
self.assertEqual(a['place'],'locality');self.assertEqual(a['name'],'Неуточнено - Q3ABCDEFG9')
self.assertEqual(a['garmin:type'],'0x9999');self.assertEqual(a['bgm:classification'],'unverified')
self.assertNotIn('place',b);self.assertEqual(b['ele'],'176')
data=t.inspect_counts(dst,t.mapping(ROOT/'config/poi-mapping.csv'),t.mapping(ROOT/'config/line-mapping.csv'),out/'audit.json')
data=t.inspect_counts(dst,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),out/'audit.json')
self.assertEqual(data['synthetic_locality_fallback_total'],1)
def test_known_bgm_types_are_not_invented_localities(self):
cfg=t.mapping(ROOT/'config/poi-mapping.csv')
self.assertEqual(cfg['0x6600']['osm_tags'],'')
cfg=t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv')
self.assertIn('bgm:spot_height=yes',cfg['0x6600']['osm_tags']);self.assertEqual(cfg['0x6600']['action'],'append')
self.assertNotIn('place=locality',cfg['0x6600']['osm_tags'])
self.assertEqual(cfg['0x650b']['osm_tags'],'tourism=information;information=route_marker;hiking=yes')
self.assertEqual(cfg['0x660b']['osm_tags'],'tourism=information;information=trail_blaze;hiking=yes')
self.assertIn('information=trail_blaze',cfg['0x650b']['osm_tags'])
self.assertIn('information=trail_blaze',cfg['0x660b']['osm_tags'])
def test_fallback_does_not_overwrite_known_native_types(self):
with tempfile.TemporaryDirectory() as d:
src=pathlib.Path(d)/'before.osm';out=pathlib.Path(d)/'after.osm';rep=pathlib.Path(d)/'rep.json'
src.write_text('''<osm><node id="1" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6400"/><tag k="landuse" v="quarry"/><tag k="name" v="Кариера П"/></node><node id="2" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6406"/><tag k="barrier" v="gate"/></node><node id="3" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x640d"/><tag k="aerialway" v="station"/></node></osm>''')
t.transform(src,out,t.mapping(ROOT/'config/poi-mapping.csv'),t.mapping(ROOT/'config/line-mapping.csv'),rep)
t.transform(src,out,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),rep)
nodes=ET.parse(out).getroot().findall('node')
self.assertTrue(all('place' not in t.tagdict(e) for e in nodes))
self.assertEqual(t.tagdict(nodes[0])['name'],'Кариера П')
@@ -53,7 +53,7 @@ class Reliability093(unittest.TestCase):
with tempfile.TemporaryDirectory() as d:
path=pathlib.Path(d)/'sample.osm';report=pathlib.Path(d)/'report.json'
path.write_text('''<osm version="0.6"><node id="1" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6600"/><tag k="ele" v="132"/></node><node id="2" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x650b"/><tag k="tourism" v="information"/><tag k="information" v="route_marker"/></node><node id="3" lat="43" lon="22"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6502"/><tag k="ford" v="yes"/></node></osm>''')
stats=t.inspect_counts(path,t.mapping(ROOT/'config/poi-mapping.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
stats=t.inspect_counts(path,t.mapping(ROOT/'config/BGMountains-native-poi-mapping-v094.csv'),t.mapping(ROOT/'config/line-mapping.csv'),report)
self.assertEqual(stats['unclassified_total'],0)
self.assertEqual(stats['annotations_not_missing_pois']['spot_height'],1)
if __name__=='__main__':unittest.main()
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""Version 0.95 regression checks, including its REAL published CLI contract.
Only mock OsmAndMapCreator is used for the orchestration test, not for proving
OBF binary correctness. The real IMG tests were run separately during release.
"""
import csv,importlib.util,json,pathlib,subprocess,sys,tempfile,unittest,xml.etree.ElementTree as ET
ROOT=pathlib.Path(__file__).resolve().parents[1]
SPEC=importlib.util.spec_from_file_location('nt',ROOT/'scripts/native_translate.py')
t=importlib.util.module_from_spec(SPEC);SPEC.loader.exec_module(t)
POI=ROOT/'config/BGMountains-native-poi-mapping-v094.csv'
LINES=ROOT/'config/line-mapping.csv'
SCRIPT=ROOT/'scripts/build_bgmountains.py'
class V095(unittest.TestCase):
def test_mapping_handoff_has_all_named_exceptions(self):
bases,conditional=t.read_poi_configuration(POI)
self.assertEqual(len(bases),1871)
self.assertEqual(len(conditional),79)
self.assertEqual(len(t.line_mapping(LINES)),50)
self.assertEqual(sum(x['code']=='0x6414' and x['name_contains']=='Сух' for x in conditional),1)
with tempfile.TemporaryDirectory() as dd:
d=pathlib.Path(dd)
t.make_cpp_rules(bases,d/'cpp.csv',d/'labels.csv',conditional)
rows=list(csv.DictReader((d/'cpp.csv').open()))
self.assertEqual(len(rows),1950)
self.assertEqual(next(x for x in rows if x['code']=='0x6414' and x['name_contains']=='')['tags'].split(';')[:2],['amenity=drinking_water','man_made=water_tap'])
self.assertEqual(next(x for x in rows if x['code']=='0x6414' and x['name_contains']=='Сух')['tags'].split(';')[:2],['man_made=water_tap','drinking_water=no'])
def test_argument_contract_with_users_flags(self):
helptext=subprocess.run([sys.executable,SCRIPT,'--help'],capture_output=True,text=True,check=True).stdout
for flag in ('--poi-config','--line-config','--download-creator','--jobs','--java-opts','--bbox','--prepare-only','--creator','--template'):
self.assertIn(flag,helptext)
self.assertNotIn('unrecognized arguments',helptext)
def test_smoke_full_orchestration_with_mock_creator(self):
with tempfile.TemporaryDirectory() as dd:
d=pathlib.Path(dd)
creator=d/'creator';creator.mkdir()
(creator/'utilities.sh').write_text('#!/bin/sh\nset -eu\n[ "$1" = "generate-obf-no-address" ]\n[ -f "$2" ]\n[ "${JAVA_TOOL_OPTIONS:-}" = "-Xms512M -Xmx25G" ]\nprintf MOCK-OBF > "${2%.osm}.obf"\n')
(creator/'utilities.sh').chmod(0o755)
(creator/'inspector.sh').write_text('#!/bin/sh\n[ "$1" = "-vpoi" ]\nprintf MOCK-INSPECTION\n')
(creator/'inspector.sh').chmod(0o755)
raw=d/'minimal.osm';raw.write_text('''<osm version="0.6"><node id="1" lat="42" lon="24"><tag k="garmin:feature" v="poi"/><tag k="garmin:type" v="0x6414"/><tag k="amenity" v="drinking_water"/><tag k="man_made" v="water_tap"/><tag k="name" v="Чешма"/></node></osm>''',encoding='utf-8')
cmd=[sys.executable,str(SCRIPT),'--osm',str(raw),'--poi-config',str(POI),'--line-config',str(LINES),'--out',str(d/'out'),'--jobs','8','--java-opts','-Xms512M -Xmx25G','--creator',str(creator)]
run=subprocess.run(cmd,cwd=ROOT,capture_output=True,text=True)
self.assertEqual(run.returncode,0,run.stderr+'\n'+run.stdout)
self.assertTrue((d/'out'/'BGMountains-native.obf').is_file())
self.assertEqual(json.loads((d/'out'/'build-manifest.json').read_text())['version'],'v0.95')
self.assertEqual(json.loads((d/'out'/'semantic-audit.json').read_text())['status'],'PASS')
tags=t.tagdict(ET.parse(d/'out'/'BGMountains-native.osm').getroot().find('node'))
self.assertEqual(tags['amenity'],'drinking_water')
self.assertEqual(tags['man_made'],'water_tap')
def test_future_template_does_not_require_existing_configs(self):
with tempfile.TemporaryDirectory() as dd:
d=pathlib.Path(dd)
# No fake success from a malformed configuration: expected exit nonzero.
p=subprocess.run([sys.executable,SCRIPT,'--osm',str(d/'notthere.osm'),'--poi-config',str(d/'missing.csv'),'--line-config',str(LINES),'--prepare-only'],capture_output=True,text=True)
self.assertNotEqual(p.returncode,0)
self.assertIn('mapping file not found',p.stderr)
if __name__=='__main__':unittest.main()