Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions testing/functional_class.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
import os
import urllib.request
from abc import ABC, abstractmethod
from utils import str_to_bool, str_to_list

class FunctionalTest(ABC):
"""Class for running FATES functional tests"""

def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str,
use_param_file:str, other_args:str):
use_param_file:str, datm_file:str, datm_file_url:str, other_args:str):
self.name = name
self.test_dir = test_dir
self.test_exe = test_exe
self.out_file = out_file
self.use_param_file = str_to_bool(use_param_file)
self.datm_file = None
self.other_args = str_to_list(other_args)
self.plot = False


# Check that datm exists and save its absolute path
if datm_file:
self.datm_file = os.path.abspath(datm_file)
if not os.path.exists(self.datm_file):
if not datm_file_url:
raise FileNotFoundError(f"datm_file not found: '{self.datm_file}'")
datm_file_dir = os.path.dirname(self.datm_file)
if not os.path.isdir(datm_file_dir):
os.makedirs(datm_file_dir)
print(f"Downloading datm_file from {datm_file_url}")
urllib.request.urlretrieve(datm_file_url, self.datm_file)

@abstractmethod
def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str):
pass
Expand Down
2 changes: 2 additions & 0 deletions testing/functional_testing/allometry/allometry_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def __init__(self, test_dict):
test_dict["test_exe"],
test_dict["out_file"],
test_dict["use_param_file"],
test_dict["datm_file"],
test_dict["datm_file_url"],
test_dict["other_args"],
)
self.plot = True
Expand Down
2 changes: 2 additions & 0 deletions testing/functional_testing/fire/fuel/fuel_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def __init__(self, test_dict):
test_dict["test_exe"],
test_dict["out_file"],
test_dict["use_param_file"],
test_dict["datm_file"],
test_dict["datm_file_url"],
test_dict["other_args"],
)
self.plot = True
Expand Down
2 changes: 2 additions & 0 deletions testing/functional_testing/fire/ros/ros_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def __init__(self, test_dict):
test_dict["test_exe"],
test_dict["out_file"],
test_dict["use_param_file"],
test_dict["datm_file"],
test_dict["datm_file_url"],
test_dict["other_args"],
)
self.plot = True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ integer function FuelModelPosition(this, fuel_model_index)
end if
end do
write(*, '(a, i2, a)') "Cannot find the fuel model index ", fuel_model_index, "."
stop
call abort()

end function FuelModelPosition

Expand Down
2 changes: 2 additions & 0 deletions testing/functional_testing/math_utils/math_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def __init__(self, test_dict):
test_dict["test_exe"],
test_dict["out_file"],
test_dict["use_param_file"],
test_dict["datm_file"],
test_dict["datm_file_url"],
test_dict["other_args"],
)
self.plot = True
Expand Down
2 changes: 2 additions & 0 deletions testing/functional_testing/patch/patch_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def __init__(self, test_dict):
test_dict["test_exe"],
test_dict["out_file"],
test_dict["use_param_file"],
test_dict["datm_file"],
test_dict["datm_file_url"],
test_dict["other_args"],
)
self.plot = True
Expand Down
12 changes: 11 additions & 1 deletion testing/functional_tests.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,42 @@ test_dir = fates_allom_ftest
test_exe = FATES_allom_exe
out_file = allometry_out.nc
use_param_file = True
datm_file =
datm_file_url =
other_args = []

[quadratic]
test_dir = fates_math_ftest
test_exe = FATES_math_exe
out_file = quad_out.nc
use_param_file = False
datm_file =
datm_file_url =
other_args = []

[fuel]
test_dir = fates_fuel_ftest
test_exe = FATES_fuel_exe
out_file = fuel_out.nc
use_param_file = True
other_args = ['../testing/test_data/BONA_datm.nc']
datm_file = ../testing/test_data/BONA_datm.nc
datm_file_url = https://www.dropbox.com/scl/fi/l7ik0xhnww3snlk2lqngr/BONA_datm.nc?rlkey=15kwixoofokyyj936xkxmfq8t&e=1&dl=1
other_args = []

[ros]
test_dir = fates_ros_ftest
test_exe = FATES_ros_exe
out_file = ros_out.nc
use_param_file = True
datm_file =
datm_file_url =
other_args = []

[patch]
test_dir = fates_patch_ftest
test_exe = FATES_patch_exe
out_file = None
use_param_file = True
datm_file =
datm_file_url =
other_args = []
41 changes: 31 additions & 10 deletions testing/run_functional_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"""
import os
import argparse
import subprocess
import matplotlib.pyplot as plt

from build_fortran_tests import build_tests, build_exists
Expand All @@ -39,12 +40,18 @@

add_cime_lib_to_path()

from CIME.utils import run_cmd_no_fail
from CIME.utils import run_cmd

# constants for this script
_DEFAULT_CONFIG_FILE = "functional_tests.cfg"
_DEFAULT_CDL_PATH = os.path.abspath("../parameter_files/fates_params_default.cdl")
_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../")
_FILE_DIR = os.path.dirname(__file__)
_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "functional_tests.cfg")
_DEFAULT_CDL_PATH = os.path.abspath(os.path.join(
_FILE_DIR,
os.pardir,
"parameter_files",
"fates_params_default.cdl",
))
_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir)
_TEST_SUB_DIR = "testing"


Expand Down Expand Up @@ -74,6 +81,13 @@ def commandline_args():
"parameter_files directory.\n",
)

parser.add_argument(
"--config-file",
type=str,
default=_DEFAULT_CONFIG_FILE,
help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'",
)

parser.add_argument(
"-b",
"--build-dir",
Expand Down Expand Up @@ -196,7 +210,7 @@ def check_param_file(param_file):
None, "Must supply parameter file with .cdl or .nc ending."
)
if not os.path.isfile(param_file):
raise argparse.ArgumentError(None, f"Cannot find file {param_file}.")
raise FileNotFoundError(param_file)


def check_build_dir(build_dir, test_dict):
Expand Down Expand Up @@ -295,8 +309,11 @@ def run_functional_tests(
if run_executables:
print("Running executables")
for _, test in test_dict.items():
# prepend parameter file (if required) to argument list
args = test.other_args
# prepend datm file (if required) to argument list
if test.datm_file:
args.insert(0, test.datm_file)
# prepend parameter file (if required) to argument list
if test.use_param_file:
args.insert(0, param_file)
# run
Expand Down Expand Up @@ -386,7 +403,11 @@ def run_fortran_exectuables(build_dir, test_dir, test_exe, run_dir, args):
run_command.extend(args)

os.chdir(run_dir)
out = run_cmd_no_fail(" ".join(run_command), combine_output=True)
cmd = " ".join(run_command)
stat, out, _ = run_cmd(cmd, combine_output=True)
if stat:
print(out)
raise subprocess.CalledProcessError(stat, cmd, out)
print(out)


Expand All @@ -395,13 +416,13 @@ def main():
Reads in command-line arguments and then runs the tests.
"""

full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE)
subclasses = FunctionalTest.__subclasses__()

args = commandline_args()

full_test_dict = config_to_dict(args.config_file)
config_dict = parse_test_list(full_test_dict, args.test_list)

test_dict = {}
subclasses = FunctionalTest.__subclasses__()
for name in config_dict.keys():
test_class = list(filter(lambda subclass: subclass.name == name, subclasses))[
0
Expand Down
15 changes: 11 additions & 4 deletions testing/run_unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
from CIME.utils import run_cmd_no_fail # pylint: disable=wrong-import-position,import-error,wrong-import-order

# constants for this script
_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../")
_DEFAULT_CONFIG_FILE = "unit_tests.cfg"
_FILE_DIR = os.path.dirname(os.path.abspath(__file__))
_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir)
_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "unit_tests.cfg")
_TEST_SUB_DIR = "testing"


Expand Down Expand Up @@ -58,6 +59,13 @@ def commandline_args():
"Will be created if it does not exist.\n",
)

parser.add_argument(
"--config-file",
type=str,
default=_DEFAULT_CONFIG_FILE,
help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'",
)

parser.add_argument(
"--make-j",
type=int,
Expand Down Expand Up @@ -129,9 +137,8 @@ def main():
Reads in command-line arguments and then runs the tests.
"""

full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE)

args = commandline_args()
full_test_dict = config_to_dict(args.config_file)
test_dict = parse_test_list(full_test_dict, args.test_list)

run_unit_tests(
Expand Down
2 changes: 1 addition & 1 deletion testing/testing_shr/FatesArgumentUtils.F90
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function command_line_arg(arg_position)

if (n_args < arg_position) then
write(*, '(a, i2, a, i2)') "Incorrect number of arguments: ", n_args, ". Should be at least", arg_position, "."
stop
call abort()
end if

call get_command_argument(arg_position, length=arglen)
Expand Down
2 changes: 1 addition & 1 deletion testing/testing_shr/FatesFactoryMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ subroutine CreateTestPatchList(patch, heights, dbhs)
if (present(dbhs)) then
if (size(heights) /= size(dbhs)) then
write(*, '(a)') "Size of heights array must match size of dbh array."
stop
call abort()
end if
end if

Expand Down
8 changes: 4 additions & 4 deletions testing/testing_shr/FatesUnitTestIOMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ subroutine Check(status)

if (status /= nf90_noerr) then
write(*,*) trim(nf90_strerror(status))
stop
call abort()
end if

end subroutine Check
Expand Down Expand Up @@ -134,11 +134,11 @@ subroutine OpenNCFile(nc_file, ncid, fmode)
call Check(nf90_create(trim(nc_file), NF90_CLOBBER, ncid))
case DEFAULT
write(*,*) 'Need to specify read, write, or readwrite'
stop
call abort()
end select
else
write(*,*) 'Problem reading file'
stop
call abort()
end if

end subroutine OpenNCFile
Expand Down Expand Up @@ -480,7 +480,7 @@ subroutine RegisterVar(ncid, var_name, dimID, type, att_names, atts, num_atts, v
nc_type = NF90_CHAR
else
write(*, *) "Must pick correct type"
stop
call abort()
end if

call Check(nf90_def_var(ncid, var_name, nc_type, dimID, varID))
Expand Down
2 changes: 1 addition & 1 deletion testing/testing_shr/FatesUnitTestParamReaderMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ subroutine ReadParameters(this, fates_params)
case default
write(*, '(a,a)') 'dimension shape:', dimension_shape
write(*, '(a)') 'unsupported number of dimensions reading parameters.'
stop
call abort()
end select
end do

Expand Down
6 changes: 3 additions & 3 deletions testing/testing_shr/SyntheticPatchTypes.F90
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ integer function PatchDataPosition(this, patch_id, patch_name)
! can't supply both
if (present(patch_id) .and. present(patch_name)) then
write(*, '(a)') "Can only supply either a patch_id or a patch_name - not both"
stop
call abort()
end if

do i = 1, this%num_patches
Expand All @@ -179,11 +179,11 @@ integer function PatchDataPosition(this, patch_id, patch_name)
end if
else
write(*, '(a)') "Must supply either a patch_id or a patch_name."
stop
call abort()
end if
end do
write(*, '(a)') "Cannot find the synthetic patch type supplied"
stop
call abort()

end function PatchDataPosition

Expand Down
Loading