Repository: ESMCI/cime

Issue:
Clean up python in unit_testing
Clean up code in unit_testing/python


Discussion:
(Much of this is now dead code and should be removed.)
Recommend do this after #1423 is closed.

Resolving patch:
diff --git a/scripts/lib/CIME/code_checker.py b/scripts/lib/CIME/code_checker.py
--- a/scripts/lib/CIME/code_checker.py
+++ b/scripts/lib/CIME/code_checker.py
@@ -26,7 +26,7 @@ def _run_pylint(on_file, interactive):
     cmd_options += " --init-hook='sys.path.extend((\"%s\",\"%s\",\"%s\"))'"%\
         (os.path.join(cimeroot,"scripts","lib"),
          os.path.join(cimeroot,"scripts","Tools"),
-         os.path.join(cimeroot,"tools","unit_testing","python"))
+         os.path.join(cimeroot,"scripts","fortran_unit_testing","python"))
 
     cmd = "%s %s %s" % (pylint, cmd_options, on_file)
     logger.debug("pylint command is %s"%cmd)


Test patch:
diff --git a/README.unit_testing b/README.unit_testing
deleted file mode 100644
--- a/README.unit_testing
+++ /dev/null
@@ -1,15 +0,0 @@
-# To run all the CIME Fortran unit tests, run the following command:
-# On yellowstone, this requires module load all-python-libs
-#
-# The creation of a temporary directory ensures that you are doing a completely
-# clean build of the unit tests. (The use of the --clean flag to run_tests.py
-# cleans most, but not all of the files created by the unit test build.) For
-# rerunning the tests after an incremental change, you can instead use an
-# existing build directory.
-#
-# We would encourage you to port these tests to other platforms.
-# The test requires an install of pFunit available from
-# https://sourceforge.net/projects/pfunit/
-#
-
-tools/unit_testing/run_tests.py --build-dir `mktemp -d ./unit_tests.XXXXXXXX`
diff --git a/tools/unit_testing/Examples/circle_area/src/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/circle_area/src/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/src/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/circle_area/src/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/circle_area/src/circle.F90 b/scripts/fortran_unit_testing/Examples/circle_area/src/circle.F90
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/src/circle.F90
rename to scripts/fortran_unit_testing/Examples/circle_area/src/circle.F90
diff --git a/tools/unit_testing/Examples/circle_area/tests/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/circle_area/tests/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/tests/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/circle_area/tests/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/circle_area/tests/CTest/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/circle_area/tests/CTest/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/tests/CTest/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/circle_area/tests/CTest/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/circle_area/tests/CTest/test_driver.F90 b/scripts/fortran_unit_testing/Examples/circle_area/tests/CTest/test_driver.F90
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/tests/CTest/test_driver.F90
rename to scripts/fortran_unit_testing/Examples/circle_area/tests/CTest/test_driver.F90
diff --git a/tools/unit_testing/Examples/circle_area/tests/pFUnit/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/circle_area/tests/pFUnit/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/tests/pFUnit/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/circle_area/tests/pFUnit/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/circle_area/tests/pFUnit/test_circle.pf b/scripts/fortran_unit_testing/Examples/circle_area/tests/pFUnit/test_circle.pf
similarity index 100%
rename from tools/unit_testing/Examples/circle_area/tests/pFUnit/test_circle.pf
rename to scripts/fortran_unit_testing/Examples/circle_area/tests/pFUnit/test_circle.pf
diff --git a/tools/unit_testing/Examples/interpolate_1d/src/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/interpolate_1d/src/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/src/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/src/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/interpolate_1d/src/interpolate_1d.F90 b/scripts/fortran_unit_testing/Examples/interpolate_1d/src/interpolate_1d.F90
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/src/interpolate_1d.F90
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/src/interpolate_1d.F90
diff --git a/tools/unit_testing/Examples/interpolate_1d/tests/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/tests/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/interpolate_1d/tests/CTest/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CTest/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/tests/CTest/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CTest/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/interpolate_1d/tests/CTest/test_driver.F90 b/scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CTest/test_driver.F90
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/tests/CTest/test_driver.F90
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/tests/CTest/test_driver.F90
diff --git a/tools/unit_testing/Examples/interpolate_1d/tests/pFUnit/CMakeLists.txt b/scripts/fortran_unit_testing/Examples/interpolate_1d/tests/pFUnit/CMakeLists.txt
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/tests/pFUnit/CMakeLists.txt
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/tests/pFUnit/CMakeLists.txt
diff --git a/tools/unit_testing/Examples/interpolate_1d/tests/pFUnit/test_interpolate_point.pf b/scripts/fortran_unit_testing/Examples/interpolate_1d/tests/pFUnit/test_interpolate_point.pf
similarity index 100%
rename from tools/unit_testing/Examples/interpolate_1d/tests/pFUnit/test_interpolate_point.pf
rename to scripts/fortran_unit_testing/Examples/interpolate_1d/tests/pFUnit/test_interpolate_point.pf
diff --git a/tools/unit_testing/Examples/test_list.xml b/scripts/fortran_unit_testing/Examples/test_list.xml
similarity index 100%
rename from tools/unit_testing/Examples/test_list.xml
rename to scripts/fortran_unit_testing/Examples/test_list.xml
diff --git a/tools/unit_testing/README b/scripts/fortran_unit_testing/README
similarity index 84%
rename from tools/unit_testing/README
rename to scripts/fortran_unit_testing/README
--- a/tools/unit_testing/README
+++ b/scripts/fortran_unit_testing/README
@@ -1,20 +1,10 @@
 -*- mode:org -*-
 #+startup: showall
 
-Quick guide to the CESM unit testing framework
+Quick guide to the CIME unit testing framework
 
 * Requirements
 
-  - Machines :: Basic compiler information is retrieved from
-                config_compilers.xml in Machines. Even if running on an
-                unsupported machine, it is necessary to have this file to
-                get some basic information about the compiler.
-
-                As of this writing, it is possible to run with Machines,
-                this directory, the CMake module directory, and the
-                directory that specifies tests. A full CESM checkout is
-                more convenient but not needed.
-
   - Python 2.6 or later :: Required for scripts. Python 3.2 is also known
        to work.
 
@@ -40,7 +30,7 @@ Quick guide to the CESM unit testing framework
 ** Quick start
 
   In the simplest case, you may want to run tests from a single directory.
-  In that case, there are just three pieces of information that you usually
+  In that case, there are just two pieces of information that you usually
   need to specify:
 
   1. The location of the directory where you want to build and run the
@@ -48,23 +38,11 @@ Quick guide to the CESM unit testing framework
 
   2. The location of the directory specifying the test via a
      CMakeLists.txt file ("TEST_SPEC_DIR").
-
-  3. The compiler vendor ("COMPILER").
+     (Optional: Defaults to the current directory)
 
   The following command can run the tests:
 
-    run_tests.py --build-dir=BUILD_DIR --test-spec-dir=TEST_SPEC_DIR \
-        --compiler=COMPILER
-
-  The current compiler options are:
-
-  - GNU
-  - IBM
-  - Intel
-  - NAG
-  - PGI
-
-  COMPILER defaults to GNU.
+    run_tests.py --build-dir=BUILD_DIR --test-spec-dir=TEST_SPEC_DIR
 
   If you change a source file, you can run the same command again, and
   run_tests.py will do the bare minimum of work necessary to re-run the
@@ -79,18 +57,9 @@ Quick guide to the CESM unit testing framework
 
    Some particularly useful options are as follows:
 
-   + --build-type :: Setting this option to "CESM" will build the tests
-                     with CESM compiler options (or the most similar
-                     pFUnit-compatible flags). Setting this option to
-                     "CESM_DEBUG" will build with CESM's DEBUG mode
-                     options.
-
    + --clean :: Cleans the directory and re-runs both CMake and make when
                   running the tests.
 
-   + -R :: This option allows you to specify a regular expression; only
-             tests with names matching that expression will run.
-
    + -v :: Verbose output, including the commands used to compile, any
            compiler warnings, and CTest output from tests that pass. To get
            CTest output from tests that fail, set the environment variable
@@ -103,34 +72,41 @@ Quick guide to the CESM unit testing framework
 
 * Defining new tests using CMake
 
-  This README focuses on integration between the python scripts here, the
-  CMake modules, and CESM Machines. Further information, such as detailed
-  APIs, can be found in the documentation for pFUnit and the CMake modules
-  themselves.
+  This README focuses on integration between the python scripts here,
+  the CMake modules, and machine information. Further information, such
+  as detailed APIs, can be found in the documentation for pFUnit and the
+  CMake modules themselves.
 
   If you are new to this system, it is *highly* recommended that you look
   through the examples first.
 
-** CIME_utils - run_tests.py interface
+** CIME_initial_setup and CIME_utils - run_tests.py interface
 
-   The following CMake snippet will include the CESM utilities module. The
+   The following CMake snippet will include the CIME utilities module. The
    variable ${CIME_CMAKE_MODULE_DIRECTORY} is defined by run_tests.py, or
    by hand if you choose not to use run_tests.py and instead invoke cmake
    directly.
 
      list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY})
+     include(CIME_initial_setup)
+     project(cime_tests Fortran C)
      include(CIME_utils)
 
+   The project name does not need to be 'cime_tests'. The key point of
+   the above snippet is that you need to include CIME_initial_setup
+   before the project line, and CIME_utils after the project line.
+
    CIME_utils processes a few options set by run_tests.py (e.g.
    "USE_COLOR"), and includes all of the following modules as well.
    Projects that do development without run_tests.py may choose to include
    only the modules below that they need.
 
-** Compilers - CESM compiler options
+** Compilers - CIME compiler options
 
    This module is also part of the run_tests.py interface; the primary
-   purpose is to read in flags generated from CESM's config_compilers.xml
-   file. However, it's also a catch-all for compiler-specific information.
+   purpose is to read in flags generated from the config_compilers.xml
+   file. However, it's also a catch-all for compiler-specific
+   information.
 
    The details of this module shouldn't be important to most users, most of
    the time, but it does provide one utility function that you may want to
diff --git a/tools/unit_testing/python/.gitignore b/scripts/fortran_unit_testing/python/.gitignore
similarity index 100%
rename from tools/unit_testing/python/.gitignore
rename to scripts/fortran_unit_testing/python/.gitignore
diff --git a/tools/unit_testing/python/printer.py b/scripts/fortran_unit_testing/python/printer.py
similarity index 100%
rename from tools/unit_testing/python/printer.py
rename to scripts/fortran_unit_testing/python/printer.py
diff --git a/tools/unit_testing/python/test_xml_test_list.py b/scripts/fortran_unit_testing/python/test_xml_test_list.py
similarity index 97%
rename from tools/unit_testing/python/test_xml_test_list.py
rename to scripts/fortran_unit_testing/python/test_xml_test_list.py
--- a/tools/unit_testing/python/test_xml_test_list.py
+++ b/scripts/fortran_unit_testing/python/test_xml_test_list.py
@@ -58,7 +58,7 @@ class TestSuitesFromXML(unittest.TestCase):
     """Tests for the suites_from_xml function."""
 
     def check_spec_list(self, xml_str, names, directories,
-                        known_paths={}, labels=None):
+                        known_paths=None, labels=None):
         """Check that a spec list matches input names and directories.
 
         This is used by the following tests to do the dirty work of making
@@ -129,7 +129,6 @@ def test_multiple_suites(self):
 
     def test_path_relative_to_known(self):
         """suites_from_xml handles a relative_to directory attribute."""
-        from os.path import abspath
 
         xml_str = """
 <root>
@@ -145,7 +144,6 @@ def test_path_relative_to_known(self):
 
     def test_path_with_whitespace(self):
         """suites_from_xml handles a directory with whitespace added."""
-        from os.path import abspath
 
         xml_str = """
 <root>
@@ -160,7 +158,6 @@ def test_path_with_whitespace(self):
 
     def test_path_with_label(self):
         """suites_from_xml handles a directory with a label correctly."""
-        from os.path import abspath
 
         xml_str = """
 <root>
diff --git a/tools/unit_testing/python/xml_test_list.py b/scripts/fortran_unit_testing/python/xml_test_list.py
similarity index 96%
rename from tools/unit_testing/python/xml_test_list.py
rename to scripts/fortran_unit_testing/python/xml_test_list.py
--- a/tools/unit_testing/python/xml_test_list.py
+++ b/scripts/fortran_unit_testing/python/xml_test_list.py
@@ -58,7 +58,7 @@ def __iter__(self):
         """
         return ( (l, d) for l, d in zip(self.labels, self.directories) )
 
-def suites_from_xml(xml_tree, known_paths={}):
+def suites_from_xml(xml_tree, known_paths=None):
     """Generate test suite descriptions from XML.
 
     Returns a TestSuiteSpec for each suite description in the XML input.
@@ -77,6 +77,9 @@ def suites_from_xml(xml_tree, known_paths={}):
        the known_paths dict.
     """
 
+    if known_paths is None:
+        known_paths = {}
+
     elements = xml_tree.findall("suite")
 
     for elem in elements:
diff --git a/tools/unit_testing/run_tests.py b/scripts/fortran_unit_testing/run_tests.py
similarity index 99%
rename from tools/unit_testing/run_tests.py
rename to scripts/fortran_unit_testing/run_tests.py
--- a/tools/unit_testing/run_tests.py
+++ b/scripts/fortran_unit_testing/run_tests.py
@@ -4,7 +4,7 @@
 _CIMEROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../..")
 sys.path.append(os.path.join(_CIMEROOT, "scripts", "Tools"))
 sys.path.append(os.path.join(_CIMEROOT, "scripts", "utils", "python"))
-sys.path.append(os.path.join(_CIMEROOT, "tools", "unit_testing", "python"))
+sys.path.append(os.path.join(_CIMEROOT, "scripts", "fortran_unit_testing", "python"))
 
 from standard_script_setup import *
 from CIME.BuildTools.configure import configure
@@ -20,7 +20,6 @@
 #=================================================
 from printer import Printer
 from shutil import rmtree
-from distutils.spawn import find_executable
 # This violates CIME policy - move to CIME/XML directory
 from xml.etree.ElementTree import ElementTree
 
diff --git a/scripts/tests/scripts_regression_tests.py b/scripts/tests/scripts_regression_tests.py
--- a/scripts/tests/scripts_regression_tests.py
+++ b/scripts/tests/scripts_regression_tests.py
@@ -196,7 +196,7 @@ def test_a_unit_test(self):
         test_dir = os.path.join(cls._testroot,"unit_tester_test")
         cls._testdirs.append(test_dir)
         os.makedirs(test_dir)
-        unit_test_tool = os.path.abspath(os.path.join(CIME.utils.get_cime_root(),"tools","unit_testing","run_tests.py"))
+        unit_test_tool = os.path.abspath(os.path.join(CIME.utils.get_cime_root(),"scripts","fortran_unit_testing","run_tests.py"))
         test_spec_dir = os.path.join(os.path.dirname(unit_test_tool),"Examples", "interpolate_1d", "tests")
         run_cmd_no_fail("%s --build-dir %s --test-spec-dir %s"\
                             %(unit_test_tool,test_dir,test_spec_dir))
@@ -214,7 +214,7 @@ def test_b_cime_f90_unit_tests(self):
         cls._testdirs.append(test_dir)
         os.makedirs(test_dir)
         test_spec_dir = CIME.utils.get_cime_root()
-        unit_test_tool = os.path.abspath(os.path.join(test_spec_dir,"tools","unit_testing","run_tests.py"))
+        unit_test_tool = os.path.abspath(os.path.join(test_spec_dir,"scripts","fortran_unit_testing","run_tests.py"))
 
         run_cmd_no_fail("%s --build-dir %s --test-spec-dir %s"\
                             %(unit_test_tool,test_dir,test_spec_dir))
diff --git a/tools/unit_testing/python/comparable.py b/tools/unit_testing/python/comparable.py
deleted file mode 100644
--- a/tools/unit_testing/python/comparable.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""Portable implementation of comparisons for total ordering.
-
-THIS MODULE IS OBSOLETE AS OF PYTHON 2.7.
-
-This module provides a "Comparable" class for subclassing; it provides the
-various "rich comparison" functions (e.g. __le__) based on the subclass's
-__eq__ and __lt__ methods (so this is a Template Method pattern).
-
-Python 2.7 and Python 3 implement a class decorator for this in the
-standard libraries (functools.total_ordering), and that decorator is much
-more flexible (besides being standard), so this should only be used for
-compatibility with older implementations.
-"""
-
-class Comparable(object):
-
-    """Provides rich comparisons for subclasses that have __eq__ and __lt__."""
-
-    def __le__(self, other):
-        return (self == other) and (self < other)
-
-    def __gt__(self, other):
-        return not (self <= other)
-
-    def __ge__(self, other):
-        return not (self < other)
-
-    def __ne__(self, other):
-        return not (self == other)
diff --git a/tools/unit_testing/python/environment.py b/tools/unit_testing/python/environment.py
deleted file mode 100644
--- a/tools/unit_testing/python/environment.py
+++ /dev/null
@@ -1,263 +0,0 @@
-"""Produce commands for environment module systems.
-
-Public classes:
-EnvSystemInterface - Abstract base class for interfaces.
-NoModuleInterface - Can be constructed, but raises exception if used.
-ModuleInterface - For standard module systems (including Lmod).
-SoftEnvInterface - For SoftEnv, the ANL MCS environment system.
-
-Public routines:
-expand_env - Expand shell variables (like "${FOO}").
-"""
-
-import os
-import re
-import subprocess
-
-__all__ = ("EnvSystemInterface", "NoModuleInterface", "ModuleInterface",
-           "SoftEnvInterface", "expand_env")
-
-class EnvSystemInterface(object):
-
-    """Abstract base class for environment system interfaces.
-
-    This class exists only to document the interface of subclasses, and to
-    provide error messages if an unimplemented function is called by
-    accident.
-
-    Normally, none of these methods should actually be called. All of them
-    raise NotImplementedError.
-
-    Public methods:
-    is_loaded
-    purge
-    load
-    unload
-    purge_str
-    load_str
-    unload_str
-    list
-    """
-
-    @classmethod
-    def _raise_not_implemented(cls, method_name):
-        raise NotImplementedError(
-            cls.__name__ +" does not implement method "+method_name+".")
-
-    def is_loaded(self, modname):
-        """Method not implemented."""
-        self._raise_not_implemented("is_loaded")
-
-    def purge(self):
-        """Method not implemented."""
-        self._raise_not_implemented("purge")
-
-    def list(self):
-        """Method not implemented."""
-        self._raise_not_implemented("list")
-
-    def load(self, modname):
-        """Method not implemented."""
-        self._raise_not_implemented("load")
-
-    def unload(self, modname):
-        """Method not implemented."""
-        self._raise_not_implemented("unload")
-
-    def purge_str(self):
-        """Method not implemented."""
-        self._raise_not_implemented("purge_str")
-
-    def load_str(self, modname):
-        """Method not implemented."""
-        self._raise_not_implemented("load_str")
-
-    def unload_str(self, modname):
-        """Method not implemented."""
-        self._raise_not_implemented("unload_str")
-
-
-class NoModuleInterface(EnvSystemInterface):
-
-    """Module interface for systems with no module system.
-
-    The purpose of this class is to allow code to construct a module
-    interface regardless of system, while still raising an error if the
-    user attempts to actually interact with the non-existent interface.
-
-    As a result, only purge_str is implemented.
-
-    Public methods:
-    purge_str - Returns null command.
-    """
-
-    def purge_str(self):
-        """Returns ":", the null shell command."""
-        return ":"
-
-
-class ModuleInterface(EnvSystemInterface):
-
-    """Module interface for systems with a typical module system.
-
-    Class methods:
-    python_init - Initialize python interface.
-
-    Public methods:
-    is_loaded - Tests if a module is currently loaded.
-    purge - Purge all modules from environment.
-    load - Load a module.
-    unload - Unload a module.
-    list - list currently loaded modules.
-    purge_str - Return command to purge modules.
-    load_str - Return command to load the given module.
-    unload_str  - Return command to unload the given module.
-    """
-
-    # Singleton declaring whether or not we have the module interface
-    # started up yet.
-    _python_initialized = False
-
-    # Magic string to grep for in "module list" output to determine whether
-    # or not a module is present.
-    _not_loaded_string = "None found"
-
-    @classmethod
-    def python_init(cls, filename):
-        """Initialize python to module system interface.
-
-        This must be called before any commands that are intended to
-        modify python's environment. The *_str methods can still be called
-        without this. Only the first call has any effect.
-
-        Arguments:
-        filename - Python file to execute to load the interface.
-        """
-        if not cls._python_initialized:
-            execfile(filename, globals())
-            cls._python_initialized = True
-
-    def is_loaded(self, modname):
-        """"Whether the module is loaded in the current environment."""
-        # This assertion message isn't quite true, but this behavior is
-        # convenient for testing purposes, which is why is_loaded exists.
-        assert self._python_initialized, \
-            "Can't test modules without initializing the python interface!"
-        process = subprocess.Popen("module list "+modname,
-                                   shell=True,
-                                   env=os.environ,
-                                   stderr=subprocess.PIPE)
-        stderr_output = process.communicate()[1]
-        # Handle unexpected error in subprocess.
-        if process.returncode not in (0,1):
-            raise Exception("module list command failed with code: "+
-                            str(process.returncode))
-        # Hack! Assume that process.returncode == 1 means that no modules
-        # are loaded, rather than some other possible error.
-        return process.returncode != 1 and \
-            re.search(self._not_loaded_string, stderr_output) is None
-
-    def purge(self):
-        """"Purge all modules from the current environment."""
-        assert self._python_initialized, \
-            "Can't purge modules without initializing the python interface!"
-        module("purge")
-
-    def list(self):
-        """"List all modules from the current environment."""
-        assert self._python_initialized, \
-            "Can't list modules without initializing the python interface!"
-        module("list")
-
-    def load(self, modname):
-        """"Load a module in the current environment."""
-        assert self._python_initialized, \
-            "Can't load modules without initializing the python interface!"
-        module("load", modname)
-
-    def unload(self, modname):
-        """"Unload a module from the current environment."""
-        assert self._python_initialized, \
-            "Can't unload modules without initializing the python interface!"
-        module("unload", modname)
-
-    def purge_str(self):
-        """Returns the shell command to purge modules as a string."""
-        return "module purge"
-
-    def load_str(self, modname):
-        """Returns the shell command to load the module as a string."""
-        return "module load "+modname
-
-    def unload_str(self, modname):
-        """Returns the shell command to unload the module as a string."""
-        return "module unload "+modname
-
-
-class SoftEnvInterface(EnvSystemInterface):
-
-    """Module interface for systems with SoftEnv.
-
-    Public methods:
-    purge_str - Return command to reset SoftEnv.
-    load_str - Return command to add the given keyword/macro.
-    unload_str  - Return command to delete the given keyword/macro.
-    """
-
-    def purge_str(self):
-        """Returns the shell command to reset SoftEnv as a string.
-
-        Unfortunately, there's not a straightforward way to clear
-        everything, so right now we just run "resoft". If it becomes
-        necessary in the future, this may change to do something else (e.g.
-        resoft using a custom file with only default settings).
-        """
-        return "resoft"
-
-    def load_str(self, modname):
-        """Returns the shell command to add the keyword as a string."""
-        return "soft add "+modname
-
-    def unload_str(self, modname):
-        """Returns the shell command to delete the keyword as a string."""
-        return "soft delete "+modname
-
-
-# Regex that matches an environment variable reference, putting the name in
-# the "name" group of the match.
-_env_re = re.compile(
-    """\$                      # Initial "$"
-       (?P<brace>\{)?          # Open brace if present
-       (?P<name>[A-Za-z0-9_]+) # Variable name
-       (?(brace)\})            # Close brace if necessary""", re.X)
-
-
-def expand_env(string, env):
-    """Expand a shell string with given environment.
-
-    Arguments:
-    string - String to expand as if in a shell.
-    env - dict specifying environment variables.
-
-    This is very limited; right now it only handles variable substitution,
-    and only with "${FOO}" or "$FOO" style syntax. The variable name must
-    contain only alphanumeric characters and "_". There is no way to escape
-    a "$".
-
-    Expansion is recursive; the output of this function is its fixed point.
-    """
-    def expand_func(match):
-        """Given an environment variable match, return replacement text."""
-        var_name = match.group("name")
-        if var_name in env:
-            return env[var_name]
-        else:
-            # If there's no match in the environment, return original text.
-            return match.group(0)
-    old_string = ""
-    new_string = string
-    while new_string != old_string:
-        old_string = new_string
-        new_string = _env_re.sub(expand_func, new_string)
-
-    return new_string
diff --git a/tools/unit_testing/python/machine_setup.py b/tools/unit_testing/python/machine_setup.py
deleted file mode 100644
--- a/tools/unit_testing/python/machine_setup.py
+++ /dev/null
@@ -1,296 +0,0 @@
-"""Functions to set up machine-specific settings.
-
-Currently, this module is not under unit test, because it interacts too
-directly with the OS of the machine it is on. Any changes to address this
-would be welcome (this would likely require the use of mock modules,
-and/or a specific set of machines to test on).
-
-Public classes:
-MachineCompilerSettings - Set up machine/compiler specific environment.
-"""
-
-# Python 3 compatible printing in Python 2.
-from __future__ import print_function
-
-from itertools import chain
-from os import environ
-import os.path
-import platform
-import re
-import subprocess
-from xml.etree.ElementTree import ElementTree
-
-import environment as ENV
-from printer import ScriptPrinter
-from xml_utils import best_match, all_matches
-__all__ = ("MachineCompilerSettings")
-
-
-def get_machine_name():
-    """Returns a canonical version of the machine name."""
-    name = platform.node()
-    # Strip everything after the first ".", and whitespace.
-    name = name.split(".")[0].strip()
-    if re.match("^yslogin[0-9]+", name):
-        name = "yellowstone"
-    elif re.match("^miralac[0-9]+", name):
-        name = "mira"
-    elif re.match("^cetuslac[0-9]+", name):
-        name = "mira"
-    elif re.match("^caldera.*", name) or re.match("^pronghorn.*", name):
-        # Use yellowstone settings for caldera/pronghorn, since the mahcines
-        # files aren't set up explicitly for those machines, and they have the
-        # same configuration as yellowstone.
-        name = "yellowstone"
-    return name
-
-def load_machine_env(compiler):
-    """Add machine environment variables not in config_compilers.xml
-
-    Besides simply setting variables, this may also load some modules.
-    """
-
-    mach = get_machine_name()
-
-    if mach == "yellowstone":
-        mod = ENV.ModuleInterface()
-        mod.python_init("/glade/apps/opt/lmod/lmod/init/env_modules_python.py")
-        mod.purge()
-        mod.load("ncarenv/1.0")
-        mod.load("ncarbinlibs/1.0")
-        if compiler == "intel":
-            mod.load("intel/15.0.1")
-            mod.load("mkl/11.1.2")
-        elif compiler == "pgi":
-            mod.load("pgi/13.9")
-        mod.load("ncarcompilers/1.0")
-        mod.load("cmake/2.8.10.2")
-        mod.load("netcdf/4.3.0")
-
-
-class MachineCompilerSettings(object):
-
-    """Encapsulate machine settings and set environment from them.
-
-    Public methods:
-    __init__ - Discover information about local machine.
-    compiler_xml_to_env - Set a specific variable using config_compilers.
-    set_compiler_env - Set up machine-specific environment.
-    write_cmake_macros - Create CMake "Macros" file.
-    """
-
-    def __init__(self, compiler, compiler_xml_path,
-                 machine=None,
-                 use_env_compiler=False,
-                 mpilib=None, use_openmp=False):
-        """Discover information about the machine and compiler.
-
-        Arguments:
-        compiler - String naming the compiler vendor.
-        compiler_xml_path - Path to config_compilers.xml file.
-        machine - String naming the machine (guessed if not provided)
-        use_env_compiler - Force use of environment variable FC instead of
-                           using the compiler in config_compilers.xml.
-        mpilib - String naming the mpi library if any.
-        use_openmp - Boolean option to use OpenMP compiler flags.
-                     Currently only works for CESM/CESM_DEBUG builds.
-        """
-        if machine is None:
-            machine = get_machine_name()
-        self.machine_dict = {
-            "COMPILER": compiler,
-            "MACH": machine,
-            "OS": platform.system(),
-            "compile_threaded": "true" if use_openmp else "false",
-            }
-        if mpilib:
-            self.machine_dict["MPILIB"] = mpilib
-        else:
-            self.machine_dict["MPILIB"] = 'mpi-serial'
-
-        self.compiler_xml_tree = ElementTree()
-        self.compiler_xml_tree.parse(compiler_xml_path)
-        self.use_env_compiler = use_env_compiler
-        self.mpilib = mpilib
-
-    def compiler_xml_to_env(self, xml_path, var_name):
-        """Look up a config_compilers entry and set a variable from it.
-
-        Arguments:
-        xml_path - Path within the xml file (e.g. "compiler/SFC").
-        var_name - Name of environment variable (e.g. "FC").
-        """
-        match = best_match(self.compiler_xml_tree, xml_path,
-                           self.machine_dict)
-        assert match is not None, "Could not determine "+var_name+ \
-            " from compiler/machines xml file."
-        environ[var_name] = match.text.strip()
-
-    def add_path(self, name, macros_printer):
-        match = best_match(self.compiler_xml_tree, "compiler/"+name+"_PATH",
-                           self.machine_dict)
-        if match is not None:
-            macros_printer.print_header(name + " location.")
-            libpath = match.text
-            _make_env_re = re.compile(
-                """\$\(                    # Initial "$" and brace
-                (?P<name>[A-Za-z0-9_]+) # Variable name
-                \)                      # Close brace""", re.X)
-            libpath = _make_env_re.sub( "$ENV{\g<name>}",libpath)
-            print("libpath = "+libpath)
-            macros_printer.print(
-                "list(APPEND CMAKE_PREFIX_PATH "+libpath+")"
-                )
-
-    def set_compiler_env(self):
-        """Set up the environment for this machine and compiler."""
-        if not self.use_env_compiler:
-            if (self.mpilib == "mpi-serial" or self.mpilib is None):
-                self.compiler_xml_to_env("compiler/SFC", "FC")
-                self.compiler_xml_to_env("compiler/SCC", "CC")
-            else:
-                self.compiler_xml_to_env("compiler/MPIFC", "FC")
-                self.compiler_xml_to_env("compiler/MPICC", "CC")
-        load_machine_env(self.machine_dict["COMPILER"])
-
-    def write_cmake_macros(self, macros_file, model="CESM"):
-        """Write CMake macros file using config_compilers.xml
-
-        Arguments:
-        macros_file - File object to write to.
-        """
-
-        # Print header to file.
-        macros_printer = ScriptPrinter(macros_file)
-        header_lines = [model+
-            " build flags for:",
-            "  Compiler = "+self.machine_dict["COMPILER"],
-            "  Machine = "+self.machine_dict["MACH"],
-            "  OS = "+self.machine_dict["OS"],
-            " MPILIB = "+self.machine_dict["MPILIB"],
-            ]
-
-        for line in header_lines:
-            macros_printer.comment(line)
-
-        macros_printer.print("include(Compilers)")
-        macros_printer.print(
-            "set(CMAKE_Fortran_FLAGS_"+model+" \"\" CACHE STRING \"Flags used by the Fortran compiler during builds.\" FORCE)")
-        macros_printer.print(
-            "set(CMAKE_Fortran_FLAGS_"+model+"_DEBUG \"\" CACHE STRING \"Flags used by the Fortran compiler during DEBUG builds.\" FORCE)")
-
-
-        macros_printer.print(
-            "set(CMAKE_C_FLAGS_"+model+" \"\" CACHE STRING \"Flags used by the C compiler during builds.\" FORCE)")
-        macros_printer.print(
-            "set(CMAKE_C_FLAGS_"+model+"_DEBUG \"\" CACHE STRING \"Flags used by the C compiler during DEBUG builds.\" FORCE)")
-
-        macros_printer.print(
-            "mark_as_advanced(CMAKE_Fortran_FLAGS_"+model+" CMAKE_Fortran_FLAGS_"
-            +model+"_DEBUG)")
-        macros_printer.print(
-            "mark_as_advanced(CMAKE_C_FLAGS_"+model+" CMAKE_C_FLAGS_"+model+"_DEBUG)")
-        macros_printer.print(
-            "set(all_build_types \"None Debug Release RelWithDebInfo MinSizeRel "+model+
-            " "+model+"_DEBUG\")")
-        macros_printer.print(
-            "set(CMAKE_BUILD_TYPE \"${CMAKE_BUILD_TYPE}\" CACHE STRING \"Choose the type of build, options are: ${all_build_types}.\" FORCE)")
-
-
-
-
-        # pFUnit location if it exists.
-        self.add_path("PFUNIT",macros_printer)
-        # NETCDF location if it exists.
-        self.add_path("NETCDF",macros_printer)
-        # PNETCDF location if it exists.
-        self.add_path("PNETCDF", macros_printer)
-        # HDF5 location if it exists.
-        self.add_path("HDF5", macros_printer)
-        # MPI location if it exists.
-        self.add_path("MPI", macros_printer)
-        # PETSc location if it exists.
-        self.add_path("PETSC", macros_printer)
-       # TRILINOS location if it exists.
-        self.add_path("TRILINOS", macros_printer)
-        # ALBANY location if it exists.
-        self.add_path("ALBANY", macros_printer)
-
-
-        # Normal and debug dictionaries for looking things up in
-        # config_compilers.
-        normal_dict = self.machine_dict.copy()
-        normal_dict["DEBUG"] = "FALSE"
-
-        debug_dict = self.machine_dict.copy()
-        debug_dict["DEBUG"] = "TRUE"
-
-        def add_formatted_flags(flags_name, format):
-            """Print CMake flags using macros_printer.
-
-            Arguments:
-            flags_name - Name to search for in config_compilers.
-            format - Function that takes a build type and flag match, and
-            returns the string to print out.
-            """
-
-            paths = ["compiler/"+flags_name, "compiler/ADD_"+flags_name]
-
-            # This creates an iterable over elements in config_compilers
-            # that match in non-debug mode.
-            normal_matches = chain.from_iterable(
-                all_matches(self.compiler_xml_tree, path, normal_dict)
-                for path in paths
-                )
-            for match in normal_matches:
-                macros_printer.print(format(model, match.text))
-
-            # Now the same for debug mode.
-            debug_matches = chain.from_iterable(
-                all_matches(self.compiler_xml_tree, path, debug_dict)
-                for path in paths
-                )
-            for match in debug_matches:
-                macros_printer.print(format(model+"_DEBUG", match.text))
-
-        # Below, we just use a bunch of lambda functions to describe how
-        # the build type and a matching element (e.g. an FFLAGS entry) are
-        # turned into a CMake function call.
-
-        macros_printer.print_header("CPP definitions.")
-        add_formatted_flags(
-            "CPPDEFS",
-            lambda b, m: "add_config_definitions("+b+" "+m+")"
-            )
-        def format_contiguous(build_type, match):
-            comma = "," if self.machine_dict["COMPILER"] != "ibm" else "\\\,"
-            contig_def = "contiguous"+comma if match == "TRUE" else ""
-            return "add_config_definitions("+build_type+\
-                " -DUSE_CONTIGUOUS="+contig_def+")"
-        add_formatted_flags(
-            "HAS_F2008_CONTIGUOUS",
-            format_contiguous
-            )
-
-        macros_printer.print_header("Fortran flags.")
-        add_formatted_flags(
-            "FFLAGS",
-            lambda b, m: "add_flags(CMAKE_Fortran_FLAGS_"+b+" "+m+")"
-            )
-
-        macros_printer.print_header("C flags.")
-        add_formatted_flags(
-            "CFLAGS",
-            lambda b, m: "add_flags(CMAKE_C_FLAGS_"+b+" "+m+")"
-            )
-
-        macros_printer.print_header("Linker flags.")
-        add_formatted_flags(
-            "LDFLAGS",
-            lambda b, m: "add_flags(CMAKE_EXE_LINKER_FLAGS_"+b+" "+m+")"
-            )
-        macros_printer.print_header("External library flags.")
-        add_formatted_flags(
-            "SLIBS",
-            lambda b, m: "add_flags(CMAKE_EXE_LINKER_FLAGS_"+b+" "+m+")"
-            )
diff --git a/tools/unit_testing/python/test_environment.py b/tools/unit_testing/python/test_environment.py
deleted file mode 100755
--- a/tools/unit_testing/python/test_environment.py
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/env python
-"""Unit tests for the environment module.
-
-Public classes:
-TestEnvSys - Test the EnvSystemInterface abstract base class.
-TestNoMod - NoModuleInterface tests.
-TestMod - ModuleInterface tests.
-TestSoft - SoftEnvInterface tests.
-TestExpandEnv - expand_env tests.
-
-To test the actual module system, you must specify these environment
-variables when running the test:
-MODULE_SYSTEM - Type of module system available locally.
-TEST_MODULE - Name of a module which is *not* loaded, to use for testing
-              purposes.
-MODULE_FILE - File that must be executed to load the interface to python
-              (if any).
-"""
-
-from os import environ
-import unittest
-
-from environment import *
-
-__all__ = ("TestEnvSys", "TestNoMod", "TestMod", "TestSoft",
-           "TestExpandEnv")
-
-# Test data gleaned from the environment.
-module_system = "none"
-test_module = "foo"
-module_file = None
-if "MODULE_SYSTEM" in environ:
-    module_system = environ["MODULE_SYSTEM"]
-    assert "TEST_MODULE" in environ, \
-        "MODULE_SYSTEM is set, but TEST_MODULE is not defined."
-    test_module = environ["TEST_MODULE"]
-    if module_system == "module":
-        assert "MODULE_FILE" in environ, \
-            "MODULE_SYSTEM is module, but MODULE_FILE is not defined."
-        module_file = environ["MODULE_FILE"]
-
-
-class TestEnvSys(unittest.TestCase):
-
-    """Tests for the EnvSystemInterface class.
-
-    This tests that each method is NotImplemented. Other test classes in
-    this module inherit from this one in order to detect when a method has
-    been implemented but no test is defined for the new method.
-
-    However, figuring out which class causes the error is a bit of a
-    guessing game. Addressing that issue is difficult in Python 2.6 because
-    of how assertRaises works.
-    """
-
-    test_class = EnvSystemInterface
-
-    def setUp(self):
-        """Set up by creating an instance of the class under test."""
-        self.test_obj = self.test_class()
-
-    def test_is_loaded(self):
-        """is_loaded raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.is_loaded, test_module)
-
-    def test_purge(self):
-        """purge raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.purge)
-
-    def test_load(self):
-        """load raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.load, test_module)
-
-    def test_purge_str(self):
-        """purge_str raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.purge_str)
-
-    def test_load_str(self):
-        """load_str raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.load_str, test_module)
-
-    def test_unload_str(self):
-        """unload_str raises NotImplementedError."""
-        self.assertRaises(NotImplementedError,
-                          self.test_obj.unload_str, test_module)
-
-
-class TestNoMod(TestEnvSys):
-
-    """Tests for the NoModuleInterface class."""
-
-    test_class = NoModuleInterface
-
-    def test_purge_str(self):
-        """User can call NoModuleInterface.purge_str and get a ":"."""
-        self.assertEqual(self.test_obj.purge_str(), ":")
-
-
-class TestMod(TestEnvSys):
-
-    """Tests for the ModuleInterface class.
-
-    It's not easy to verify that this behaves correctly, so in many cases
-    we do some kind of check for self consistency only. If module_system
-    is not "module", then most of the tests look for an exception.
-    """
-
-    test_class = ModuleInterface
-
-    def setUp(self):
-        """Set up module system if present, then call parent setUp."""
-        if module_system == "module":
-            ModuleInterface.python_init(module_file)
-        super(TestMod, self).setUp()
-
-    def tearDown(self):
-        """Remove the test module from the current environment."""
-        if module_system == "module":
-            self.test_obj.unload(test_module)
-
-    def test_is_loaded(self):
-        """ModuleInterface.is_loaded returns false for unloaded module."""
-        if module_system == "module":
-            self.assertFalse(self.test_obj.is_loaded(test_module))
-        else:
-            self.assertRaises(AssertionError,
-                              self.test_obj.is_loaded, test_module)
-
-    def test_purge(self):
-        """ModuleInterface.purge removes the test module."""
-        if module_system == "module":
-            self.test_obj.load(test_module)
-            self.test_obj.purge()
-            self.assertFalse(self.test_obj.is_loaded(test_module))
-        else:
-            self.assertRaises(AssertionError,
-                              self.test_obj.purge)
-
-    def test_load(self):
-        """ModuleInterface.load loads a module."""
-        if module_system == "module":
-            self.test_obj.load(test_module)
-            self.assertTrue(self.test_obj.is_loaded(test_module))
-        else:
-            self.assertRaises(AssertionError,
-                              self.test_obj.load, test_module)
-
-    def test_unload(self):
-        """ModuleInterface.unload unloads a module."""
-        if module_system == "module":
-            self.test_obj.load(test_module)
-            self.test_obj.unload(test_module)
-            self.assertFalse(self.test_obj.is_loaded(test_module))
-        else:
-            self.assertRaises(AssertionError,
-                              self.test_obj.unload, test_module)
-
-    def test_purge_str(self):
-        """User gets non-null string from ModuleInterface.purge_str."""
-        self.assertNotEqual(self.test_obj.purge_str().strip(),
-                            "")
-
-    def test_load_str(self):
-        """User gets non-null string from ModuleInterface.load_str."""
-        self.assertNotEqual(self.test_obj.load_str(test_module).strip(),
-                            "")
-
-    def test_unload_str(self):
-        """User gets non-null string from ModuleInterface.unload_str."""
-        self.assertNotEqual(self.test_obj.unload_str(test_module).strip(),
-                            "")
-
-
-class TestSoft(TestEnvSys):
-
-    """Tests for the SoftEnvInterface class.
-
-    It's not easy to verify that this behaves correctly without a specific
-    test machine in mind. As with the module tests, we really just verify
-    that no exceptions are raised and the return isn't the null string.
-    """
-
-    test_class = SoftEnvInterface
-
-    def test_purge_str(self):
-        """User gets non-null string from SoftEnvInterface.purge_str."""
-        self.assertNotEqual(self.test_obj.purge_str().strip(), "")
-
-    def test_load_str(self):
-        """User gets non-null string from SoftEnvInterface.load_str."""
-        self.assertNotEqual(self.test_obj.load_str(test_module).strip(),
-                            "")
-
-    def test_unload_str(self):
-        """User gets non-null string from SoftEnvInterface.unload_str."""
-        self.assertNotEqual(self.test_obj.unload_str(test_module).strip(),
-                            "")
-
-
-class TestExpandEnv(unittest.TestCase):
-
-    """Tests for the expand_env function."""
-
-    def test_no_variable(self):
-        """With no variables, expand_env is a no-op."""
-        self.assertEqual(expand_env("foo", {"UNUSED": "not used"}), "foo")
-
-    def test_variable_missing(self):
-        """With variables not present, expand_env is a no-op."""
-        self.assertEqual(expand_env("${NOT_HERE}", {}), "${NOT_HERE}")
-
-    def test_brace_expansion(self):
-        """Test that an expansion works with curly braces."""
-        self.assertEqual(expand_env("${FOO}", {"FOO": "bar"}), "bar")
-
-    def test_bare_expansion(self):
-        """Test that an expansion works with no braces."""
-        self.assertEqual(expand_env("$FOO", {"FOO": "bar"}), "bar")
-
-    def test_recursive_expansion(self):
-        """Test that expansion is done recursively."""
-        self.assertEqual(expand_env("${FOO}",
-                                    {"FOO": "${FOO2}", "FOO2": "bar"}),
-                         "bar")
-
-    def test_brace_closing(self):
-        """Test that braces must be closed for expansion to occur."""
-        self.assertEqual(expand_env("${FOO", {"FOO": "bar"}), "${FOO")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tools/unit_testing/python/test_xml_utils.py b/tools/unit_testing/python/test_xml_utils.py
deleted file mode 100755
--- a/tools/unit_testing/python/test_xml_utils.py
+++ /dev/null
@@ -1,294 +0,0 @@
-#!/usr/bin/env python
-"""Unit tests for the xml_utils module.
-
-Public classes:
-TestBestMatch - best_match tests.
-TestAllMatches - all_matches tests.
-TestElementsToDict - elements_to_dict tests.
-"""
-
-import unittest
-from xml.etree.ElementTree import XML, ElementTree
-
-from xml_utils import best_match, all_matches, elements_to_dict
-
-__all__ = ("TestBestMatch", "TestAllMatches", "TestElementsToDict")
-
-class TestBestMatch(unittest.TestCase):
-
-    """Tests for the best_match function."""
-
-    def setUp(self):
-        """Create an ElementTree object as test data.
-
-        This is a complex set of data, but the tests on it are
-        comparatively simple. Some of the data (such as the empty "foo"
-        tags) are present purely to prove that they are never matched.
-        """
-
-        root = XML("""
-<root>
- <foo a="1">
-  <data2>a</data2>
-  <data3>a</data3>
- </foo>
- <foo>
-  <data1>generic</data1>
-  <data2>generic</data2>
-  <data3>generic</data3>
- </foo>
- <foo b="2">
-  <data2>b</data2>
-  <data3>b</data3>
- </foo>
- <foo a="1" b="2">
-  <data3>ab</data3>
- </foo>
- <foo a="2" />
- <foo b="1" />
- <foo a="1" b="1" />
- <foo a="2" b="1" />
- <bar a="2">
-  <data1>abar</data1>
- </bar>
- <bar b="1">
-  <data1>bbar</data1>
- </bar>
-</root>
-""")
-
-        self.xml_tree = ElementTree(root)
-
-    def test_no_match(self):
-        """best_match returns None when no paths match."""
-
-        self.assertTrue(best_match(self.xml_tree, "invalid")
-                        is None)
-
-    def test_simple_match(self):
-        """best_match can find the only match, when no attributes are used."""
-
-        self.assertEqual(
-            "generic",
-            best_match(self.xml_tree, "foo/data1").text
-            )
-
-    def test_no_match_attr(self):
-        """best_match returns None when there are no attribute matches."""
-
-        self.assertTrue(best_match(self.xml_tree, "bar/data1")
-                        is None)
-        self.assertTrue(best_match(self.xml_tree, "bar/data1", {"a": "1"})
-                        is None)
-
-    def test_match_with_attr(self):
-        """best_match returns the only path match, with matching attribute."""
-
-        self.assertEqual(
-            "abar",
-            best_match(self.xml_tree, "bar/data1", {"a": "2"}).text
-            )
-
-    def test_match_on_attr(self):
-        """best_match returns the only path and attribute match."""
-
-        self.assertEqual(
-            "b",
-            best_match(self.xml_tree, "foo/data2",
-                       {"a": "2", "b": "2"}).text
-            )
-
-    def test_match_most_specific(self):
-        """best_match returns the most specific match for each path."""
-
-        self.assertEqual(
-            "generic",
-            best_match(self.xml_tree, "foo/data1",
-                       {"a": "1", "b": "2"}).text
-            )
-        self.assertEqual(
-            "ab",
-            best_match(self.xml_tree, "foo/data3",
-                       {"a": "1", "b": "2"}).text
-            )
-
-    def test_match_first(self):
-        """best_match returns the first matching entry."""
-
-        self.assertEqual(
-            "abar",
-            best_match(self.xml_tree, "bar/data1",
-                       {"a": "2", "b": "1"}).text
-            )
-
-
-class TestAllMatches(unittest.TestCase):
-
-    """Tests for the all_matches function."""
-
-    def setUp(self):
-        """Create an ElementTree for use as test data."""
-
-        root = XML("""
-<root>
- <test1>
-  <data>the_text</data>
- </test1>
- <test2>
-  <data>first_text</data>
-  <data>second_text</data>
- </test2>
- <test3>
- </test3>
- <test4>
-  <data invalid="true">first_text</data>
-  <data valid="false">second_text</data>
-  <data valid="true" invalid="true">second_text</data>
- </test4>
- <test5>
-  <data invalid="false">first_text</data>
-  <data valid="true">second_text</data>
-  <data>third_text</data>
-  <data valid="true" invalid="false">fourth_text</data>
-  <data valid="true" invalid="true">bad_text</data>
- </test5>
- <test6>
-  <data ignore="ignore this">the_text</data>
- </test6>
-</root>
-""")
-
-        self.xml_tree = ElementTree(root)
-
-    def test_one_match(self):
-        """all_matches returns one element successfully."""
-        self.assertEqual(
-            [e.text for e in all_matches(self.xml_tree, "test1/data")],
-            ["the_text"],
-            )
-
-    def test_multiple_matches(self):
-        """all_matches returns many elements successfully.
-
-        Elements must be returned in the right order as well.
-        """
-        self.assertEqual(
-            [e.text for e in all_matches(self.xml_tree, "test2/data")],
-            ["first_text", "second_text"],
-            )
-
-    def test_no_matches(self):
-        """all_matches returns no elements if none are found."""
-        self.assertEqual(
-            [e.text for e in all_matches(self.xml_tree, "test3/data")],
-            [],
-            )
-
-    def test_no_attr_matches(self):
-        """all_matches returns no elements if none match attributes."""
-        self.assertEqual(
-            [e.text for e in all_matches(self.xml_tree, "test4/data",
-                                         {"valid": "true"})],
-            [],
-            )
-
-    def test_attr_matches(self):
-        """all_matches returns elements that match attributes."""
-        self.assertEqual(
-            [e.text for e in
-             all_matches(self.xml_tree, "test5/data",
-                         {"valid": "true", "invalid": "false"})],
-            ["first_text", "second_text", "third_text", "fourth_text"],
-            )
-
-    def test_ignore_attribute(self):
-        """all_matches can ignore "extra" attributes."""
-        self.assertEqual(
-            [e.text for e in
-             all_matches(self.xml_tree, "test6/data",
-                         ignore=["ignore"])],
-            ["the_text"]
-            )
-
-
-# In true TDD form, these tests are far longer than the code they apply to!
-# Of course, when they were written, the first version of the code was
-# longer.
-class TestElementsToDict(unittest.TestCase):
-
-    """Tests for the elements_to_dict function."""
-
-    @staticmethod
-    def string_to_elements(xml_str, ignore="key"):
-        """Converts an XML string to a list of its "ENV" elements."""
-        return all_matches(ElementTree(XML(xml_str)), "ENV",
-                           ignore=ignore)
-
-    def test_no_elements(self):
-        """elements_to_dict with no elements produces an empty dict."""
-        self.assertEqual(elements_to_dict([]), {})
-
-    def test_one_element(self):
-        """elements_to_dict can handle one element."""
-        elements = self.string_to_elements("""
-<root>
- <ENV key="foo">bar</ENV>
-</root>
-""")
-        self.assertEqual(elements_to_dict(elements), {"foo": "bar"})
-
-    def test_multiple_elements(self):
-        """elements_to_dict can handle multiple elements."""
-        elements = self.string_to_elements("""
-<root>
- <ENV key="foo1">bar1</ENV>
- <ENV key="foo2">bar2</ENV>
-</root>
-""")
-        self.assertEqual(elements_to_dict(elements),
-                         {"foo1": "bar1", "foo2": "bar2"})
-
-    def test_key_attr(self):
-        """elements_to_dict uses key_attr as key attribute."""
-        key_attr = "name"
-        xml_str = """
-<root>
- <ENV name="foo">bar</ENV>
-</root>
-"""
-        elements = self.string_to_elements(xml_str, ignore=key_attr)
-        self.assertEqual(elements_to_dict(elements, key_attr=key_attr),
-                         {"foo": "bar"})
-
-    def test_multiple_key(self):
-        """elements_to_dict lets later values overwrite earlier ones.
-
-        This might not actually be the best behavior, but it's the easiest
-        to implement; this test is really just to prevent *accidentally*
-        changing the design.
-        """
-        elements = self.string_to_elements("""
-<root>
- <ENV key="foo">bar1</ENV>
- <ENV key="foo">bar2</ENV>
-</root>
-""")
-        self.assertEqual(elements_to_dict(elements),
-                         {"foo": "bar2"})
-
-    def test_keyless_match(self):
-        """elements_to_dict with a match with no key returns empty dict.
-
-        It might be better to raise an exception; this is a robustness and
-        simplicity vs. correctness tradeoff.
-        """
-        elements = self.string_to_elements("""
-<root>
- <ENV>bar</ENV>
-</root>
-""")
-        self.assertEqual(elements_to_dict(elements), {})
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tools/unit_testing/python/xml_utils.py b/tools/unit_testing/python/xml_utils.py
deleted file mode 100644
--- a/tools/unit_testing/python/xml_utils.py
+++ /dev/null
@@ -1,202 +0,0 @@
-"""XML search utilities.
-
-These are based on the standard library xml.etree.ElementTree module.
-However, that module is not explicitly imported at this time, so it is in
-principle possible to pass arguments that imitate the interface of that
-module.
-
-Exported functions:
-best_match - Search for a specific element in an XML tree.
-all_matches - Search for all matching elements in an XML tree.
-elements_to_dict - Create a dict from XML entries with a key attribute.
-"""
-
-from comparable import Comparable
-
-__all__ = ("best_match", "all_matches", "elements_to_dict")
-
-class ElementMatch(Comparable):
-
-    """Class to aid searching for/matching an xml element.
-
-    Public methods:
-    __init__ - Create an ElementMatch.
-    __iadd__ - Add in "quality" of other match.
-    __eq__ - Test if quality is same as another match.
-    __lt__ - Compare quality to another match.
-    __nonzero__/__bool__ - Test for a valid match.
-
-    Public data:
-    element - Element for the match (None if no match). Read-only.
-
-    Note that using __eq__ to test quality interferes with hashable
-    collections, so for now, __hash__ = None.
-    """
-
-    def __init__(self, element=None, quality=0):
-        """Define a element match.
-
-        Arguments:
-        element - The element that was found.
-        quality - Abstract measure of "quality" (specificity) of the match.
-
-        With both options left out, this will return a null ElementMatch.
-        """
-        self._element = element
-        self._quality = quality
-
-    def __iadd__(self, other):
-        """Add the quality from another match to that of this match."""
-        self._quality += other._quality
-        return self
-
-    __hash__ = None
-    """Not implemented, because __eq__ is overridden."""
-
-    def __eq__(self, other):
-        """Check whether two matches have the same quality."""
-        return (not self and not other) or \
-            (self and other and self._quality == other._quality)
-
-    def __lt__(self, other):
-        """Compare matches by their quality; null matches always lose."""
-        return (not self and other) or \
-            (self and other and self._quality < other._quality)
-
-    def __bool__(self):
-        """Test if this is a real match or just the null one."""
-        return self._element is not None
-
-    __nonzero__ = __bool__
-
-    @property
-    def element(self):
-        """Element from a valid match, or None for a null match."""
-        return self._element
-
-# Right now this is treated as private to the module. But it's getting big
-# enough to justify having its own tests, maybe.
-def _element_attribute_match(element, attributes, ignore=[]):
-    """Check an element to see if it matches the given attributes.
-
-    If an element passes the check, give it a "quality" corresponding to
-    the number of matched attributes. Otherwise, return null match.
-
-    The ignore attribute specifies attributes to ignore.
-    """
-    match_quality = 0
-    for key in element.keys():
-        if key in ignore:
-            continue
-        if key in attributes and attributes[key] == element.get(key):
-            match_quality += 1
-        else:
-            return ElementMatch()
-    return ElementMatch(element, match_quality)
-
-def best_match(xml_tree, path, attributes={}):
-    """Find the best match for a path with attributes in an XML tree.
-
-    The return value is the matched element.
-
-    Arguments:
-    xml_tree - A tree from the xml.etree.ElementTree module.
-    path - The path to search for.
-    attributes - A dict containing attributes to match. Not all attributes
-                 must be present on an element to get a match, but any
-                 attributes present must match this input, and the "best"
-                 match has the most matches.
-
-    The attributes argument defaults to an empty dict.
-    """
-
-    # Recursive function to find a match.
-    def find_best_below(element, path):
-        # Done when there's no more of the path to match.
-        if len(path) == 0:
-            return ElementMatch(element)
-
-        # Otherwise, get the beginning part of the path.
-        path_head, slash, path_tail = path.partition("/")
-
-        # Search through subelements that match the next part of the path.
-        # For each one, get the quality of the match based on the number
-        # of matching attributes, then call self recursively to get
-        # best match from subelements.
-        best_match = ElementMatch()
-        for trial_element in element.findall(path_head):
-            local_match = _element_attribute_match(trial_element,
-                                                   attributes)
-            if local_match:
-                new_match = \
-                    find_best_below(trial_element, path_tail)
-                new_match += local_match
-                if best_match < new_match:
-                    best_match = new_match
-
-        return best_match
-
-    # Do the search.
-    element = xml_tree.getroot()
-    match = find_best_below(element, path)
-
-    return match.element
-
-def all_matches(xml_tree, path, attributes={}, ignore=[]):
-    """Find all matches for a path with attributes in an XML tree.
-
-    This is a generator. Each of the returned values will be a matching
-    element.
-
-    Arguments:
-    xml_tree - A tree from the xml.etree.ElementTree module.
-    path - The path to search for.
-    attributes - A dict containing attributes to match. Not all attributes
-                 must be present on an element to get a match, but any
-                 attributes present must match this input.
-    ignore - A list of attributes to ignore when matching. Attributes in
-             this list are ignored regardless of whether or not they are in
-             the attributes dict.
-
-    The attributes argument defaults to an empty dict.
-    """
-
-    # Recursive function to find all matches.
-    def find_matches_below(element, path):
-        # Done when there's no more of the path to match.
-        if len(path) == 0:
-            yield ElementMatch(element)
-            return
-
-        # Otherwise, get the beginning part of the path.
-        path_head, slash, path_tail = path.partition("/")
-
-        # Search through subelements that match the next part of the path.
-        # For each one, call self recursively to get all subelement matches.
-        for trial_element in element.findall(path_head):
-            if _element_attribute_match(trial_element, attributes, ignore):
-                for match in find_matches_below(trial_element, path_tail):
-                    yield match
-
-    element = xml_tree.getroot()
-    for match in find_matches_below(element, path):
-        yield match.element
-
-def elements_to_dict(elements, key_attr="key"):
-    """Uses a key attribute to produce a dict from ElementTree elements.
-
-    For each element, it creates an entry in the returned dict. The key is
-    determined by key_attr, and the value is the text of the element.
-
-    Arguments:
-    elements - An iterable of elements to convert.
-    key_attr - The name of an attribute. Each element must have this
-               attribute to be included in the dict, and the value of this
-               attribute will be used as the key.
-    """
-
-    return dict(
-        (elem.get(key_attr), elem.text)
-        for elem in elements
-        if key_attr in elem.keys()
-        )