Fix catalog parsing, add unit-tests
Fixes issues with conflated nodes and skipped previews in the catalog XML.
This commit is contained in:
parent
93b71e29e6
commit
acb8a7a793
8 changed files with 179 additions and 9 deletions
|
@ -144,6 +144,22 @@ class Node(object):
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
|
class ParseState:
|
||||||
|
def __init__(self):
|
||||||
|
self._counters = {}
|
||||||
|
|
||||||
|
def getNextIndex(self, name):
|
||||||
|
if name in self._counters:
|
||||||
|
self._counters[name] += 1
|
||||||
|
else:
|
||||||
|
self._counters[name] = 0
|
||||||
|
return self._counters[name]
|
||||||
|
|
||||||
|
def recordExplicitIndex(self, name, index):
|
||||||
|
if not name in self._counters:
|
||||||
|
self._counters[name] = index
|
||||||
|
else:
|
||||||
|
self._counters[name] = max(self._counters[name], index)
|
||||||
|
|
||||||
class PropsHandler(handler.ContentHandler):
|
class PropsHandler(handler.ContentHandler):
|
||||||
def __init__(self, root = None, path = None, includePaths = []):
|
def __init__(self, root = None, path = None, includePaths = []):
|
||||||
|
@ -152,6 +168,7 @@ class PropsHandler(handler.ContentHandler):
|
||||||
self._basePath = os.path.dirname(path)
|
self._basePath = os.path.dirname(path)
|
||||||
self._includes = includePaths
|
self._includes = includePaths
|
||||||
self._locator = None
|
self._locator = None
|
||||||
|
self._stateStack = [ParseState()]
|
||||||
|
|
||||||
if root is None:
|
if root is None:
|
||||||
# make a nameless root node
|
# make a nameless root node
|
||||||
|
@ -163,25 +180,31 @@ class PropsHandler(handler.ContentHandler):
|
||||||
|
|
||||||
def startElement(self, name, attrs):
|
def startElement(self, name, attrs):
|
||||||
self._content = None
|
self._content = None
|
||||||
|
if (name == 'PropertyList'):
|
||||||
|
# still need to handle includes on the root element
|
||||||
if 'include' in attrs.keys():
|
if 'include' in attrs.keys():
|
||||||
self.handleInclude(attrs['include'])
|
self.handleInclude(attrs['include'])
|
||||||
|
|
||||||
if (name == 'PropertyList'):
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
currentState = self._stateStack[-1]
|
||||||
if 'n' in attrs.keys():
|
if 'n' in attrs.keys():
|
||||||
try:
|
try:
|
||||||
index = int(attrs['n'])
|
index = int(attrs['n'])
|
||||||
except:
|
except:
|
||||||
print "Invalid index at line:", self._locator.getLineNumber(), "of", self._path
|
raise IndexError("Invalid index at line:", self._locator.getLineNumber(), "of", self._path)
|
||||||
self._current = self._current.addChild(name)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
currentState.recordExplicitIndex(name, index)
|
||||||
self._current = self._current.getChild(name, index, create=True)
|
self._current = self._current.getChild(name, index, create=True)
|
||||||
else:
|
else:
|
||||||
|
index = currentState.getNextIndex(name)
|
||||||
# important we use getChild here, so that includes are resolved
|
# important we use getChild here, so that includes are resolved
|
||||||
# correctly
|
# correctly
|
||||||
self._current = self._current.getChild(name, create=True)
|
self._current = self._current.getChild(name, index, create=True)
|
||||||
|
|
||||||
|
self._stateStack.append(ParseState())
|
||||||
|
|
||||||
|
if 'include' in attrs.keys():
|
||||||
|
self.handleInclude(attrs['include'])
|
||||||
|
|
||||||
self._currentTy = None;
|
self._currentTy = None;
|
||||||
if 'type' in attrs.keys():
|
if 'type' in attrs.keys():
|
||||||
|
@ -229,6 +252,7 @@ class PropsHandler(handler.ContentHandler):
|
||||||
self._current = self._current.parent
|
self._current = self._current.parent
|
||||||
self._content = None
|
self._content = None
|
||||||
self._currentTy = None
|
self._currentTy = None
|
||||||
|
self._stateStack.pop()
|
||||||
|
|
||||||
|
|
||||||
def parsePropsBool(self, content):
|
def parsePropsBool(self, content):
|
||||||
|
|
11
catalog/testData/bad-index.xml
Normal file
11
catalog/testData/bad-index.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<PropertyList>
|
||||||
|
<sim>
|
||||||
|
<!-- found in the F-15 XML -->
|
||||||
|
<uhf n="[0]">
|
||||||
|
<frequencies>
|
||||||
|
<selected-mhz type="int">225000</selected-mhz>
|
||||||
|
</frequencies>
|
||||||
|
</uhf>
|
||||||
|
</sim>
|
||||||
|
</PropertyList>
|
25
catalog/testData/props1.xml
Normal file
25
catalog/testData/props1.xml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<PropertyList>
|
||||||
|
<value type="int">42</value>
|
||||||
|
|
||||||
|
<thing>
|
||||||
|
<value>apple</value>
|
||||||
|
</thing>
|
||||||
|
|
||||||
|
<thing>
|
||||||
|
<value>lemon</value>
|
||||||
|
</thing>
|
||||||
|
|
||||||
|
<thing>
|
||||||
|
<value>pear</value>
|
||||||
|
</thing>
|
||||||
|
|
||||||
|
<!-- ensure explicit indexing works -->
|
||||||
|
<sub>
|
||||||
|
<value n="1">a</value>
|
||||||
|
<value n="3">b</value>
|
||||||
|
<value n="99">c</value>
|
||||||
|
<value>d</value> <!-- should be assigned n=100 -->
|
||||||
|
</sub>
|
||||||
|
|
||||||
|
</PropertyList>
|
20
catalog/testData/props2.xml
Normal file
20
catalog/testData/props2.xml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<PropertyList include="root-include.xml">
|
||||||
|
<value type="int">33</value>
|
||||||
|
|
||||||
|
<thing>
|
||||||
|
<value>apple</value>
|
||||||
|
</thing>
|
||||||
|
|
||||||
|
<thing>
|
||||||
|
<value>lemon</value>
|
||||||
|
</thing>
|
||||||
|
|
||||||
|
|
||||||
|
<sub include ="sub-include.xml">
|
||||||
|
<!-- different index to avoid being included over -->
|
||||||
|
<widget n="100" type="int">99</widget>
|
||||||
|
|
||||||
|
</sub>
|
||||||
|
|
||||||
|
</PropertyList>
|
22
catalog/testData/root-include.xml
Normal file
22
catalog/testData/root-include.xml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<PropertyList>
|
||||||
|
<value type="int">42</value>
|
||||||
|
<value type="int">43</value>
|
||||||
|
<value type="int">44</value>
|
||||||
|
|
||||||
|
<sim>
|
||||||
|
<views>
|
||||||
|
</views>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
</preview>
|
||||||
|
</sim>
|
||||||
|
|
||||||
|
<payload>
|
||||||
|
<weight>
|
||||||
|
</weight>
|
||||||
|
</payload>
|
||||||
|
</PropertyList>
|
6
catalog/testData/sub-include.xml
Normal file
6
catalog/testData/sub-include.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
<PropertyList>
|
||||||
|
<widget type="int">42</widget>
|
||||||
|
<widget type="int">43</widget>
|
||||||
|
<widget type="int">44</widget>
|
||||||
|
</PropertyList>
|
61
catalog/test_sgprops.py
Executable file
61
catalog/test_sgprops.py
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import types
|
||||||
|
import sgprops
|
||||||
|
|
||||||
|
class SGProps(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_parse(self):
|
||||||
|
parsed = sgprops.readProps("testData/props1.xml")
|
||||||
|
|
||||||
|
self.assertEqual(parsed.getValue("value"), 42)
|
||||||
|
self.assertEqual(type(parsed.getValue("value")), types.IntType)
|
||||||
|
|
||||||
|
valNode = parsed.getChild("value")
|
||||||
|
self.assertEqual(valNode.parent, parsed)
|
||||||
|
self.assertEqual(valNode.name, "value")
|
||||||
|
|
||||||
|
self.assertEqual(valNode.value, 42)
|
||||||
|
self.assertEqual(type(valNode.value), types.IntType)
|
||||||
|
|
||||||
|
with self.assertRaises(IndexError):
|
||||||
|
missingNode = parsed.getChild("missing")
|
||||||
|
|
||||||
|
things = parsed.getChildren("thing")
|
||||||
|
self.assertEqual(len(things), 3)
|
||||||
|
|
||||||
|
self.assertEqual(things[0], parsed.getChild("thing", 0));
|
||||||
|
self.assertEqual(things[1], parsed.getChild("thing", 1));
|
||||||
|
self.assertEqual(things[2], parsed.getChild("thing", 2));
|
||||||
|
|
||||||
|
self.assertEqual(things[0].getValue("value"), "apple");
|
||||||
|
self.assertEqual(things[1].getValue("value"), "lemon");
|
||||||
|
self.assertEqual(things[2].getValue("value"), "pear");
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalidIndex(self):
|
||||||
|
with self.assertRaises(IndexError):
|
||||||
|
parsed = sgprops.readProps("testData/bad-index.xml")
|
||||||
|
|
||||||
|
def test_include(self):
|
||||||
|
parsed = sgprops.readProps("testData/props2.xml")
|
||||||
|
|
||||||
|
# test that value in main file over-rides the one in the include
|
||||||
|
self.assertEqual(parsed.getValue("value"), 33)
|
||||||
|
|
||||||
|
# but these come from the included file
|
||||||
|
self.assertEqual(parsed.getValue("value[1]"), 43)
|
||||||
|
self.assertEqual(parsed.getValue("value[2]"), 44)
|
||||||
|
|
||||||
|
subNode = parsed.getChild("sub")
|
||||||
|
widgets = subNode.getChildren("widget")
|
||||||
|
self.assertEqual(len(widgets), 4)
|
||||||
|
|
||||||
|
self.assertEqual(widgets[2].value, 44)
|
||||||
|
self.assertEqual(widgets[3].value, 99)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -10,6 +10,7 @@ import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import sgprops
|
import sgprops
|
||||||
|
import sys
|
||||||
|
|
||||||
CATALOG_VERSION = 4
|
CATALOG_VERSION = 4
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ def scan_aircraft_dir(aircraft_dir):
|
||||||
if d == None:
|
if d == None:
|
||||||
continue
|
continue
|
||||||
except:
|
except:
|
||||||
print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file)
|
print "Skipping set file since couldn't be parsed:", os.path.join(aircraft_dir, file), sys.exc_info()[0]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
setDicts.append(d)
|
setDicts.append(d)
|
||||||
|
|
Loading…
Add table
Reference in a new issue