Repository: ESMCI/cime

Issue:
Get some unit testing defaults from config_machines
_From @billsacks on August 4, 2015 17:6_

With the changes here:

https://github.com/CESM-Development/cime/pull/87

a number of machine-specific settings need to be explicitly specified when running run_tests.py; these should be obtained from config_machines:
- mpilib
- maybe the mpirun command
- maybe the default compiler for a machine

@quantheory suggested this; he and @jedwards4b seem like the ones who would know how to do this.


_Copied from original issue: CESM-Development/cime#92_


Discussion:
It looks like mpilib and compiler can pick up defaults now, although some tweaks are needed:

- For mpilib: fix documentation of this argument in run_tests.py to say it's optional, and test to make sure it works

- For compiler: get rid of the default in the parser.add_argument call: `    parser.add_argument("--compiler",  default="gnu",`, so that it defaults to None.

Ideally, the mpirun command would still be picked up by default. In addition, it would be nice if `--use-openmp` could be determined by default, maybe based on some new xml variable that says whether the pFUnit build requires openmp? But for now, maybe the easiest is to default to this being true, since that's the way we typically build pFUnit. Then could have a flag to set it to false, like --no-openmp.

Resolving patch:
diff --git a/scripts/lib/CIME/XML/machines.py b/scripts/lib/CIME/XML/machines.py
--- a/scripts/lib/CIME/XML/machines.py
+++ b/scripts/lib/CIME/XML/machines.py
@@ -95,8 +95,34 @@ def probe_machine_name(self):
         Find a matching regular expression for hostname
         in the NODENAME_REGEX field in the file.   First match wins.
         """
-        machine = None
+
+        names_not_found = []
+
         nametomatch = socket.getfqdn()
+        machine = self._probe_machine_name_one_guess(nametomatch)
+
+        if machine is None:
+            names_not_found.append(nametomatch)
+
+            nametomatch = socket.gethostname()
+            machine = self._probe_machine_name_one_guess(nametomatch)
+
+            if machine is None:
+                names_not_found.append(nametomatch)
+
+                names_not_found_quoted = ["'" + name + "'" for name in names_not_found]
+                names_not_found_str = ' or '.join(names_not_found_quoted)
+                logger.warning("Could not find machine match for %s" % names_not_found_str)
+
+        return machine
+
+    def _probe_machine_name_one_guess(self, nametomatch):
+        """
+        Find a matching regular expression for nametomatch in the NODENAME_REGEX
+        field in the file. First match wins. Returns None if no match is found.
+        """
+
+        machine = None
         nodes = self.get_nodes("machine")
 
         for node in nodes:
@@ -113,10 +139,6 @@ def probe_machine_name(self):
                     machine = machtocheck
                     break
 
-        if machine is None:
-            # Check for a local definition
-            logger.warning("Could not probe machine for hostname '%s'" % nametomatch)
-
         return machine
 
     def set_machine(self, machine):
diff --git a/scripts/lib/CIME/case.py b/scripts/lib/CIME/case.py
--- a/scripts/lib/CIME/case.py
+++ b/scripts/lib/CIME/case.py
@@ -142,7 +142,8 @@ def _initialize_derived_attributes(self):
         mpi_attribs = {
             "compiler" : self.get_value("COMPILER"),
             "mpilib"   : self.get_value("MPILIB"),
-            "threaded" : self.get_build_threaded()
+            "threaded" : self.get_build_threaded(),
+            "unit_testing" : False
             }
 
         executable = env_mach_spec.get_mpirun(self, mpi_attribs, job="case.run", exe_only=True)[0]
@@ -1076,7 +1077,8 @@ def get_mpirun_cmd(self, job="case.run"):
         mpi_attribs = {
             "compiler" : self.get_value("COMPILER"),
             "mpilib"   : self.get_value("MPILIB"),
-            "threaded" : self.get_build_threaded()
+            "threaded" : self.get_build_threaded(),
+            "unit_testing" : False
             }
 
         executable, args = env_mach_specific.get_mpirun(self, mpi_attribs, job=job)


Test patch:
diff --git a/README.unit_testing b/README.unit_testing
--- a/README.unit_testing
+++ b/README.unit_testing
@@ -13,4 +13,4 @@
 # https://sourceforge.net/projects/pfunit/
 #
 
-tools/unit_testing/run_tests.py --test-spec-dir=. --compiler=intel --mpilib=mpich2 --use-openmp --mpirun-command=mpirun.lsf --build-dir `mktemp -d --tmpdir=. unit_tests.XXXXXXXX`
+tools/unit_testing/run_tests.py --build-dir `mktemp -d ./unit_tests.XXXXXXXX`
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
@@ -190,7 +190,7 @@ def test_a_unit_test(self):
         os.makedirs(test_dir)
         unit_test_tool = os.path.abspath(os.path.join(CIME.utils.get_cime_root(),"tools","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 --compiler intel --use-openmp --mpirun-command mpirun.lsf"\
+        run_cmd_no_fail("%s --build-dir %s --test-spec-dir %s"\
                             %(unit_test_tool,test_dir,test_spec_dir))
         cls._do_teardown.append(test_dir)
 
@@ -211,8 +211,8 @@ def test_b_cime_f90_unit_tests(self):
         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"))
 
-        run_cmd_no_fail("%s --build-dir %s --test-spec-dir %s --compiler %s --use-openmp --mpirun-command mpirun.lsf"\
-                            %(unit_test_tool,test_dir,test_spec_dir, compiler))
+        run_cmd_no_fail("%s --build-dir %s --test-spec-dir %s"\
+                            %(unit_test_tool,test_dir,test_spec_dir))
         cls._do_teardown.append(test_dir)
 
     @classmethod
diff --git a/tools/unit_testing/run_tests.py b/tools/unit_testing/run_tests.py
--- a/tools/unit_testing/run_tests.py
+++ b/tools/unit_testing/run_tests.py
@@ -64,9 +64,9 @@ def parse_command_line(args):
     parser.add_argument("--no-color",  action="store_false",
                       help="""Turn off colorized output."""
     )
-    parser.add_argument("--compiler",  default="gnu",
+    parser.add_argument("--compiler",
                         help="""Compiler vendor for build (supported depends on machine).
-Only used for lookup in CIME Machines files."""
+                        If not specified, use the default for this machine."""
                         )
     parser.add_argument("--enable-genf90", action="store_true",
                         default=True,
@@ -83,19 +83,23 @@ def parse_command_line(args):
 
     parser.add_argument("--mpilib",
     help="""MPI Library to use in build.
-Required argument (until we can get this from config_machines)
-Must match an MPILIB option in config_compilers.xml.
-e.g., for yellowstone, can use 'mpich2'."""
+    If not specified, use the default for this machine/compiler.
+    Must match an MPILIB option in config_compilers.xml.
+    e.g., for yellowstone, can use 'mpich2'."""
     )
-    parser.add_argument(
-        "--mpirun-command", default="",
-        help="""Command to use to run an MPI executable.
 
-If not specified, does not use any mpirun prefix to run executables."""
+    parser.add_argument("--no-mpirun", action="store_true",
+                        help="""If specified, then run executables without an mpirun command""")
+
+    parser.add_argument("--mpirun-command",
+        help="""Command to use to run an MPI executable.
+        If not specified, uses the default for this machine.
+        Ignored if --no-mpirun is specified."""
         )
     parser.add_argument(
-        "--test-spec-dir",
-        help="""Location where tests are specified."""
+        "--test-spec-dir", default=".",
+        help="""Location where tests are specified.
+        Defaults to current directory."""
         )
     parser.add_argument(
     "-T", "--ctest-args",
@@ -110,9 +114,8 @@ def parse_command_line(args):
 override the command provided by Machines."""
     )
     parser.add_argument(
-        "--use-openmp",  action="store_true",
-        default=False,
-        help="""Turn on OPENMP support for tests."""
+        "--no-openmp",  action="store_true",
+        help="""Default is to include OPENMP support for tests; this option excludes openmp"""
     )
     parser.add_argument(
         "--xml-test-list",
@@ -133,8 +136,8 @@ def parse_command_line(args):
 
     return output, args.build_dir, args.build_optimized, args.clean,\
         args.cmake_args, args.compiler, args.enable_genf90, args.machine, args.machines_dir,\
-        args.make_j, args.mpilib, args.mpirun_command, args.test_spec_dir, args.ctest_args,\
-        args.use_openmp, args.xml_test_list, args.verbose
+        args.make_j, args.mpilib, args.no_mpirun, args.mpirun_command, args.test_spec_dir, args.ctest_args,\
+        args.no_openmp, args.xml_test_list, args.verbose
 
 
 def cmake_stage(name, test_spec_dir, build_optimized, mpirun_command, output, cmake_args=None, clean=False, verbose=False, enable_genf90=True, color=True):
@@ -169,7 +172,7 @@ def cmake_stage(name, test_spec_dir, build_optimized, mpirun_command, output, cm
             test_spec_dir,
             "-DCIME_CMAKE_MODULE_DIRECTORY="+os.path.abspath(os.path.join(_CIMEROOT,"src","externals","CMake")),
             "-DCMAKE_BUILD_TYPE="+build_type,
-            "-DPFUNIT_MPIRUN="+mpirun_command,
+            "-DPFUNIT_MPIRUN='"+mpirun_command+"'",
             ]
         if verbose:
             cmake_command.append("-Wdev")
@@ -216,10 +219,12 @@ def make_stage(name, output, make_j, clean=False, verbose=True):
 def _main():
     output, build_dir, build_optimized, clean,\
         cmake_args, compiler, enable_genf90, machine, machines_dir,\
-        make_j, mpilib, mpirun_command, test_spec_dir, ctest_args,\
-        use_openmp, xml_test_list, verbose \
+        make_j, mpilib, no_mpirun, mpirun_command, test_spec_dir, ctest_args,\
+        no_openmp, xml_test_list, verbose \
         = parse_command_line(sys.argv)
 
+    use_openmp = not no_openmp
+
 #=================================================
 # Find directory and file paths.
 #=================================================
@@ -241,14 +246,6 @@ def _main():
             )
 
 
-
-
-# Search for the CESM root directory.
-# First check the option. If not specified, look to see if there's a tools
-# directory two levels up (just as a sanity check).
-    if machine is None:
-        machine="yellowstone"
-
     if machines_dir is not None:
         machines_file = os.path.join(machines_dir, "config_machines.xml")
         machobj = Machines(infile=machines_file, machine=machine)
@@ -269,8 +266,10 @@ def _main():
 #=================================================
     if mpilib is None:
         mpilib = machobj.get_default_MPIlib()
+        logger.warn("Using mpilib: %s"%mpilib)
     if compiler is None:
         compiler = machobj.get_default_compiler()
+        logger.warn("Compiler is %s"%compiler)
 
     debug = not build_optimized
     os_ = machobj.get_value("OS")
@@ -280,8 +279,22 @@ def _main():
     #
     configure(machobj, build_dir, ["CMake"], compiler, mpilib, debug, os_)
     machspecific = EnvMachSpecific(build_dir)
+
+    if no_mpirun:
+        mpirun_command = ""
+    elif mpirun_command is None:
+        mpi_attribs = {
+            "compiler" : compiler,
+            "mpilib"   : mpilib,
+            "threaded" : use_openmp,
+            "unit_testing" : True
+        }
+
+        # We can get away with specifying case=None since we're using exe_only=True
+        mpirun_command, _ = machspecific.get_mpirun(case=None, attribs=mpi_attribs, exe_only=True)
+        logger.warn("mpirun command is '%s'"%mpirun_command)
+
     machspecific.load_env(compiler, debug, mpilib)
-    logger.warn("Compiler is %s"%compiler)
     os.environ["OS"] = os_
     os.environ["COMPILER"] = compiler
     os.environ["DEBUG"] = stringify_bool(debug)