pyloxi: automatically test all datafiles

The new function add_datafiles_tests is fairly magical but I don't see a better
way to do this in Python 2.6.
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index fa97a79..c91c7b3 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -27,7 +27,7 @@
 # under the EPL.
 import unittest
 import test_data
-from testutil import test_datafile
+from testutil import add_datafiles_tests
 
 try:
     import loxi.of10 as ofp
@@ -58,9 +58,6 @@
         self.assertTrue(hasattr(loxi.of10, "message"))
 
 class TestActions(unittest.TestCase):
-    def test_output(self):
-        test_datafile('action_output.data')
-
     def test_output_equality(self):
         action = ofp.action.output(port=1, max_len=0x1234)
         action2 = ofp.action.output(port=1, max_len=0x1234)
@@ -74,9 +71,6 @@
         self.assertNotEquals(action, action2)
         action2.max_len = 0x1234
 
-    def test_bsn_set_tunnel_dst(self):
-        test_datafile('of10/action_bsn_set_tunnel_dst.data')
-
 # Assumes action serialization/deserialization works
 class TestActionList(unittest.TestCase):
     def test_normal(self):
@@ -130,15 +124,6 @@
         self.assertEquals(0xfc000, ofp.OFPFW_NW_DST_MASK)
 
 class TestCommon(unittest.TestCase):
-    def test_port_desc(self):
-        test_datafile('of10/port_desc.data')
-
-    def test_table_stats_entry(self):
-        test_datafile('of10/table_stats_entry.data')
-
-    def test_flow_stats_entry(self):
-        test_datafile('of10/flow_stats_entry.data')
-
     def test_match(self):
         match = ofp.match()
         self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
@@ -161,17 +146,11 @@
         msg = ofp.message.hello(xid=0)
         self.assertEquals(msg.xid, 0)
 
-    def test_hello(self):
-        test_datafile('of10/hello.data')
-
     def test_echo_request_construction(self):
         msg = ofp.message.echo_request(data="abc")
         self.assertEquals(msg.data, "abc")
 
-    def test_echo_request(self):
-        test_datafile('of10/echo_request.data')
-
-        # Invalid length
+    def test_echo_request_invalid_length(self):
         buf = "\x01\x02\x00\x07\x12\x34\x56"
         with self.assertRaisesRegexp(ofp.ProtocolError, "buffer too short"):
             ofp.message.echo_request.unpack(buf)
@@ -649,6 +628,11 @@
         self.assertEquals(msg.queues[1].properties[0].rate, 6)
         self.assertEquals(msg.queues[1].properties[1].rate, 7)
 
+class TestDataFiles(unittest.TestCase):
+    pass
+
+add_datafiles_tests(TestDataFiles, 'of10/', ofp)
+
 class TestParse(unittest.TestCase):
     def test_parse_header(self):
         import loxi
diff --git a/py_gen/tests/testutil.py b/py_gen/tests/testutil.py
index 1e12e9b..ea30902 100644
--- a/py_gen/tests/testutil.py
+++ b/py_gen/tests/testutil.py
@@ -28,6 +28,9 @@
 
 import sys
 import difflib
+import re
+import os
+import unittest
 import test_data
 
 # Human-friendly format for binary strings. 8 bytes per line.
@@ -72,12 +75,32 @@
             (type(obj).__name__, expected, pretty, diff(expected, pretty)))
 
 # Run test_serialization and possibly test_pretty against the named data file
-# Uses the globals of the calling function to get 'ofp'
-def test_datafile(name):
+def test_datafile(name, ofp):
     data = test_data.read(name)
+    if not 'python' in data:
+        raise unittest.SkipTest("no python section in datafile")
     binary = data['binary']
     python = data['python']
-    obj = eval(python, sys._getframe(1).f_globals)
+    obj = eval(python, { 'ofp': ofp })
     test_serialization(obj, binary)
     if 'python pretty-printer' in data:
         test_pretty(obj, data['python pretty-printer'])
+
+# Add test_datafile tests for each datafile matching the given regex
+# The argument 'klass' should be a subclass of TestCase which will have the
+# test_* methods added to it.
+#
+# It would be cleaner to do this by constructing a TestSuite instance and
+# adding individual TestCase objects, but the TestLoader wouldn't pick it
+# up. We could use the load_tests protocol but that isn't available before
+# Python 2.7.
+def add_datafiles_tests(klass, regex, ofp):
+    for filename in test_data.list_files():
+        match = re.match(regex, filename)
+        if not match:
+            continue
+        def make_test(filename):
+            def fn(self):
+                test_datafile(filename, ofp)
+            return fn
+        setattr(klass, 'test_' + os.path.splitext(filename)[0], make_test(filename))