diff --git a/framework/src/base/MooseApp.C b/framework/src/base/MooseApp.C index 5d95db8d1c2d..b161e1ee0f3d 100644 --- a/framework/src/base/MooseApp.C +++ b/framework/src/base/MooseApp.C @@ -1716,6 +1716,9 @@ MooseApp::runInputs() " --run \" again."); } + // Set this application as the app name for the moose_test_runner script that we're running + setenv("MOOSE_TEST_RUNNER_APP_NAME", appBinaryName().c_str(), true); + Moose::out << "Working Directory: " << working_dir << "\nRunning Command: " << cmd << std::endl; mooseAssert(comm().size() == 1, "Should be run in serial"); const auto return_value = system(cmd.c_str()); diff --git a/modules/functional_expansion_tools/run_tests b/modules/functional_expansion_tools/run_tests deleted file mode 100755 index 894fc2727815..000000000000 --- a/modules/functional_expansion_tools/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, None, MOOSE_DIR) diff --git a/modules/functional_expansion_tools/run_tests b/modules/functional_expansion_tools/run_tests new file mode 120000 index 000000000000..fd3e4b073f7c --- /dev/null +++ b/modules/functional_expansion_tools/run_tests @@ -0,0 +1 @@ +../../scripts/run_tests \ No newline at end of file diff --git a/python/TestHarness/TestHarness.py b/python/TestHarness/TestHarness.py index 257345094e5d..0ae971eccda9 100644 --- a/python/TestHarness/TestHarness.py +++ b/python/TestHarness/TestHarness.py @@ -8,16 +8,15 @@ #* https://www.gnu.org/licenses/lgpl-2.1.html import sys -import itertools import platform import os, re, inspect, errno, copy, json -import shlex from . import RaceChecker import subprocess import shutil import socket import datetime import getpass +from collections import namedtuple from socket import gethostname from FactorySystem.Factory import Factory @@ -27,7 +26,6 @@ import pyhit import argparse -from timeit import default_timer as clock def readTestRoot(fname): @@ -37,17 +35,23 @@ def readTestRoot(fname): # TODO: add check to see if the binary exists before returning. This can be used to # allow users to control fallthrough for e.g. individual module binaries vs. the # combined binary. - return root['app_name'], args, root - -def findTestRoot(start=os.getcwd(), method=os.environ.get('METHOD', 'opt')): - rootdir = os.path.abspath(start) - while os.path.dirname(rootdir) != rootdir: - fname = os.path.join(rootdir, 'testroot') - if os.path.exists(fname): - app_name, args, hit_node = readTestRoot(fname) - return rootdir, app_name, args, hit_node - rootdir = os.path.dirname(rootdir) - raise RuntimeError('test root directory not found in "{}"'.format(start)) + return root.get('app_name'), args, root + +# Struct that represents all of the information pertaining to a testroot file +TestRoot = namedtuple('TestRoot', ['root_dir', 'app_name', 'args', 'hit_node']) +def findTestRoot() -> TestRoot: + """ + Search for the test root in all folders above this one + """ + start = os.getcwd() + root_dir = start + while os.path.dirname(root_dir) != root_dir: + testroot_file = os.path.join(root_dir, 'testroot') + if os.path.exists(testroot_file) and os.access(testroot_file, os.R_OK): + app_name, args, hit_node = readTestRoot(testroot_file) + return TestRoot(root_dir=root_dir, app_name=app_name, args=args, hit_node=hit_node) + root_dir = os.path.dirname(root_dir) + return None # This function finds a file in the herd trunk containing all the possible applications # that may be built with an "up" target. If passed the value ROOT it will simply @@ -177,36 +181,64 @@ def findDepApps(dep_names, use_current_only=False): return '\n'.join(dep_dirs) class TestHarness: - @staticmethod - def buildAndRun(argv, app_name, moose_dir, moose_python=None): - harness = TestHarness(argv, moose_dir, app_name=app_name, moose_python=moose_python) - harness.findAndRunTests() - sys.exit(harness.error_code) + def buildAndRun(argv: list, app_name: str, moose_dir: str, moose_python: str = None, + skip_testroot: bool = False) -> None: + # Cannot skip the testroot if we don't have an application name + if skip_testroot and not app_name: + raise ValueError(f'Must provide "app_name" when skip_testroot=True') - def __init__(self, argv, moose_dir, app_name=None, moose_python=None): + # Assume python directory from moose (in-tree) if moose_python is None: - self.moose_python_dir = os.path.join(moose_dir, "python") + moose_python_dir = os.path.join(moose_dir, "python") + # Given a python directory (installed app) else: - self.moose_python_dir = moose_python + moose_python_dir = moose_python + + # Set MOOSE_DIR and PYTHONPATH for child processes os.environ['MOOSE_DIR'] = moose_dir - os.environ['PYTHONPATH'] = self.moose_python_dir + ':' + os.environ.get('PYTHONPATH', '') + pythonpath = os.environ.get('PYTHONPATH', '').split(':') + if moose_python_dir not in pythonpath: + pythonpath = [moose_python_dir] + pythonpath + os.environ['PYTHONPATH'] = ':'.join(pythonpath) + + # Search for the test root (if any; required when app_name is not specified) + test_root = None if skip_testroot else findTestRoot() + + # Failed to find a test root + if test_root is None: + # app_name was specified so without a testroot, we don't + # know what application to run + if app_name is None: + raise RuntimeError(f'Failed to find testroot by traversing upwards from {os.getcwd()}') + # app_name was specified so just run from this directory + # without any additional parameters + test_root = TestRoot(root_dir='.', app_name=app_name, + args=[], hit_node=pyhit.Node()) + # Found a testroot, but without an app_name + elif test_root.app_name is None: + # app_name was specified from buildAndRun(), so use it + if app_name: + test_root = test_root._replace(app_name=app_name) + # Missing an app_name + else: + raise RuntimeError(f'{test_root.root_dir}/testroot missing app_name') - if app_name: - rootdir, app_name, args, root_params = '.', app_name, [], pyhit.Node() - else: - rootdir, app_name, args, root_params = findTestRoot(start=os.getcwd()) + harness = TestHarness(argv, moose_dir, moose_python_dir, test_root) + harness.findAndRunTests() + sys.exit(harness.error_code) - self._rootdir = rootdir + def __init__(self, argv: list, moose_dir: str, moose_python: str, test_root: TestRoot): + self.moose_python_dir = moose_python + self._rootdir = test_root.root_dir self._orig_cwd = os.getcwd() - os.chdir(rootdir) - argv = argv[:1] + args + argv[1:] + os.chdir(test_root.root_dir) + argv = argv[:1] + test_root.args + argv[1:] self.factory = Factory() - self.app_name = app_name - - self.root_params = root_params + self.app_name = test_root.app_name + self.root_params = test_root.hit_node # Build a Warehouse to hold the MooseObjects self.warehouse = Warehouse() @@ -217,7 +249,7 @@ def __init__(self, argv, moose_dir, app_name=None, moose_python=None): # Get dependent applications and load dynamic tester plugins # If applications have new testers, we expect to find them in /scripts/TestHarness/testers # Use the find_dep_apps script to get the dependent applications for an app - app_dirs = findDepApps(app_name, use_current_only=True).split('\n') + app_dirs = findDepApps(self.app_name, use_current_only=True).split('\n') # For installed binaries, the apps will exist in RELEASE_PATH/scripts, where in # this case RELEASE_PATH is moose_dir share_dir = os.path.join(moose_dir, 'share') @@ -348,10 +380,10 @@ def __init__(self, argv, moose_dir, app_name=None, moose_python=None): # This is so we can easily pass checks around to any scheduler plugin self.options._checks = checks - self.initialize(argv, app_name) + self.initialize(argv, self.app_name) # executable is available after initalize - checks['installation_type'] = util.checkInstalled(self.executable, app_name) + checks['installation_type'] = util.checkInstalled(self.executable, self.app_name) os.chdir(self._orig_cwd) diff --git a/python/TestHarness/tests/test_FailedJSON.py b/python/TestHarness/tests/test_FailedJSON.py index 307d68cf7840..2c23a3c3236d 100644 --- a/python/TestHarness/tests/test_FailedJSON.py +++ b/python/TestHarness/tests/test_FailedJSON.py @@ -7,7 +7,7 @@ #* Licensed under LGPL 2.1, please see LICENSE for details #* https://www.gnu.org/licenses/lgpl-2.1.html -import os, sys, io +import os, io import unittest import mock import TestHarness @@ -21,12 +21,12 @@ def mocked_output(self, mocked, expect_fail, mocked_return): out = io.StringIO() with redirect_stdout(out): mocked_return.return_value=f'{mocked}' - harness = TestHarness.TestHarness(['', '-i', 'required_objects'], MOOSE_DIR) + with self.assertRaises(SystemExit) as e: + TestHarness.TestHarness.buildAndRun(['', '-i', 'required_objects'], None, MOOSE_DIR) if expect_fail: - with self.assertRaises(SystemExit): - harness.findAndRunTests() + self.assertNotEqual(e.exception.code, 0) else: - harness.findAndRunTests() + self.assertEqual(e.exception.code, 0) return out.getvalue() def testGoodJSONOutput(self): diff --git a/python/TestHarness/tests/test_InstallType.py b/python/TestHarness/tests/test_InstallType.py index 1a2ae7e047cf..1cd6fe0fec23 100644 --- a/python/TestHarness/tests/test_InstallType.py +++ b/python/TestHarness/tests/test_InstallType.py @@ -22,12 +22,12 @@ def mocked_output(self, mocked, expect_fail, mocked_return): with redirect_stdout(out): mocked_return.return_value=mocked cmd = ['', '-i', 'install_type', '-c', '--term-format', 'njCst'] - harness = TestHarness.TestHarness(cmd, MOOSE_DIR) + with self.assertRaises(SystemExit) as e: + TestHarness.TestHarness.buildAndRun(cmd, None, MOOSE_DIR) if expect_fail: - with self.assertRaises(SystemExit): - harness.findAndRunTests() + self.assertNotEqual(e.exception.code, 0) else: - harness.findAndRunTests() + self.assertEqual(e.exception.code, 0) return out.getvalue() def testInstalled(self): diff --git a/python/TestHarness/tests/test_MachineType.py b/python/TestHarness/tests/test_MachineType.py index 10508d8a0c06..5bee9e1b0b53 100644 --- a/python/TestHarness/tests/test_MachineType.py +++ b/python/TestHarness/tests/test_MachineType.py @@ -22,12 +22,12 @@ def mocked_output(self, mocked, expect_fail, mocked_return): with redirect_stdout(out): mocked_return.return_value=mocked cmd = ['', '-i', 'always_ok', '-c', '--term-format', 'njCst'] - harness = TestHarness.TestHarness(cmd, MOOSE_DIR) + with self.assertRaises(SystemExit) as e: + TestHarness.TestHarness.buildAndRun(cmd, None, MOOSE_DIR) if expect_fail: - with self.assertRaises(SystemExit): - harness.findAndRunTests() + self.assertNotEqual(e.exception.code, 0) else: - harness.findAndRunTests() + self.assertEqual(e.exception.code, 0) return out.getvalue() def testNotSkipped(self): diff --git a/scripts/moose_test_runner b/scripts/moose_test_runner index 520bb3a4954a..0c7d93822787 100755 --- a/scripts/moose_test_runner +++ b/scripts/moose_test_runner @@ -1,6 +1,15 @@ #!/usr/bin/env python3 import sys, os +# Environment variable set by MooseApp so that we can capture the +# name of the executing application (the binary that --run is called from). +# This will override `app_name` within the `testroot`. +app_name = os.environ.get('MOOSE_TEST_RUNNER_APP_NAME') +if not app_name: + print('The variable MOOSE_TEST_RUNNER_APP_NAME is not set.') + print('This must be run via the "--run" command line option from a MOOSE app') + sys.exit(1) + mydir = os.path.dirname(os.path.realpath(__file__)) moose_config_path = os.path.join(mydir, '..', 'share', 'moose', 'moose_config.py') if not os.path.exists(moose_config_path): @@ -28,4 +37,4 @@ if not os.path.isdir(MOOSE_PYTHON_DIR): sys.path.append(MOOSE_PYTHON_DIR) from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, None, MOOSE_DIR, moose_python=MOOSE_PYTHON_DIR) +TestHarness.buildAndRun(sys.argv, app_name, MOOSE_DIR, MOOSE_PYTHON_DIR) diff --git a/stork/run_tests.module b/stork/run_tests.module index 894fc2727815..8000d321a658 100644 --- a/stork/run_tests.module +++ b/stork/run_tests.module @@ -5,4 +5,4 @@ MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dir sys.path.append(os.path.join(MOOSE_DIR, 'python')) from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, None, MOOSE_DIR) +TestHarness.buildAndRun(sys.argv, 'stork', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step01_moose_app/run_tests b/tutorials/tutorial01_app_development/step01_moose_app/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step01_moose_app/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step01_moose_app/run_tests b/tutorials/tutorial01_app_development/step01_moose_app/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step01_moose_app/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step02_input_file/run_tests b/tutorials/tutorial01_app_development/step02_input_file/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step02_input_file/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step02_input_file/run_tests b/tutorials/tutorial01_app_development/step02_input_file/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step02_input_file/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step05_kernel_object/run_tests b/tutorials/tutorial01_app_development/step05_kernel_object/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step05_kernel_object/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step05_kernel_object/run_tests b/tutorials/tutorial01_app_development/step05_kernel_object/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step05_kernel_object/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step06_input_params/run_tests b/tutorials/tutorial01_app_development/step06_input_params/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step06_input_params/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step06_input_params/run_tests b/tutorials/tutorial01_app_development/step06_input_params/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step06_input_params/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step08_test_harness/run_tests b/tutorials/tutorial01_app_development/step08_test_harness/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step08_test_harness/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step08_test_harness/run_tests b/tutorials/tutorial01_app_development/step08_test_harness/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step08_test_harness/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step09_mat_props/run_tests b/tutorials/tutorial01_app_development/step09_mat_props/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step09_mat_props/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step09_mat_props/run_tests b/tutorials/tutorial01_app_development/step09_mat_props/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step09_mat_props/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial01_app_development/step10_auxkernels/run_tests b/tutorials/tutorial01_app_development/step10_auxkernels/run_tests deleted file mode 100755 index 7366b8b78119..000000000000 --- a/tutorials/tutorial01_app_development/step10_auxkernels/run_tests +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'babbler', MOOSE_DIR) diff --git a/tutorials/tutorial01_app_development/step10_auxkernels/run_tests b/tutorials/tutorial01_app_development/step10_auxkernels/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial01_app_development/step10_auxkernels/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial02_multiapps/app/run_tests b/tutorials/tutorial02_multiapps/app/run_tests deleted file mode 100755 index f96a617371c7..000000000000 --- a/tutorials/tutorial02_multiapps/app/run_tests +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -import sys, os - -MOOSE_DIR = os.path.abspath(os.environ.get('MOOSE_DIR', os.path.join(os.path.dirname(__file__), '..', 'moose'))) -if os.path.exists(os.path.abspath(os.path.join('moose', 'framework', 'Makefile'))): - MOOSE_DIR = os.path.abspath('moose') -MOOSE_DIR = os.environ.get('MOOSE_DIR', MOOSE_DIR) - -sys.path.append(os.path.join(MOOSE_DIR, 'python')) - -from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'multi_app_tut', MOOSE_DIR) diff --git a/tutorials/tutorial02_multiapps/app/run_tests b/tutorials/tutorial02_multiapps/app/run_tests new file mode 120000 index 000000000000..6c553a29fe60 --- /dev/null +++ b/tutorials/tutorial02_multiapps/app/run_tests @@ -0,0 +1 @@ +../../../scripts/run_tests \ No newline at end of file diff --git a/tutorials/tutorial03_verification/app/run_tests b/tutorials/tutorial03_verification/app/run_tests index edb9169f9589..797dc270ef39 100755 --- a/tutorials/tutorial03_verification/app/run_tests +++ b/tutorials/tutorial03_verification/app/run_tests @@ -9,4 +9,4 @@ MOOSE_DIR = os.environ.get('MOOSE_DIR', MOOSE_DIR) sys.path.append(os.path.join(MOOSE_DIR, 'python')) from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'verification_tutorial', MOOSE_DIR) +TestHarness.buildAndRun(sys.argv, 'verification_tutorial', MOOSE_DIR, skip_testroot=True) diff --git a/tutorials/tutorial04_meshing/app/run_tests b/tutorials/tutorial04_meshing/app/run_tests index 3b67dd7e0b07..8aa0dbeb98da 100755 --- a/tutorials/tutorial04_meshing/app/run_tests +++ b/tutorials/tutorial04_meshing/app/run_tests @@ -9,4 +9,4 @@ MOOSE_DIR = os.environ.get('MOOSE_DIR', MOOSE_DIR) sys.path.append(os.path.join(MOOSE_DIR, 'python')) from TestHarness import TestHarness -TestHarness.buildAndRun(sys.argv, 'meshing_tutorial', MOOSE_DIR) +TestHarness.buildAndRun(sys.argv, 'meshing_tutorial', MOOSE_DIR, skip_testroot=True)