chiark / gitweb /
mdwsetup.py: Add a command for running tests.
[runlisp] / mdwsetup.py
index 57832a2d20e5a9727d77ec913c2c57d407b349ed..55120fb116438b40cebbad25b55f8f073be70345 100644 (file)
@@ -33,6 +33,13 @@ import subprocess as SUB
 import distutils.core as DC
 import distutils.log as DL
 
 import distutils.core as DC
 import distutils.log as DL
 
+###--------------------------------------------------------------------------
+### Compatibility hacks.
+
+def with_metaclass(meta, *supers):
+  return meta("#<anonymous base %s>" % meta.__name__,
+              supers or (object,), dict())
+
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
 ###--------------------------------------------------------------------------
 ### Random utilities.
 
@@ -73,10 +80,10 @@ def progoutput(command):
   The COMMAND must produce exactly one line of output, and must exit with
   status zero.
   """
   The COMMAND must produce exactly one line of output, and must exit with
   status zero.
   """
-  kid = SUB.Popen(command, stdout = SUB.PIPE)
+  kid = SUB.Popen(command, stdout = SUB.PIPE, universal_newlines = True)
   try:
     out = kid.stdout.readline()
   try:
     out = kid.stdout.readline()
-    junk = kid.stdout.read()
+    junk = kid.stdout.read(1)
   finally:
     kid.stdout.close()
   if junk != '': raise ValueError \
   finally:
     kid.stdout.close()
   if junk != '': raise ValueError \
@@ -227,7 +234,7 @@ class CommandClass (type):
     else: CMDS[name] = c
     return c
 
     else: CMDS[name] = c
     return c
 
-class Command (DC.Command, object):
+class Command (with_metaclass(CommandClass, DC.Command, object)):
   """
   Base class for `mdwsetup' command classes.
 
   """
   Base class for `mdwsetup' command classes.
 
@@ -250,9 +257,9 @@ class distdir (Command):
   description = "print the distribution directory name to stdout"
   def run(me):
     d = me.distribution
   description = "print the distribution directory name to stdout"
   def run(me):
     d = me.distribution
-    print '%s-%s' % (d.get_name(), d.get_version())
+    print('%s-%s' % (d.get_name(), d.get_version()))
 
 
-class build_gen(Command):
+class build_gen (Command):
   """
   Generate files, according to the `genfiles'.
 
   """
   Generate files, according to the `genfiles'.
 
@@ -281,7 +288,47 @@ class build (_build, Command):
   sub_commands = [('build_gen', lambda me: me.distribution.genfiles)]
   sub_commands += _build.sub_commands
 
   sub_commands = [('build_gen', lambda me: me.distribution.genfiles)]
   sub_commands += _build.sub_commands
 
-class clean_gen(Command):
+class test (Command):
+  """
+  Run unit tests, according to the `unittests'.
+
+  The `unittests' keyword argument to `setup' lists module names (or other
+  things acceptable to the `loadTestsFromNames' test-loader method) to be
+  run.  The build library directory is prepended to the load path before
+  running the tests to ensure that the newly built modules are tested.  If
+  `unittest_dir' is set, then this is appended to the load path so that test
+  modules can be found there.
+  """
+  NAME = "test"
+  description = "run the included test suite"
+
+  user_options = \
+    [('build-lib=', 'b', "directory containing compiled moules"),
+     ('tests=', 't', "tests to run"),
+     ('verbose-test', 'V', "run tests verbosely")]
+
+  def initialize_options(me):
+    me.build_lib = None
+    me.verbose_test = False
+    me.tests = None
+  def finalize_options(me):
+    me.set_undefined_options('build', ('build_lib', 'build_lib'))
+  def run(me):
+    import unittest as U
+    d = me.distribution
+    SYS.path = [me.build_lib] + SYS.path
+    if d.unittest_dir is not None: SYS.path.append(d.unittest_dir)
+    if me.tests is not None: tests = me.tests.split(",")
+    else: tests = d.unittests
+    suite = U.defaultTestLoader.loadTestsFromNames(tests)
+    runner = U.TextTestRunner(verbosity = me.verbose_test and 2 or 1)
+    if me.dry_run: return
+    result = runner.run(suite)
+    if result.errors or result.failures or \
+       getattr(result, "unexpectedSuccesses", 0):
+      SYS.exit(2)
+
+class clean_gen (Command):
   """
   Remove the generated files, as listed in `genfiles'.
 
   """
   Remove the generated files, as listed in `genfiles'.
 
@@ -293,7 +340,7 @@ class clean_gen(Command):
     d = me.distribution
     for g in d.genfiles: g.clean(dry_run_p = me.dry_run)
 
     d = me.distribution
     for g in d.genfiles: g.clean(dry_run_p = me.dry_run)
 
-class clean_others(Command):
+class clean_others (Command):
   """
   Remove the files listed in the `cleanfiles' argument to `setup'.
   """
   """
   Remove the files listed in the `cleanfiles' argument to `setup'.
   """
@@ -344,6 +391,8 @@ class Dist (DC.Distribution):
   ## our enhanced commands.
   def __init__(me, attrs = None):
     me.genfiles = []
   ## our enhanced commands.
   def __init__(me, attrs = None):
     me.genfiles = []
+    me.unittest_dir = None
+    me.unittests = []
     me.cleanfiles = []
     me._auto_version_p = False
     DC.Distribution.__init__(me, attrs)
     me.cleanfiles = []
     me._auto_version_p = False
     DC.Distribution.__init__(me, attrs)