Skip to content

Commit

Permalink
Merge pull request #28997 from loganharbour/install_testroot
Browse files Browse the repository at this point in the history
Use the app name from the binary when running installed tests
  • Loading branch information
loganharbour authored Nov 6, 2024
2 parents 53b7f72 + ccd41f3 commit f357342
Show file tree
Hide file tree
Showing 18 changed files with 106 additions and 129 deletions.
3 changes: 3 additions & 0 deletions framework/src/base/MooseApp.C
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,9 @@ MooseApp::runInputs()
" --run <dir>\" 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());
Expand Down
8 changes: 0 additions & 8 deletions modules/functional_expansion_tools/run_tests

This file was deleted.

1 change: 1 addition & 0 deletions modules/functional_expansion_tools/run_tests
104 changes: 68 additions & 36 deletions python/TestHarness/TestHarness.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,7 +26,6 @@
import pyhit

import argparse
from timeit import default_timer as clock

def readTestRoot(fname):

Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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 <app_dir>/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')
Expand Down Expand Up @@ -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)

Expand Down
10 changes: 5 additions & 5 deletions python/TestHarness/tests/test_FailedJSON.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down
8 changes: 4 additions & 4 deletions python/TestHarness/tests/test_InstallType.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
8 changes: 4 additions & 4 deletions python/TestHarness/tests/test_MachineType.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
11 changes: 10 additions & 1 deletion scripts/moose_test_runner
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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)
2 changes: 1 addition & 1 deletion stork/run_tests.module
Original file line number Diff line number Diff line change
Expand Up @@ -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)

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

12 changes: 0 additions & 12 deletions tutorials/tutorial02_multiapps/app/run_tests

This file was deleted.

1 change: 1 addition & 0 deletions tutorials/tutorial02_multiapps/app/run_tests
2 changes: 1 addition & 1 deletion tutorials/tutorial03_verification/app/run_tests
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion tutorials/tutorial04_meshing/app/run_tests
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit f357342

Please sign in to comment.