diff --git a/.github/workflows/capgen_unit_tests.yaml b/.github/workflows/capgen_unit_tests.yaml index 4d871a52..7247100f 100644 --- a/.github/workflows/capgen_unit_tests.yaml +++ b/.github/workflows/capgen_unit_tests.yaml @@ -15,8 +15,38 @@ jobs: steps: - uses: actions/checkout@v3 - name: update repos and install dependencies - run: sudo apt-get update && sudo apt-get install -y build-essential ${{matrix.fortran-compiler}} cmake python3 git libxml2-utils + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + libopenmpi-dev \ + ${{matrix.fortran-compiler}} \ + cmake \ + python3 \ + git \ + libxml2-utils + pip install --user pytest + + - name: Build the framework + run: | + cmake -S. -B./build -DOPENMP=ON -DCCPP_FRAMEWORK_ENABLE_TESTS=ON + cd build + make + - name: Run unit tests - run: cd test && ./run_fortran_tests.sh + run: | + cd build + ctest --rerun-failed --output-on-failure . --verbose + + - name: Run python tests + run: | + BUILD_DIR=./build \ + PYTHONPATH=test/:scripts/ \ + pytest \ + test/capgen_test/capgen_test_reports.py \ + test/advection_test/advection_test_reports.py \ + test/ddthost_test/ddthost_test_reports.py \ + test/var_compatibility_test/var_compatibility_test_reports.py + - name: Run Fortran to metadata test run: cd test && ./test_fortran_to_metadata.sh diff --git a/.gitignore b/.gitignore index c2eb493d..8982481f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ # All compiled Python modules *.pyc +# CMake build directory +build/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 100192cd..00000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: python - -python: - - "3.6" - - "3.7" - - "3.8" - - "3.9" - -branches: - only: - - feature/capgen - -install: - - pip install pylint - -script: - - env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_table.py - - env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_scheme_file.py - - python test/unit_tests/test_metadata_table.py - - python test/unit_tests/test_metadata_scheme_file.py - -notifications: - email: - recipients: - - dom.heinzeller@noaa.gov - - goldy@ucar.edu - on_success: always # default: change - on_failure: always # default: always diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d03783d..e0316793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,46 +4,117 @@ project(ccpp_framework VERSION 5.0.0 LANGUAGES Fortran) +include(cmake/ccpp_capgen.cmake) + #------------------------------------------------------------------------------ # Set package definitions set(PACKAGE "ccpp-framework") -set(AUTHORS "Dom Heinzeller" "Grant Firl" "Mike Kavulich" "Dustin Swales" "Courtney Peverley") string(TIMESTAMP YEAR "%Y") +option(OPENMP "Enable OpenMP support for the framework" OFF) +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) +option(CCPP_FRAMEWORK_BUILD_DOCUMENTATION + "Create and install the HTML documentation (requires Doxygen)" OFF) +option(CCPP_FRAMEWORK_ENABLE_TESTS "Enable building/running CCPP regression tests" OFF) +option(CCPP_RUN_ADVECTION_TEST "Enable advection regression test" OFF) +option(CCPP_RUN_CAPGEN_TEST "Enable capgen regression test" OFF) +option(CCPP_RUN_DDT_HOST_TEST "Enable ddt host regression test" OFF) +option(CCPP_RUN_VAR_COMPATIBILITY_TEST "Enable variable compatibility regression test" OFF) + +message("") +message("OPENMP .............................. ${OPENMP}") +message("BUILD_SHARED_LIBS ................... ${BUILD_SHARED_LIBS}") +message("") +message("CCPP_FRAMEWORK_BUILD_DOCUMENTATION ...${CCPP_FRAMEWORK_BUILD_DOCUMENTATION}") +message("CCPP_FRAMEWORK_ENABLE_TESTS ......... ${CCPP_FRAMEWORK_ENABLE_TESTS}") +message("CCPP_RUN_ADVECTION_TEST ............. ${CCPP_RUN_ADVECTION_TEST}") +message("CCPP_RUN_CAPGEN_TEST ................ ${CCPP_RUN_CAPGEN_TEST}") +message("CCPP_RUN_DDT_HOST_TEST .............. ${CCPP_RUN_DDT_HOST_TEST}") +message("CCPP_RUN_VAR_COMPATIBILITY_TEST ..... ${CCPP_RUN_VAR_COMPATIBILITY_TEST}") +message("") + +set(CCPP_VERBOSITY "0" CACHE STRING "Verbosity level of output (default: 0)") + +# Warn user on conflicting test options +if(CCPP_RUN_ADVECTION_TEST OR + CCPP_RUN_CAPGEN_TEST OR + CCPP_RUN_DDT_HOST_TEST OR + CCPP_RUN_VAR_COMPATIBILITY_TEST) + set(CCPP_MANUALLY_DECLARED_TEST ON BOOL) +endif() +if(CCPP_MANUALLY_DECLARED_TEST AND CCPP_FRAMEWORK_ENABLE_TESTS) + message(WARNING "Detected a manual test flag and the flag to run all tests. If only expected to run a single test, please unset CCPP_FRAMEWORK_ENABLE_TESTS option.") +endif() +set(CCPP_RUNNING_TESTS CCPP_FRAMEWORK_ENABLE_TESTS OR CCPP_MANUALLY_DECLARED_TEST) + +# If running tests, set appropriate flags to help with debugging test issues. +if(CCPP_RUNNING_TESTS) + if(${CMAKE_Fortran_COMPILER_ID} STREQUAL "GNU") + ADD_COMPILE_OPTIONS(-fcheck=all) + ADD_COMPILE_OPTIONS(-fbacktrace) + ADD_COMPILE_OPTIONS(-ffpe-trap=zero) + ADD_COMPILE_OPTIONS(-finit-real=nan) + ADD_COMPILE_OPTIONS(-ggdb) + ADD_COMPILE_OPTIONS(-ffree-line-length-none) + ADD_COMPILE_OPTIONS(-cpp) + elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "Intel") + ADD_COMPILE_OPTIONS(-fpe0) + ADD_COMPILE_OPTIONS(-warn) + ADD_COMPILE_OPTIONS(-traceback) + ADD_COMPILE_OPTIONS(-debug extended) + ADD_COMPILE_OPTIONS(-fpp) + ADD_COMPILE_OPTIONS(-diag-disable=10448) + elseif(${CMAKE_Fortran_COMPILER_ID} STREQUAL "IntelLLVM") + ADD_COMPILE_OPTIONS(-fpe0) + ADD_COMPILE_OPTIONS(-warn) + ADD_COMPILE_OPTIONS(-traceback) + ADD_COMPILE_OPTIONS(-debug full) + ADD_COMPILE_OPTIONS(-fpp) + elseif (${CMAKE_Fortran_COMPILER_ID} STREQUAL "NVIDIA" OR ${CMAKE_Fortran_COMPILER_ID} STREQUAL "NVHPC") + ADD_COMPILE_OPTIONS(-Mnoipa) + ADD_COMPILE_OPTIONS(-traceback) + ADD_COMPILE_OPTIONS(-Mfree) + ADD_COMPILE_OPTIONS(-Ktrap=fp) + ADD_COMPILE_OPTIONS(-Mpreprocess) + else() + message (WARNING "This program may not be able to be compiled with compiler :${CMAKE_Fortran_COMPILER_ID}") + endif() +endif() + +# Use rpaths on MacOSX +set(CMAKE_MACOSX_RPATH 1) + #------------------------------------------------------------------------------ # Set MPI flags for Fortran with MPI F08 interface -find_package(MPI REQUIRED Fortran) +find_package(MPI COMPONENTS Fortran REQUIRED) if(NOT MPI_Fortran_HAVE_F08_MODULE) message(FATAL_ERROR "MPI implementation does not support the Fortran 2008 mpi_f08 interface") endif() #------------------------------------------------------------------------------ # Set OpenMP flags for C/C++/Fortran -if (OPENMP) +if(OPENMP) find_package(OpenMP REQUIRED) - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") -endif (OPENMP) +endif() #------------------------------------------------------------------------------ # Set a default build type if none was specified if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "Coverage") + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) endif() #------------------------------------------------------------------------------ -# Pass debug/release flag to Fortran files for preprocessor -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-DDEBUG) +# Add the sub-directories +add_subdirectory(src) + +if(CCPP_RUNNING_TESTS) + enable_testing() + add_subdirectory(test) endif() -#------------------------------------------------------------------------------ -# Request a static build -option(BUILD_SHARED_LIBS "Build a static library" OFF) +if (CCPP_FRAMEWORK_BUILD_DOCUMENTATION) + find_package(Doxygen REQUIRED) + add_subdirectory(doc) +endif() -#------------------------------------------------------------------------------ -# Add the sub-directories -add_subdirectory(src) -add_subdirectory(doc) diff --git a/cmake/ccpp_capgen.cmake b/cmake/ccpp_capgen.cmake new file mode 100644 index 00000000..24c891f2 --- /dev/null +++ b/cmake/ccpp_capgen.cmake @@ -0,0 +1,129 @@ +# CMake wrapper for ccpp_capgen.py +# Currently meant to be a CMake API needed for generating caps for regression tests. +# +# CAPGEN_DEBUG - ON/OFF (Default: OFF) - Enables debug capability through ccpp_capgen.py +# CAPGEN_EXPECT_THROW_ERROR - ON/OFF (Default: OFF) - Scans ccpp_capgen.py log for error string and errors if not found. +# HOST_NAME - String name of host +# OUTPUT_ROOT - String path to put generated caps +# VERBOSITY - Number of --verbose flags to pass to capgen +# HOSTFILES - CMake list of host metadata filenames +# SCHEMEFILES - CMake list of scheme metadata files +# SUITES - CMake list of suite xml files +function(ccpp_capgen) + set(optionalArgs CAPGEN_DEBUG CAPGEN_EXPECT_THROW_ERROR) + set(oneValueArgs HOST_NAME OUTPUT_ROOT VERBOSITY) + set(multi_value_keywords HOSTFILES SCHEMEFILES SUITES) + + cmake_parse_arguments(arg "${optionalArgs}" "${oneValueArgs}" "${multi_value_keywords}" ${ARGN}) + + # Error if script file not found. + set(CCPP_CAPGEN_CMD_LIST "${CMAKE_SOURCE_DIR}/scripts/ccpp_capgen.py") + if(NOT EXISTS ${CCPP_CAPGEN_CMD_LIST}) + message(FATAL_ERROR "function(ccpp_capgen): Could not find ccpp_capgen.py. Looked for ${CCPP_CAPGEN_CMD_LIST}.") + endif() + + # Interpret parsed arguments + if(DEFINED arg_CAPGEN_DEBUG) + list(APPEND CCPP_CAPGEN_CMD_LIST "--debug") + endif() + if(DEFINED arg_HOSTFILES) + list(JOIN arg_HOSTFILES "," HOSTFILES_SEPARATED) + list(APPEND CCPP_CAPGEN_CMD_LIST "--host-files" "${HOSTFILES_SEPARATED}") + endif() + if(DEFINED arg_SCHEMEFILES) + list(JOIN arg_SCHEMEFILES "," SCHEMEFILES_SEPARATED) + list(APPEND CCPP_CAPGEN_CMD_LIST "--scheme-files" "${SCHEMEFILES_SEPARATED}") + endif() + if(DEFINED arg_SUITES) + list(JOIN arg_SUITES "," SUITES_SEPARATED) + list(APPEND CCPP_CAPGEN_CMD_LIST "--suites" "${SUITES_SEPARATED}") + endif() + if(DEFINED arg_HOST_NAME) + list(APPEND CCPP_CAPGEN_CMD_LIST "--host-name" "${arg_HOST_NAME}") + endif() + if(DEFINED arg_OUTPUT_ROOT) + message(STATUS "Creating output directory: ${arg_OUTPUT_ROOT}") + file(MAKE_DIRECTORY "${arg_OUTPUT_ROOT}") + list(APPEND CCPP_CAPGEN_CMD_LIST "--output-root" "${arg_OUTPUT_ROOT}") + endif() + if(DEFINED arg_VERBOSITY) + string(REPEAT "--verbose" ${arg_VERBOSITY} VERBOSE_PARAMS_SEPERATED) + separate_arguments(VERBOSE_PARAMS UNIX_COMMAND "${VERBOSE_PARAMS_SEPERATED}") + list(APPEND CCPP_CAPGEN_CMD_LIST ${VERBOSE_PARAMS}) + endif() + + message(STATUS "Running ccpp_capgen.py from ${CMAKE_CURRENT_SOURCE_DIR}") + + unset(CAPGEN_OUT) # Unset CAPGEN_OUT to prevent incorrect output on subsequent ccpp_capgen(...) calls. + execute_process(COMMAND ${CCPP_CAPGEN_CMD_LIST} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE CAPGEN_OUT + ERROR_VARIABLE CAPGEN_OUT + RESULT_VARIABLE RES + COMMAND_ECHO STDOUT) + + message(STATUS "ccpp-capgen stdout: ${CAPGEN_OUT}") + + if(arg_CAPGEN_EXPECT_THROW_ERROR) + # Determine if the process succeeded but had an expected string in the process log. + string(FIND "${CAPGEN_OUT}" "Variables of type ccpp_constituent_properties_t only allowed in register phase" ERROR_INDEX) + + if (ERROR_INDEX GREATER -1) + message(STATUS "Capgen build produces expected error message.") + else() + message(FATAL_ERROR "CCPP cap generation did not generate expected error. Expected 'Variables of type constituent_properties_t only allowed in register phase.") + endif() + else() + if(RES EQUAL 0) + message(STATUS "ccpp-capgen completed successfully") + else() + message(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}") + endif() + endif() +endfunction() + +# CMake wrapper for ccpp_datafile.py +# Currently meant to be a CMake API needed for generating caps for regression tests. +# +# DATATABLE - Path to generated datatable.xml file +# REPORT_NAME - String report name to get list of generated files form capgen (typically --ccpp-files) +function(ccpp_datafile) + set(oneValueArgs DATATABLE REPORT_NAME) + cmake_parse_arguments(arg "" "${oneValueArgs}" "" ${ARGN}) + + set(CCPP_DATAFILE_CMD "${CMAKE_SOURCE_DIR}/scripts/ccpp_datafile.py") + + if(NOT EXISTS ${CCPP_DATAFILE_CMD}) + message(FATAL_ERROR "function(ccpp_datafile): Could not find ccpp_datafile.py. Looked for ${CCPP_DATAFILE_CMD}.") + endif() + + if(NOT DEFINED arg_REPORT_NAME) + message(FATAL_ERROR "function(ccpp_datafile): REPORT_NAME not set. Must specify the report to generate to run cpp_datafile.py") + endif() + list(APPEND CCPP_DATAFILE_CMD "${arg_REPORT_NAME}") + + if(NOT DEFINED arg_DATATABLE) + message(FATAL_ERROR "function(ccpp_datafile): DATATABLE not set. A datatable file must be configured to call ccpp_datafile.") + endif() + list(APPEND CCPP_DATAFILE_CMD "${arg_DATATABLE}") + + message(STATUS "Running ccpp_datafile from ${CMAKE_CURRENT_SOURCE_DIR}") + + unset(CCPP_CAPS) # Unset CCPP_CAPS to prevent incorrect output on subsequent ccpp_datafile(...) calls. + execute_process(COMMAND ${CCPP_DATAFILE_CMD} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE CCPP_CAPS + RESULT_VARIABLE RES + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_STRIP_TRAILING_WHITESPACE + COMMAND_ECHO STDOUT) + message(STATUS "CCPP_CAPS = ${CCPP_CAPS}") + if(RES EQUAL 0) + message(STATUS "CCPP cap files retrieved") + else() + message(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}") + endif() + string(REPLACE "," ";" CCPP_CAPS_LIST ${CCPP_CAPS}) # Convert "," separated list from python back to ";" separated list for CMake. + set(CCPP_CAPS_LIST "${CCPP_CAPS_LIST}" PARENT_SCOPE) +endfunction() + diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 9e96e4e4..b7997658 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -2,28 +2,16 @@ # Doxygen rules # # Add a target to generate API documentation with Doxygen -find_package(Doxygen) -option(BUILD_DOCUMENTATION - "Create and install the HTML documentation (requires Doxygen)" - ${DOXYGEN_FOUND}) +set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) +set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) -# -if(BUILD_DOCUMENTATION) - if(NOT DOXYGEN_FOUND) - message(FATAL_ERROR "Doxygen is needed to build the documentation.") - endif() - - set(doxyfile_in ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) - set(doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - - configure_file(${doxyfile_in} ${doxyfile} @ONLY) +configure_file(${doxyfile_in} ${doxyfile} @ONLY) - add_custom_target(doc - COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM) -endif() +add_custom_target(doc + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) set(gmtb_sty_in ${CMAKE_CURRENT_SOURCE_DIR}/DevelopersGuide/gmtb.sty) set(gmtb_sty ${CMAKE_CURRENT_BINARY_DIR}/DevelopersGuide/gmtb.sty) diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..fe2284ec --- /dev/null +++ b/doc/README.md @@ -0,0 +1,11 @@ +# Building the documentation + +Similar to building the source code, building the documentation can be done by: + +```bash +$ cmake -S -B -DCCPP_FRAMEWORK_BUILD_DOCUMENTATION=ON +cd build_directory +make doc +``` + +and the compiled documentation should be in `/doc/html`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ff78a81..a7428a77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,9 +12,12 @@ set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_I # Define the executable and what to link add_library(ccpp_framework STATIC ${SOURCES_F90}) target_link_libraries(ccpp_framework PUBLIC MPI::MPI_Fortran) -set_target_properties(ccpp_framework PROPERTIES VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - LINK_FLAGS ${CMAKE_Fortran_FLAGS}) +if(OPENMP) + target_link_libraries(ccpp_framework PUBLIC OpenMP::OpenMP_Fortran) +endif() +set_target_properties(ccpp_framework PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}) #------------------------------------------------------------------------------ # Installation diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..5666599d --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,14 @@ +add_subdirectory(utils) + +if(CCPP_FRAMEWORK_ENABLE_TESTS OR CCPP_RUN_ADVECTION_TEST) + add_subdirectory(advection_test) +endif() +if(CCPP_FRAMEWORK_ENABLE_TESTS OR CCPP_RUN_CAPGEN_TEST) + add_subdirectory(capgen_test) +endif() +if(CCPP_FRAMEWORK_ENABLE_TESTS OR CCPP_RUN_DDT_HOST_TEST) + add_subdirectory(ddthost_test) +endif() +if(CCPP_FRAMEWORK_ENABLE_TESTS OR CCPP_RUN_VAR_COMPATIBILITY_TEST) + add_subdirectory(var_compatibility_test) +endif() diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..9b8d808c --- /dev/null +++ b/test/README.md @@ -0,0 +1,75 @@ +# Testing + +## Unit tests +To run the Python based unit tests, see the associated documentation in the `unit_tests` directory. + +## Doc tests +The Python source code has a wide range of doctests that can be used to verify implementation details quickly. To run the Python based doc tests, run: +```bash +$ export PYTHONPATH=/scripts:/scripts/parse_tools +$ pytest -v /scripts/ --doctest-modules +``` + +## Regression tests +The run the regression tests with mock host models, build the main project with your test option: + +```bash +$ cmake -S -B ... +$ cd +$ make +$ ctest +``` + +For example, to run all of the regression tests from the root of the project, you can use: +```bash +cmake -B./build -S./ -DCCPP_FRAMEWORK_ENABLE_TESTS=ON +``` + +Currently (as of July 2025), if everything works as expected, you should see something like: +``` +Test project + Start 1: ctest_advection_host_integration +1/4 Test #1: ctest_advection_host_integration ........... Passed 0.01 sec + Start 2: ctest_capgen_host_integration +2/4 Test #2: ctest_capgen_host_integration .............. Passed 0.01 sec + Start 3: ctest_ddt_host_integration +3/4 Test #3: ctest_ddt_host_integration ................. Passed 0.01 sec + Start 4: ctest_var_compatibility_host_integration +4/4 Test #4: ctest_var_compatibility_host_integration ... Passed 0.02 sec + +100% tests passed, 0 tests failed out of 4 + +Total Test time (real) = 0.06 sec +``` + +There are several `...` to enable tests: + +1) `-DCCPP_FRAMEWORK_ENABLE_TESTS=ON` Turns on all regression tests. +2) `-DCCPP_RUN_ADVECTION_TEST=ON` Turns on only the advection test +3) `-DCCPP_RUN_CAPGEN_TEST=ON` Turns on only the capgen test +4) `-DCCPP_RUN_DDT_HOST_TEST=ON` Turns on only the ddt host test +5) `-DCCPP_RUN_VAR_COMPATIBILITY_TEST=ON` Turns on only the variable compatibility test + +By default, the tests will build in release mode. To enable debug mode, you will need to set the build type: `-DCMAKE_BUILD_TYPE=Release` (or if you want release with debug symbols: `-DCMAKE_BUILD_TYPE=RelWithDebInfo`). + +To enable more verbose output for `ccpp_capgen.py`, add `-DCCPP_VERBOSITY=` to the `cmake` command line arguments where `n={1,2,3}` (`n=0` or no verbosity by default). + +If needed, the generated caps will be in `/test//ccpp`. + +### Python regression test interface + +There is a matching Python based API for each regression test. To run the corresponding python tests, build the framework using the build process from above and then you can run: + +```bash +BUILD_DIR= \ +PYTHONPATH=/test/:/scripts/ \ + pytest \ + /test/capgen_test/capgen_test_reports.py \ + /test/advection_test/advection_test_reports.py \ + /test/ddthost_test/ddthost_test_reports.py \ + /test/var_compatibility_test/var_compatibility_test_reports.py +``` + +You may run tests individually instead of all tests as your use case needs. + +Please see each test directory for more information on that specific test. diff --git a/test/advection_test/CMakeLists.txt b/test/advection_test/CMakeLists.txt index 5a9b546d..0f1be200 100644 --- a/test/advection_test/CMakeLists.txt +++ b/test/advection_test/CMakeLists.txt @@ -1,233 +1,57 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.10) -PROJECT(test_host) -ENABLE_LANGUAGE(Fortran) - -include(CMakeForceCompiler) - -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) - -#------------------------------------------------------------------------------ -# -# Set where the CCPP Framework lives -# -#------------------------------------------------------------------------------ -get_filename_component(TEST_ROOT "${CMAKE_SOURCE_DIR}" DIRECTORY) -get_filename_component(CCPP_ROOT "${TEST_ROOT}" DIRECTORY) -#------------------------------------------------------------------------------ -# # Create list of SCHEME_FILES, HOST_FILES, and SUITE_FILES -# Paths should be relative to CMAKE_SOURCE_DIR (this file's directory) -# -#------------------------------------------------------------------------------ -LIST(APPEND SCHEME_FILES "cld_suite_files.txt") -LIST(APPEND SCHEME_FILES_ERROR "cld_suite_files_error.txt") -LIST(APPEND HOST_FILES "test_host_data" "test_host_mod") -LIST(APPEND SUITE_FILES "cld_suite.xml") -LIST(APPEND SUITE_FILES_ERROR "cld_suite_error.xml") +set(SCHEME_FILES "cld_liq" "cld_ice" "apply_constituent_tendencies" "const_indices") +set(SCHEME_FILES_ERROR "cld_liq" "cld_ice" "dlc_liq") +set(HOST_FILES "test_host_data" "test_host_mod") +set(SUITE_FILES "cld_suite.xml") +set(SUITE_FILES_ERROR "cld_suite_error.xml") # HOST is the name of the executable we will build. -# We assume there are files ${HOST}.meta and ${HOST}.F90 in CMAKE_SOURCE_DIR -SET(HOST "${CMAKE_PROJECT_NAME}") - -#------------------------------------------------------------------------------ -# -# End of project-specific input -# -#------------------------------------------------------------------------------ - -# By default, no verbose output -SET(VERBOSITY 0 CACHE STRING "Verbosity level of output (default: 0)") -# By default, generated caps go in ccpp subdir -SET(CCPP_CAP_FILES "${CMAKE_BINARY_DIR}/ccpp" CACHE - STRING "Location of CCPP-generated cap files") - -SET(CCPP_FRAMEWORK ${CCPP_ROOT}/scripts) - -# Use rpaths on MacOSX -set(CMAKE_MACOSX_RPATH 1) - -#------------------------------------------------------------------------------ -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - #message(STATUS "Setting build type to 'Debug' as none was specified.") - #set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) - message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() +set(HOST "test_host") -ADD_COMPILE_OPTIONS(-O0) +# By default, generated caps go in this test specific ccpp subdir +set(CCPP_CAP_FILES "${CMAKE_CURRENT_BINARY_DIR}/ccpp") -if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") -# gfortran -# MESSAGE("gfortran being used.") - ADD_COMPILE_OPTIONS(-fcheck=all) - ADD_COMPILE_OPTIONS(-fbacktrace) - ADD_COMPILE_OPTIONS(-ffpe-trap=zero) - ADD_COMPILE_OPTIONS(-finit-real=nan) - ADD_COMPILE_OPTIONS(-ggdb) - ADD_COMPILE_OPTIONS(-ffree-line-length-none) - ADD_COMPILE_OPTIONS(-cpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") -# ifort -# MESSAGE("ifort being used.") - #ADD_COMPILE_OPTIONS(-check all) - ADD_COMPILE_OPTIONS(-fpe0) - ADD_COMPILE_OPTIONS(-warn) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-debug extended) - ADD_COMPILE_OPTIONS(-fpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "PGI") -# pgf90 -# MESSAGE("pgf90 being used.") - ADD_COMPILE_OPTIONS(-g) - ADD_COMPILE_OPTIONS(-Mipa=noconst) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-Mfree) - ADD_COMPILE_OPTIONS(-Mfptrap) - ADD_COMPILE_OPTIONS(-Mpreprocess) -else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - message (WARNING "This program has only been compiled with gfortran, pgf90 and ifort. If another compiler is needed, the appropriate flags SHOULD be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt") -endif (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") +# Create lists for Fortran and meta data files from file names +list(TRANSFORM SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SCHEME_FORTRAN_FILES) +list(TRANSFORM SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SCHEME_META_FILES) +list(TRANSFORM SCHEME_FILES_ERROR APPEND ".F90" OUTPUT_VARIABLE SCHEME_ERROR_FORTRAN_FILES) +list(TRANSFORM SCHEME_FILES_ERROR APPEND ".meta" OUTPUT_VARIABLE SCHEME_ERROR_META_FILES) +list(TRANSFORM HOST_FILES APPEND ".F90" OUTPUT_VARIABLE ADVECTION_HOST_FORTRAN_FILES) +list(TRANSFORM HOST_FILES APPEND ".meta" OUTPUT_VARIABLE ADVECTION_HOST_METADATA_FILES) -#------------------------------------------------------------------------------ -# CMake Modules -# Set the CMake module path -list(APPEND CMAKE_MODULE_PATH "${CCPP_FRAMEWORK}/cmake") -#------------------------------------------------------------------------------ -# Set OpenMP flags for C/C++/Fortran -if (OPENMP) - include(detect_openmp) - detect_openmp() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - message(STATUS "Enable OpenMP support for C/C++/Fortran compiler") -else(OPENMP) - message (STATUS "Disable OpenMP support for C/C++/Fortran compiler") -endif() - -# Create metadata and source file lists -FOREACH(FILE ${SCHEME_FILES}) - FILE(STRINGS ${FILE} FILENAMES) - LIST(APPEND SCHEME_FILENAMES ${FILENAMES}) -ENDFOREACH(FILE) -string(REPLACE ";" "," SCHEME_METADATA "${SCHEME_FILES}") - -# Create metadata and source file lists -FOREACH(FILE ${SCHEME_FILES_ERROR}) - FILE(STRINGS ${FILE} FILENAMES) - LIST(APPEND SCHEME_FILENAMES_ERROR ${FILENAMES}) -ENDFOREACH(FILE) -string(REPLACE ";" "," SCHEME_METADATA_ERROR "${SCHEME_FILES_ERROR}") - -FOREACH(FILE ${SCHEME_FILENAMES}) - # target_sources prefers absolute pathnames - string(REPLACE ".meta" ".F90" TEMP "${FILE}") - get_filename_component(ABS_PATH "${TEMP}" ABSOLUTE) - list(APPEND LIBRARY_LIST ${ABS_PATH}) -ENDFOREACH(FILE) - -FOREACH(FILE ${HOST_FILES}) - LIST(APPEND HOST_METADATA "${FILE}.meta") - # target_sources prefers absolute pathnames - get_filename_component(ABS_PATH "${FILE}.F90" ABSOLUTE) - LIST(APPEND HOST_SOURCE "${ABS_PATH}") -ENDFOREACH(FILE) -list(APPEND LIBRARY_LIST ${HOST_SOURCE}) -string(REPLACE ";" ".meta," HOST_METADATA "${HOST_FILES}") -set(HOST_METADATA "${HOST_METADATA}.meta,${HOST}.meta") - -string(REPLACE ";" "," SUITE_XML "${SUITE_FILES}") -string(REPLACE ";" "," SUITE_XML_ERROR "${SUITE_FILES_ERROR}") +list(APPEND ADVECTION_HOST_METADATA_FILES "${HOST}.meta") # Run ccpp_capgen that we expect to fail -set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py") -list(APPEND CAPGEN_CMD "--host-files") -list(APPEND CAPGEN_CMD "${HOST_METADATA}") -list(APPEND CAPGEN_CMD "--scheme-files") -list(APPEND CAPGEN_CMD "${SCHEME_METADATA_ERROR}") -list(APPEND CAPGEN_CMD "--suites") -list(APPEND CAPGEN_CMD "${SUITE_XML_ERROR}") -list(APPEND CAPGEN_CMD "--host-name") -list(APPEND CAPGEN_CMD "test_host") -list(APPEND CAPGEN_CMD "--output-root") -list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}") -while (VERBOSITY GREATER 0) - list(APPEND CAPGEN_CMD "--verbose") - MATH(EXPR VERBOSITY "${VERBOSITY} - 1") -endwhile () -list(APPEND CAPGEN_CMD "--debug") -string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}") -MESSAGE(STATUS "Running: ${CAPGEN_STRING}") -EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE CAPGEN_OUT ERROR_VARIABLE CAPGEN_OUT RESULT_VARIABLE RES) -MESSAGE(STATUS "${CAPGEN_OUT}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap generation completed") -else() - # Example: Validate the error message - string(FIND "${CAPGEN_OUT}" "Variables of type ccpp_constituent_properties_t only allowed in register phase" ERROR_INDEX) - - if (ERROR_INDEX GREATER -1) - MESSAGE(STATUS "Capgen build produces expected error message.") - else() - MESSAGE(FATAL_ERROR "CCPP cap generation did not generate expected error. Expected 'Variables of type ccpp_cosntituent_properties_t only allowed in register phase. Got: " ${CAPGEN_OUT}"") - endif() -endif(RES EQUAL 0) +ccpp_capgen(CAPGEN_EXPECT_THROW_ERROR ON + CAPGEN_DEBUG ON + VERBOSITY ${CCPP_VERBOSITY} + HOSTFILES ${ADVECTION_HOST_METADATA_FILES} + SCHEMEFILES ${SCHEME_ERROR_META_FILES} + SUITES ${SUITE_FILES_ERROR} + HOST_NAME ${HOST} + OUTPUT_ROOT "${CCPP_CAP_FILES}") # Run ccpp_capgen -set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py") -list(APPEND CAPGEN_CMD "--host-files") -list(APPEND CAPGEN_CMD "${HOST_METADATA}") -list(APPEND CAPGEN_CMD "--scheme-files") -list(APPEND CAPGEN_CMD "${SCHEME_METADATA}") -list(APPEND CAPGEN_CMD "--suites") -list(APPEND CAPGEN_CMD "${SUITE_XML}") -list(APPEND CAPGEN_CMD "--host-name") -list(APPEND CAPGEN_CMD "test_host") -list(APPEND CAPGEN_CMD "--output-root") -list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}") -while (VERBOSITY GREATER 0) - list(APPEND CAPGEN_CMD "--verbose") - MATH(EXPR VERBOSITY "${VERBOSITY} - 1") -endwhile () -list(APPEND CAPGEN_CMD "--debug") -string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}") -MESSAGE(STATUS "Running: ${CAPGEN_STRING}") -EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE CAPGEN_OUT ERROR_VARIABLE CAPGEN_OUT RESULT_VARIABLE RES) -MESSAGE(STATUS "${CAPGEN_OUT}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap generation completed") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}") -endif(RES EQUAL 0) - -# Retrieve the list of files from datatable.xml and set to CCPP_CAPS -set(DTABLE_CMD "${CCPP_FRAMEWORK}/ccpp_datafile.py") -list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml") -list(APPEND DTABLE_CMD "--ccpp-files") -list(APPEND DTABLE_CMD "--separator=\\;") -string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}") -MESSAGE(STATUS "Running: ${DTABLE_STRING}") -EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} OUTPUT_VARIABLE CCPP_CAPS - RESULT_VARIABLE RES - OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) -message(STATUS "CCPP_CAPS = ${CCPP_CAPS}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap files retrieved") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}") -endif(RES EQUAL 0) -list(APPEND LIBRARY_LIST ${CCPP_CAPS}) -add_library(TESTLIB OBJECT ${LIBRARY_LIST}) -ADD_EXECUTABLE(${HOST} ${HOST}.F90 $) - -INCLUDE_DIRECTORIES(${CCPP_CAP_FILES}) - -set_target_properties(${HOST} PROPERTIES - COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}" - LINK_FLAGS "${CMAKE_Fortran_FLAGS}") +ccpp_capgen(CAPGEN_DEBUG ON + VERBOSITY ${CCPP_VERBOSITY} + HOSTFILES ${ADVECTION_HOST_METADATA_FILES} + SCHEMEFILES ${SCHEME_META_FILES} + SUITES ${SUITE_FILES} + HOST_NAME ${HOST} + OUTPUT_ROOT "${CCPP_CAP_FILES}") + +# Retrieve the list of Fortran files required for test host from datatable.xml and set to CCPP_CAPS_LIST +ccpp_datafile(DATATABLE "${CCPP_CAP_FILES}/datatable.xml" + REPORT_NAME "--ccpp-files") + +# Create test host library +add_library(ADVECTION_TESTLIB OBJECT ${SCHEME_FORTRAN_FILES} + ${ADVECTION_HOST_FORTRAN_FILES} + ${CCPP_CAPS_LIST}) + +# Setup test executable with needed dependencies +add_executable(advection_host_integration test_advection_host_integration.F90 ${HOST}.F90) +target_link_libraries(advection_host_integration PRIVATE ADVECTION_TESTLIB test_utils) +target_include_directories(advection_host_integration PRIVATE "$") + +# Add executable to be called with ctest +add_test(NAME ctest_advection_host_integration COMMAND advection_host_integration) diff --git a/test/advection_test/README.md b/test/advection_test/README.md new file mode 100644 index 00000000..ce3e285d --- /dev/null +++ b/test/advection_test/README.md @@ -0,0 +1,21 @@ +# Advection Test + +Contains tests to exercise the capabilities of the constituents object, including: +- Adding a build-time constituent via metadata property +- Adding a run-time constituent via a register phase + - Also tests that trying to add a constituent outside of the register phase errors as expected +- Passing around and modifying the constituent array +- Accessing and modifying a constituent tendency variable +- Passing around the constituent tendency array +- Dimensions are case-insensitive + +## Building/Running + +To explicitly build/run the advection test host, run: + +```bash +$ cmake -S -B -DCCPP_RUN_ADVECTION_TEST=ON +$ cd +$ make +$ ctest +``` diff --git a/test/advection_test/advection_test_reports.py b/test/advection_test/advection_test_reports.py new file mode 100644 index 00000000..4fbe8e68 --- /dev/null +++ b/test/advection_test/advection_test_reports.py @@ -0,0 +1,127 @@ +#! /usr/bin/env python3 +""" +----------------------------------------------------------------------- + Description: Test advection database report python interface + + Assumptions: + + Command line arguments: build_dir database_filepath + + Usage: python test_reports +----------------------------------------------------------------------- +""" +import os +import unittest + +from test_stub import BaseTests + +_BUILD_DIR = os.path.join(os.path.abspath(os.environ['BUILD_DIR']), "test", "advection_test") +_DATABASE = os.path.abspath(os.path.join(_BUILD_DIR, "ccpp", "datatable.xml")) + +_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) +_SCRIPTS_DIR = os.path.abspath(os.path.join(_FRAMEWORK_DIR, "scripts")) + +# Check data +_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] +_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_cld_suite_cap.F90")] +_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), + os.path.join(_FRAMEWORK_DIR, "src", + "ccpp_constituent_prop_mod.F90"), + os.path.join(_FRAMEWORK_DIR, "src", + "ccpp_scheme_utils.F90"), + os.path.join(_FRAMEWORK_DIR, "src", "ccpp_hashable.F90"), + os.path.join(_FRAMEWORK_DIR, "src", "ccpp_hash_table.F90")] +_CCPP_FILES = _UTILITY_FILES + _HOST_FILES + _SUITE_FILES +_DEPENDENCIES = [""] +_PROCESS_LIST = [""] +_MODULE_LIST = ["cld_ice", "cld_liq", "const_indices", "apply_constituent_tendencies"] +_SUITE_LIST = ["cld_suite"] +_REQUIRED_VARS_CLD = ["ccpp_error_code", "ccpp_error_message", + "horizontal_loop_begin", "horizontal_loop_end", + "surface_air_pressure", "temperature", + "tendency_of_cloud_liquid_dry_mixing_ratio", + "time_step_for_physics", "water_temperature_at_freezing", + "water_vapor_specific_humidity", + "cloud_ice_dry_mixing_ratio", + "cloud_liquid_dry_mixing_ratio", + "ccpp_constituents", + "ccpp_constituent_tendencies", + "number_of_ccpp_constituents", + "dynamic_constituents_for_cld_ice", + "dynamic_constituents_for_cld_liq", + "test_banana_constituent_indices", "test_banana_name", + "banana_array_dim", + "test_banana_name_array", + "test_banana_constituent_index", + # Added by --debug option + "horizontal_dimension", + "vertical_layer_dimension"] +_INPUT_VARS_CLD = ["surface_air_pressure", "temperature", + "horizontal_loop_begin", "horizontal_loop_end", + "time_step_for_physics", "water_temperature_at_freezing", + "water_vapor_specific_humidity", + "cloud_ice_dry_mixing_ratio", + "cloud_liquid_dry_mixing_ratio", + "tendency_of_cloud_liquid_dry_mixing_ratio", + "ccpp_constituents", + "ccpp_constituent_tendencies", + "number_of_ccpp_constituents", + "banana_array_dim", + "test_banana_name_array", "test_banana_name", + # Added by --debug option + "horizontal_dimension", + "vertical_layer_dimension"] +_OUTPUT_VARS_CLD = ["ccpp_error_code", "ccpp_error_message", + "water_vapor_specific_humidity", "temperature", + "tendency_of_cloud_liquid_dry_mixing_ratio", + "cloud_ice_dry_mixing_ratio", + "ccpp_constituents", + "ccpp_constituent_tendencies", + "cloud_liquid_dry_mixing_ratio", + "dynamic_constituents_for_cld_ice", + "dynamic_constituents_for_cld_liq", + "dynamic_constituents_for_cld_liq", + "test_banana_constituent_indices", + "test_banana_constituent_index"] + + +class TestAdvectionHostDataTables(unittest.TestCase, BaseTests.TestHostDataTables): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + +class CommandLineAdvectionHostDatafileRequiredFiles(unittest.TestCase, BaseTests.TestHostCommandLineDataFiles): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestCapgenCldSuite(unittest.TestCase, BaseTests.TestSuite): + database = _DATABASE + required_vars = _REQUIRED_VARS_CLD + input_vars = _INPUT_VARS_CLD + output_vars = _OUTPUT_VARS_CLD + suite_name = "cld_suite" + + +class CommandLineCapgenDdtSuite(unittest.TestCase, BaseTests.TestSuiteCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_CLD + input_vars = _INPUT_VARS_CLD + output_vars = _OUTPUT_VARS_CLD + suite_name = "cld_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" diff --git a/test/advection_test/cld_suite_files.txt b/test/advection_test/cld_suite_files.txt deleted file mode 100644 index 4ce40a53..00000000 --- a/test/advection_test/cld_suite_files.txt +++ /dev/null @@ -1,4 +0,0 @@ -cld_liq.meta -cld_ice.meta -apply_constituent_tendencies.meta -const_indices.meta diff --git a/test/advection_test/cld_suite_files_error.txt b/test/advection_test/cld_suite_files_error.txt deleted file mode 100644 index 63ff75b0..00000000 --- a/test/advection_test/cld_suite_files_error.txt +++ /dev/null @@ -1,3 +0,0 @@ -cld_liq.meta -cld_ice.meta -dlc_liq.meta diff --git a/test/advection_test/run_test b/test/advection_test/run_test deleted file mode 100755 index 2301a4fc..00000000 --- a/test/advection_test/run_test +++ /dev/null @@ -1,271 +0,0 @@ -#! /bin/bash - -currdir="`pwd -P`" -scriptdir="$( cd $( dirname $0 ); pwd -P )" - -## -## Option default values -## -defdir="at_build" -build_dir="${currdir}/${defdir}" -cleanup="PASS" # Other supported options are ALWAYS and NEVER -verbosity=0 - -## -## General syntax help function -## Usage: help -## -help () { - local hname="Usage: `basename ${0}`" - local hprefix="`echo ${hname} | tr '[!-~]' ' '`" - echo "${hname} [ --build-dir ] [ --cleanup ]" - echo "${hprefix} [ --verbosity <#> ]" - hprefix=" " - echo "" - echo "${hprefix} : Directory for building and running the test" - echo "${hprefix} default is /${defdir}" - echo "${hprefix} : Cleanup option is ALWAYS, NEVER, or PASS" - echo "${hprefix} default is PASS" - echo "${hprefix} verbosity: 0, 1, or 2" - echo "${hprefix} default is 0" - exit $1 -} - -## -## Error output function (should be handed a string) -## -perr() { - >&2 echo -e "\nERROR: ${@}\n" - exit 1 -} - -## -## Cleanup the build and test directory -## -docleanup() { - # We start off in the build directory - if [ "${build_dir}" == "${currdir}" ]; then - echo "WARNING: Cannot clean ${build_dir}" - else - cd ${currdir} - rm -rf ${build_dir} - fi -} - -## Process our input arguments -while [ $# -gt 0 ]; do - case $1 in - --h | -h | --help | -help) - help 0 - ;; - --build-dir) - if [ $# -lt 2 ]; then - perr "${1} requires a build directory" - fi - build_dir="${2}" - shift - ;; - --cleanup) - if [ $# -lt 2 ]; then - perr "${1} requies a cleanup option (ALWAYS, NEVER, PASS)" - fi - if [ "${2}" == "ALWAYS" -o "${2}" == "NEVER" -o "${2}" == "PASS" ]; then - cleanup="${2}" - else - perr "Allowed cleanup options: ALWAYS, NEVER, PASS" - fi - shift - ;; - --verbosity) - if [ $# -lt 2 ]; then - perr "${1} requires a verbosity value (0, 1, or 2)" - fi - if [ "${2}" == "0" -o "${2}" == "1" -o "${2}" == "2" ]; then - verbosity=$2 - else - perr "allowed verbosity levels are 0, 1, 2" - fi - shift - ;; - *) - perr "Unrecognized option, \"${1}\"" - ;; - esac - shift -done - -# Create the build directory, if necessary -if [ -d "${build_dir}" ]; then - # Always make sure build_dir is not in the test dir - if [ "$( cd ${build_dir}; pwd -P )" == "${currdir}" ]; then - build_dir="${build_dir}/${defdir}" - fi -else - mkdir -p ${build_dir} - res=$? - if [ $res -ne 0 ]; then - perr "Unable to create build directory, '${build_dir}'" - fi -fi -build_dir="$( cd ${build_dir}; pwd -P )" - -## framework is the CCPP Framework root dir -framework="$( cd $( dirname $( dirname ${scriptdir} ) ); pwd -P )" -fsrc="${framework}/src" - -## -## check strings for datafile command-list test -## NB: This has to be after build_dir is finalized -## -host_files="${build_dir}/ccpp/test_host_ccpp_cap.F90" -hash_files="${fsrc}/ccpp_hashable.F90,${fsrc}/ccpp_hash_table.F90" -suite_files="${build_dir}/ccpp/ccpp_cld_suite_cap.F90" -utility_files="${build_dir}/ccpp/ccpp_kinds.F90" -utility_files="${utility_files},${fsrc}/ccpp_constituent_prop_mod.F90" -utility_files="${utility_files},${fsrc}/ccpp_scheme_utils.F90" -utility_files="${utility_files},${hash_files}" -ccpp_files="${utility_files},${host_files},${suite_files}" -process_list="" -module_list="apply_constituent_tendencies,cld_ice,cld_liq,const_indices" -dependencies="" -suite_list="cld_suite" -required_vars="banana_array_dim" -required_vars="${required_vars},ccpp_constituent_tendencies,ccpp_constituents" -required_vars="${required_vars},ccpp_error_code,ccpp_error_message" -required_vars="${required_vars},cloud_ice_dry_mixing_ratio" -required_vars="${required_vars},cloud_liquid_dry_mixing_ratio" -required_vars="${required_vars},dynamic_constituents_for_cld_ice" -required_vars="${required_vars},dynamic_constituents_for_cld_liq" -required_vars="${required_vars},horizontal_dimension" -required_vars="${required_vars},horizontal_loop_begin" -required_vars="${required_vars},horizontal_loop_end" -required_vars="${required_vars},number_of_ccpp_constituents" -required_vars="${required_vars},surface_air_pressure" -required_vars="${required_vars},temperature" -required_vars="${required_vars},tendency_of_cloud_liquid_dry_mixing_ratio" -required_vars="${required_vars},test_banana_constituent_index" -required_vars="${required_vars},test_banana_constituent_indices" -required_vars="${required_vars},test_banana_name,test_banana_name_array" -required_vars="${required_vars},time_step_for_physics" -required_vars="${required_vars},vertical_layer_dimension" -required_vars="${required_vars},water_temperature_at_freezing" -required_vars="${required_vars},water_vapor_specific_humidity" -input_vars="banana_array_dim" -input_vars="${input_vars},ccpp_constituent_tendencies,ccpp_constituents" -input_vars="${input_vars},cloud_ice_dry_mixing_ratio,cloud_liquid_dry_mixing_ratio" -input_vars="${input_vars},horizontal_dimension" -input_vars="${input_vars},horizontal_loop_begin" -input_vars="${input_vars},horizontal_loop_end" -input_vars="${input_vars},number_of_ccpp_constituents" -input_vars="${input_vars},surface_air_pressure,temperature" -input_vars="${input_vars},tendency_of_cloud_liquid_dry_mixing_ratio" -input_vars="${input_vars},test_banana_name,test_banana_name_array" -input_vars="${input_vars},time_step_for_physics" -input_vars="${input_vars},vertical_layer_dimension" -input_vars="${input_vars},water_temperature_at_freezing" -input_vars="${input_vars},water_vapor_specific_humidity" -output_vars="ccpp_constituent_tendencies,ccpp_constituents" -output_vars="${output_vars},ccpp_error_code,ccpp_error_message" -output_vars="${output_vars},cloud_ice_dry_mixing_ratio" -output_vars="${output_vars},cloud_liquid_dry_mixing_ratio" -output_vars="${output_vars},dynamic_constituents_for_cld_ice" -output_vars="${output_vars},dynamic_constituents_for_cld_liq" -output_vars="${output_vars},temperature" -output_vars="${output_vars},tendency_of_cloud_liquid_dry_mixing_ratio" -output_vars="${output_vars},test_banana_constituent_index" -output_vars="${output_vars},test_banana_constituent_indices" -output_vars="${output_vars},water_vapor_specific_humidity" - -## -## Run a database report and check the return string -## $1 is the report program file -## $2 is the database file -## $3 is the report string -## $4 is the check string -## $5+ are any optional arguments -## -check_datatable() { - local checkstr=${4} - local teststr - local prog=${1} - local database=${2} - local report=${3} - shift 4 - echo "Checking ${report} report" - teststr="`${prog} ${database} ${report} $@`" - if [ "${teststr}" != "${checkstr}" ]; then - perr "datatable check:\nExpected: '${checkstr}'\nGot: '${teststr}'" - fi -} - -# cd to the build directory -cd ${build_dir} -res=$? -if [ $res -ne 0 ]; then - perr "Unable to cd to build directory, '${build_dir}'" -fi -# Clean build directory -rm -rf * -res=$? -if [ $res -ne 0 ]; then - perr "Unable to clean build directory, '${build_dir}'" -fi -# Run CMake -opts="" -if [ $verbosity -gt 0 ]; then - opts="${opts} -DVERBOSITY=${verbosity}" -fi -# Run cmake -cmake ${scriptdir} ${opts} -res=$? -if [ $res -ne 0 ]; then - perr "CMake failed with exit code, ${res}" -fi -# Test the datafile user interface -report_prog="${framework}/scripts/ccpp_datafile.py" -datafile="${build_dir}/ccpp/datatable.xml" -echo "Running python interface tests" -python3 ${scriptdir}/test_reports.py ${build_dir} ${datafile} -res=$? -if [ $res -ne 0 ]; then - perr "python interface tests failed" -fi -echo "Running command line tests" -echo "Checking required files from command line:" -check_datatable ${report_prog} ${datafile} "--host-files" ${host_files} -check_datatable ${report_prog} ${datafile} "--suite-files" ${suite_files} -check_datatable ${report_prog} ${datafile} "--utility-files" ${utility_files} -check_datatable ${report_prog} ${datafile} "--ccpp-files" ${ccpp_files} -echo -e "\nChecking lists from command line" -check_datatable ${report_prog} ${datafile} "--process-list" "${process_list}" -check_datatable ${report_prog} ${datafile} "--module-list" ${module_list} -check_datatable ${report_prog} ${datafile} "--dependencies" "${dependencies}" -check_datatable ${report_prog} ${datafile} "--suite-list" ${suite_list} \ - --sep ";" -echo -e "\nChecking variables from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars} "cld_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars} "cld_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars} "cld_suite" -# Run make -make -res=$? -if [ $res -ne 0 ]; then - perr "make failed with exit code, ${res}" -fi -# Run test -./test_host -res=$? -if [ $res -ne 0 ]; then - perr "test_host failed with exit code, ${res}" -fi - -if [ "${cleanup}" == "ALWAYS" ]; then - docleanup -elif [ $res -eq 0 -a "${cleanup}" == "PASS" ]; then - docleanup -fi - -exit $res diff --git a/test/advection_test/test_advection_host_integration.F90 b/test/advection_test/test_advection_host_integration.F90 new file mode 100644 index 00000000..728137fa --- /dev/null +++ b/test/advection_test/test_advection_host_integration.F90 @@ -0,0 +1,77 @@ +program test + use test_prog, only: test_host, suite_info, cm, cs + + implicit none + + character(len=cs), target :: test_parts1(1) + character(len=cm), target :: test_invars1(12) + character(len=cm), target :: test_outvars1(13) + character(len=cm), target :: test_reqvars1(18) + + type(suite_info) :: test_suites(1) + logical :: run_okay + + test_parts1 = (/ 'physics '/) + test_invars1 = (/ & + 'banana_array_dim ', & + 'cloud_ice_dry_mixing_ratio ', & + 'cloud_liquid_dry_mixing_ratio ', & + 'tendency_of_cloud_liquid_dry_mixing_ratio', & + 'surface_air_pressure ', & + 'temperature ', & + 'time_step_for_physics ', & + 'water_temperature_at_freezing ', & + 'ccpp_constituent_tendencies ', & + 'ccpp_constituents ', & + 'number_of_ccpp_constituents ', & + 'water_vapor_specific_humidity ' /) + test_outvars1 = (/ & + 'ccpp_error_message ', & + 'ccpp_error_code ', & + 'temperature ', & + 'water_vapor_specific_humidity ', & + 'cloud_liquid_dry_mixing_ratio ', & + 'ccpp_constituent_tendencies ', & + 'ccpp_constituents ', & + 'dynamic_constituents_for_cld_liq ', & + 'dynamic_constituents_for_cld_ice ', & + 'tendency_of_cloud_liquid_dry_mixing_ratio', & + 'test_banana_constituent_index ', & + 'test_banana_constituent_indices ', & + 'cloud_ice_dry_mixing_ratio ' /) + test_reqvars1 = (/ & + 'banana_array_dim ', & + 'surface_air_pressure ', & + 'temperature ', & + 'time_step_for_physics ', & + 'cloud_liquid_dry_mixing_ratio ', & + 'tendency_of_cloud_liquid_dry_mixing_ratio', & + 'cloud_ice_dry_mixing_ratio ', & + 'dynamic_constituents_for_cld_liq ', & + 'dynamic_constituents_for_cld_ice ', & + 'water_temperature_at_freezing ', & + 'ccpp_constituent_tendencies ', & + 'ccpp_constituents ', & + 'number_of_ccpp_constituents ', & + 'test_banana_constituent_index ', & + 'test_banana_constituent_indices ', & + 'water_vapor_specific_humidity ', & + 'ccpp_error_message ', & + 'ccpp_error_code ' /) + + ! Setup expected test suite info + test_suites(1)%suite_name = 'cld_suite' + test_suites(1)%suite_parts => test_parts1 + test_suites(1)%suite_input_vars => test_invars1 + test_suites(1)%suite_output_vars => test_outvars1 + test_suites(1)%suite_required_vars => test_reqvars1 + + call test_host(run_okay, test_suites) + + if (run_okay) then + STOP 0 + else + STOP -1 + end if + +end program test diff --git a/test/advection_test/test_host.F90 b/test/advection_test/test_host.F90 index fa6c8e43..c1482a93 100644 --- a/test/advection_test/test_host.F90 +++ b/test/advection_test/test_host.F90 @@ -25,8 +25,6 @@ module test_prog type(ccpp_constituent_properties_t), private, target, allocatable :: host_constituents(:) - - private :: check_list private :: check_suite private :: advect_constituents ! Move data around private :: check_errflg @@ -50,92 +48,10 @@ subroutine check_errflg(subname, errflg, errmsg, errflg_final) end subroutine check_errflg - logical function check_list(test_list, chk_list, list_desc, suite_name) - ! Check a list () against its expected value () - - ! Dummy arguments - character(len=*), intent(in) :: test_list(:) - character(len=*), intent(in) :: chk_list(:) - character(len=*), intent(in) :: list_desc - character(len=*), optional, intent(in) :: suite_name - - ! Local variables - logical :: found - integer :: num_items - integer :: lindex, tindex - integer, allocatable :: check_unique(:) - character(len=2) :: sep - character(len=256) :: errmsg - - check_list = .true. - errmsg = '' - - ! Check the list size - num_items = size(chk_list) - if (size(test_list) /= num_items) then - write(errmsg, '(a,i0,2a)') 'ERROR: Found ', size(test_list), & - ' ', trim(list_desc) - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' for suite, ', & - trim(suite_name) - end if - write(errmsg(len_trim(errmsg)+1:), '(a,i0)') ', should be ', num_items - write(6, *) trim(errmsg) - errmsg = '' - check_list = .false. - end if - - ! Now, check the list contents for 1-1 correspondence - if (check_list) then - allocate(check_unique(num_items)) - check_unique = -1 - do lindex = 1, num_items - found = .false. - do tindex = 1, num_items - if (trim(test_list(lindex)) == trim(chk_list(tindex))) then - check_unique(tindex) = lindex - found = .true. - exit - end if - end do - if (.not. found) then - check_list = .false. - write(errmsg, '(5a)') 'ERROR: ', trim(list_desc), ' item, ', & - trim(test_list(lindex)), ', was not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - write(6, *) trim(errmsg) - errmsg = '' - end if - end do - if (check_list .and. ANY(check_unique < 0)) then - check_list = .false. - write(errmsg, '(3a)') 'ERROR: The following ', trim(list_desc), & - ' items were not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - sep = '; ' - do lindex = 1, num_items - if (check_unique(lindex) < 0) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') sep, & - trim(chk_list(lindex)) - sep = ', ' - end if - end do - write(6, *) trim(errmsg) - errmsg = '' - end if - end if - - end function check_list - logical function check_suite(test_suite) use test_host_ccpp_cap, only: ccpp_physics_suite_part_list use test_host_ccpp_cap, only: ccpp_physics_suite_variables + use test_utils, only: check_list ! Dummy argument type(suite_info), intent(in) :: test_suite @@ -242,6 +158,7 @@ subroutine test_host(retval, test_suites) use test_host_ccpp_cap, only: ccpp_physics_suite_list use test_host_ccpp_cap, only: test_host_const_get_index use test_host_ccpp_cap, only: test_host_model_const_properties + use test_utils, only: check_list type(suite_info), intent(in) :: test_suites(:) logical, intent(out) :: retval @@ -1134,81 +1051,3 @@ subroutine test_host(retval, test_suites) end subroutine test_host end module test_prog - - program test - use test_prog, only: test_host, suite_info, cm, cs - - implicit none - - character(len=cs), target :: test_parts1(1) - character(len=cm), target :: test_invars1(12) - character(len=cm), target :: test_outvars1(13) - character(len=cm), target :: test_reqvars1(18) - - type(suite_info) :: test_suites(1) - logical :: run_okay - - test_parts1 = (/ 'physics '/) - test_invars1 = (/ & - 'banana_array_dim ', & - 'cloud_ice_dry_mixing_ratio ', & - 'cloud_liquid_dry_mixing_ratio ', & - 'tendency_of_cloud_liquid_dry_mixing_ratio', & - 'surface_air_pressure ', & - 'temperature ', & - 'time_step_for_physics ', & - 'water_temperature_at_freezing ', & - 'ccpp_constituent_tendencies ', & - 'ccpp_constituents ', & - 'number_of_ccpp_constituents ', & - 'water_vapor_specific_humidity ' /) - test_outvars1 = (/ & - 'ccpp_error_message ', & - 'ccpp_error_code ', & - 'temperature ', & - 'water_vapor_specific_humidity ', & - 'cloud_liquid_dry_mixing_ratio ', & - 'ccpp_constituent_tendencies ', & - 'ccpp_constituents ', & - 'dynamic_constituents_for_cld_liq ', & - 'dynamic_constituents_for_cld_ice ', & - 'tendency_of_cloud_liquid_dry_mixing_ratio', & - 'test_banana_constituent_index ', & - 'test_banana_constituent_indices ', & - 'cloud_ice_dry_mixing_ratio ' /) - test_reqvars1 = (/ & - 'banana_array_dim ', & - 'surface_air_pressure ', & - 'temperature ', & - 'time_step_for_physics ', & - 'cloud_liquid_dry_mixing_ratio ', & - 'tendency_of_cloud_liquid_dry_mixing_ratio', & - 'cloud_ice_dry_mixing_ratio ', & - 'dynamic_constituents_for_cld_liq ', & - 'dynamic_constituents_for_cld_ice ', & - 'water_temperature_at_freezing ', & - 'ccpp_constituent_tendencies ', & - 'ccpp_constituents ', & - 'number_of_ccpp_constituents ', & - 'test_banana_constituent_index ', & - 'test_banana_constituent_indices ', & - 'water_vapor_specific_humidity ', & - 'ccpp_error_message ', & - 'ccpp_error_code ' /) - - ! Setup expected test suite info - test_suites(1)%suite_name = 'cld_suite' - test_suites(1)%suite_parts => test_parts1 - test_suites(1)%suite_input_vars => test_invars1 - test_suites(1)%suite_output_vars => test_outvars1 - test_suites(1)%suite_required_vars => test_reqvars1 - - call test_host(run_okay, test_suites) - - if (run_okay) then - STOP 0 - else - STOP -1 - end if - -end program test diff --git a/test/advection_test/test_reports.py b/test/advection_test/test_reports.py deleted file mode 100644 index 04294364..00000000 --- a/test/advection_test/test_reports.py +++ /dev/null @@ -1,194 +0,0 @@ -#! /usr/bin/env python3 -""" ------------------------------------------------------------------------ - Description: Test advection database report python interface - - Assumptions: - - Command line arguments: build_dir database_filepath - - Usage: python test_reports ------------------------------------------------------------------------ -""" -import sys -import os - -_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) -_SCRIPTS_DIR = os.path.abspath(os.path.join(_FRAMEWORK_DIR, "scripts")) - -if not os.path.exists(_SCRIPTS_DIR): - raise ImportError("Cannot find scripts directory") -# end if - -if ((sys.version_info[0] < 3) or - (sys.version_info[0] == 3) and (sys.version_info[1] < 8)): - raise Exception("Python 3.8 or greater required") -# end if - -sys.path.append(_SCRIPTS_DIR) -# pylint: disable=wrong-import-position -from ccpp_datafile import datatable_report, DatatableReport -# pylint: enable=wrong-import-position - -def usage(errmsg=None): - """Raise an exception with optional error message and usage message""" - emsg = "usage: {} " - if errmsg: - emsg = errmsg + '\n' + emsg - # end if - raise ValueError(emsg.format(sys.argv[0])) - -if len(sys.argv) != 3: - usage() -# end if - -_BUILD_DIR = os.path.abspath(sys.argv[1]) -_DATABASE = os.path.abspath(sys.argv[2]) -if not os.path.isdir(_BUILD_DIR): - _EMSG = " must be an existing build directory" - usage(_EMSG) -# end if -if (not os.path.exists(_DATABASE)) or (not os.path.isfile(_DATABASE)): - _EMSG = " must be an existing CCPP database file" - usage(_EMSG) -# end if - -# Check data -_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] -_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_cld_suite_cap.F90")] -_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), - os.path.join(_FRAMEWORK_DIR, "src", - "ccpp_constituent_prop_mod.F90"), - os.path.join(_FRAMEWORK_DIR, "src", - "ccpp_scheme_utils.F90"), - os.path.join(_FRAMEWORK_DIR, "src", "ccpp_hashable.F90"), - os.path.join(_FRAMEWORK_DIR, "src", "ccpp_hash_table.F90")] -_CCPP_FILES = _UTILITY_FILES + _HOST_FILES + _SUITE_FILES -_PROCESS_LIST = list() -_MODULE_LIST = ["cld_ice", "cld_liq", "const_indices", "apply_constituent_tendencies"] -_SUITE_LIST = ["cld_suite"] -_DYN_CONST_ROUTINES = ["cld_ice_dynamic_constituents", "cld_liq_dynamic_constituents"] -_REQUIRED_VARS_CLD = ["ccpp_error_code", "ccpp_error_message", - "horizontal_loop_begin", "horizontal_loop_end", - "surface_air_pressure", "temperature", - "tendency_of_cloud_liquid_dry_mixing_ratio", - "time_step_for_physics", "water_temperature_at_freezing", - "water_vapor_specific_humidity", - "cloud_ice_dry_mixing_ratio", - "cloud_liquid_dry_mixing_ratio", - "ccpp_constituents", - "ccpp_constituent_tendencies", - "number_of_ccpp_constituents", - "dynamic_constituents_for_cld_ice", - "dynamic_constituents_for_cld_liq", - "test_banana_constituent_indices", "test_banana_name", - "banana_array_dim", - "test_banana_name_array", - "test_banana_constituent_index", - # Added by --debug option - "horizontal_dimension", - "vertical_layer_dimension"] -_INPUT_VARS_CLD = ["surface_air_pressure", "temperature", - "horizontal_loop_begin", "horizontal_loop_end", - "time_step_for_physics", "water_temperature_at_freezing", - "water_vapor_specific_humidity", - "cloud_ice_dry_mixing_ratio", - "cloud_liquid_dry_mixing_ratio", - "tendency_of_cloud_liquid_dry_mixing_ratio", - "ccpp_constituents", - "ccpp_constituent_tendencies", - "number_of_ccpp_constituents", - "banana_array_dim", - "test_banana_name_array", "test_banana_name", - # Added by --debug option - "horizontal_dimension", - "vertical_layer_dimension"] -_OUTPUT_VARS_CLD = ["ccpp_error_code", "ccpp_error_message", - "water_vapor_specific_humidity", "temperature", - "tendency_of_cloud_liquid_dry_mixing_ratio", - "cloud_ice_dry_mixing_ratio", - "ccpp_constituents", - "ccpp_constituent_tendencies", - "cloud_liquid_dry_mixing_ratio", - "dynamic_constituents_for_cld_ice", - "dynamic_constituents_for_cld_liq", - "test_banana_constituent_indices", - "test_banana_constituent_index"] - -def fields_string(field_type, field_list, sep): - """Create an error string for field(s), . - is used to separate items in """ - indent = ' '*11 - if field_list: - if len(field_list) > 1: - field_str = "{} Fields: ".format(field_type) - else: - field_str = "{} Field: ".format(field_type) - # end if - fmsg = "\n{}{}{}".format(indent, field_str, sep.join(field_list)) - else: - fmsg = "" - # end if - return fmsg - -def check_datatable(database, report_type, check_list, sep=','): - """Run a database report and check the return string. - If an error is found, print an error message. - Return the number of errors""" - if sep is None: - sep = ',' - # end if - test_str = datatable_report(database, report_type, sep) - test_list = [x for x in test_str.split(sep) if x] - missing = list() - unexpected = list() - for item in check_list: - if item not in test_list: - missing.append(item) - # end if - # end for - for item in test_list: - if item not in check_list: - unexpected.append(item) - # end if - # end for - if missing or unexpected: - vmsg = "ERROR in {} datafile check:".format(report_type.action) - vmsg += fields_string("Missing", missing, sep) - vmsg += fields_string("Unexpected", unexpected, sep) - print(vmsg) - else: - print("{} report okay".format(report_type.action)) - # end if - return len(missing) + len(unexpected) - -NUM_ERRORS = 0 -print("Checking required files from python:") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("host_files"), - _HOST_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_files"), - _SUITE_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("utility_files"), - _UTILITY_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("ccpp_files"), - _CCPP_FILES) -print("\nChecking lists from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("process_list"), - _PROCESS_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("module_list"), - _MODULE_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_list"), - _SUITE_LIST) -print("\nChecking variables for CLD suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="cld_suite"), - _REQUIRED_VARS_CLD) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="cld_suite"), - _INPUT_VARS_CLD) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="cld_suite"), - _OUTPUT_VARS_CLD) - -sys.exit(NUM_ERRORS) diff --git a/test/capgen_test/.gitignore b/test/capgen_test/.gitignore deleted file mode 100644 index 378eac25..00000000 --- a/test/capgen_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/test/capgen_test/CMakeLists.txt b/test/capgen_test/CMakeLists.txt index ccae4f08..7aa3b60c 100644 --- a/test/capgen_test/CMakeLists.txt +++ b/test/capgen_test/CMakeLists.txt @@ -1,188 +1,54 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) -PROJECT(test_host) -ENABLE_LANGUAGE(Fortran) -include(CMakeForceCompiler) - -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) - -#------------------------------------------------------------------------------ -# -# Set where the CCPP Framework lives -# -#------------------------------------------------------------------------------ -get_filename_component(TEST_ROOT "${CMAKE_SOURCE_DIR}" DIRECTORY) -get_filename_component(CCPP_ROOT "${TEST_ROOT}" DIRECTORY) #------------------------------------------------------------------------------ # # Create list of SCHEME_FILES, HOST_FILES, and SUITE_FILES # Paths should be relative to CMAKE_SOURCE_DIR (this file's directory) # #------------------------------------------------------------------------------ -LIST(APPEND SCHEME_FILES "temp_scheme_files.txt" "ddt_suite_files.txt") -LIST(APPEND HOST_FILES "test_host_data" "test_host_mod") -LIST(APPEND SUITE_FILES "ddt_suite.xml" "temp_suite.xml") +set(SCHEME_FILES "setup_coeffs" "temp_set" "temp_adjust" "temp_calc_adjust") +set(SUITE_SCHEME_FILES "make_ddt" "environ_conditions") +set(HOST_FILES "test_host_data" "test_host_mod") +set(SUITE_FILES "ddt_suite.xml" "temp_suite.xml") + # HOST is the name of the executable we will build. # We assume there are files ${HOST}.meta and ${HOST}.F90 in CMAKE_SOURCE_DIR -SET(HOST "${CMAKE_PROJECT_NAME}") +set(HOST "test_host") -#------------------------------------------------------------------------------ -# -# End of project-specific input -# -#------------------------------------------------------------------------------ - -# By default, no verbose output -SET(VERBOSITY 0 CACHE STRING "Verbosity level of output (default: 0)") # By default, generated caps go in ccpp subdir -SET(CCPP_CAP_FILES "${CMAKE_BINARY_DIR}/ccpp" CACHE - STRING "Location of CCPP-generated cap files") - -SET(CCPP_FRAMEWORK ${CCPP_ROOT}/scripts) - -# Use rpaths on MacOSX -set(CMAKE_MACOSX_RPATH 1) - -#------------------------------------------------------------------------------ -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - #message(STATUS "Setting build type to 'Debug' as none was specified.") - #set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) - message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -ADD_COMPILE_OPTIONS(-O0) - -if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") -# gfortran -# MESSAGE("gfortran being used.") - ADD_COMPILE_OPTIONS(-fcheck=all) - ADD_COMPILE_OPTIONS(-fbacktrace) - ADD_COMPILE_OPTIONS(-ffpe-trap=zero) - ADD_COMPILE_OPTIONS(-finit-real=nan) - ADD_COMPILE_OPTIONS(-ggdb) - ADD_COMPILE_OPTIONS(-ffree-line-length-none) - ADD_COMPILE_OPTIONS(-cpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") -# ifort -# MESSAGE("ifort being used.") - #ADD_COMPILE_OPTIONS(-check all) - ADD_COMPILE_OPTIONS(-fpe0) - ADD_COMPILE_OPTIONS(-warn) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-debug extended) - ADD_COMPILE_OPTIONS(-fpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "PGI") -# pgf90 -# MESSAGE("pgf90 being used.") - ADD_COMPILE_OPTIONS(-g) - ADD_COMPILE_OPTIONS(-Mipa=noconst) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-Mfree) - ADD_COMPILE_OPTIONS(-Mfptrap) - ADD_COMPILE_OPTIONS(-Mpreprocess) -else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - message (WARNING "This program has only been compiled with gfortran, pgf90 and ifort. If another compiler is needed, the appropriate flags SHOULD be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt") -endif (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - -#------------------------------------------------------------------------------ -# CMake Modules -# Set the CMake module path -list(APPEND CMAKE_MODULE_PATH "${CCPP_FRAMEWORK}/cmake") -#------------------------------------------------------------------------------ -# Set OpenMP flags for C/C++/Fortran -if (OPENMP) - include(detect_openmp) - detect_openmp() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - message(STATUS "Enable OpenMP support for C/C++/Fortran compiler") -else(OPENMP) - message (STATUS "Disable OpenMP support for C/C++/Fortran compiler") -endif() - -# Create metadata and source file lists -FOREACH(FILE ${SCHEME_FILES}) - FILE(STRINGS ${FILE} FILENAMES) - LIST(APPEND SCHEME_FILENAMES ${FILENAMES}) -ENDFOREACH(FILE) -string(REPLACE ";" "," SCHEME_METADATA "${SCHEME_FILES}") - -FOREACH(FILE ${SCHEME_FILENAMES}) - # target_sources prefers absolute pathnames - string(REPLACE ".meta" ".F90" TEMP "${FILE}") - get_filename_component(ABS_PATH "${TEMP}" ABSOLUTE) - list(APPEND LIBRARY_LIST ${ABS_PATH}) -ENDFOREACH(FILE) - -FOREACH(FILE ${HOST_FILES}) - LIST(APPEND HOST_METADATA "${FILE}.meta") - # target_sources prefers absolute pathnames - get_filename_component(ABS_PATH "${FILE}.F90" ABSOLUTE) - LIST(APPEND HOST_SOURCE "${ABS_PATH}") -ENDFOREACH(FILE) -list(APPEND LIBRARY_LIST ${HOST_SOURCE}) -string(REPLACE ";" ".meta," HOST_METADATA "${HOST_FILES}") -set(HOST_METADATA "${HOST_METADATA}.meta,${HOST}.meta") - -string(REPLACE ";" "," SUITE_XML "${SUITE_FILES}") - -# Run ccpp_capgen -set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py") -list(APPEND CAPGEN_CMD "--host-files") -list(APPEND CAPGEN_CMD "${HOST_METADATA}") -list(APPEND CAPGEN_CMD "--scheme-files") -list(APPEND CAPGEN_CMD "${SCHEME_METADATA}") -list(APPEND CAPGEN_CMD "--suites") -list(APPEND CAPGEN_CMD "${SUITE_XML}") -list(APPEND CAPGEN_CMD "--host-name") -list(APPEND CAPGEN_CMD "test_host") -list(APPEND CAPGEN_CMD "--output-root") -list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}") -while (VERBOSITY GREATER 0) - list(APPEND CAPGEN_CMD "--verbose") - MATH(EXPR VERBOSITY "${VERBOSITY} - 1") -endwhile () -list(APPEND CAPGEN_CMD "--debug") -string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}") -MESSAGE(STATUS "Running: ${CAPGEN_STRING}") -EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE CAPGEN_OUT ERROR_VARIABLE CAPGEN_OUT RESULT_VARIABLE RES) -MESSAGE(STATUS "${CAPGEN_OUT}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap generation completed") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}") -endif(RES EQUAL 0) - -# Retrieve the list of files from datatable.xml and set to CCPP_CAPS -set(DTABLE_CMD "${CCPP_FRAMEWORK}/ccpp_datafile.py") -list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml") -list(APPEND DTABLE_CMD "--ccpp-files") -list(APPEND DTABLE_CMD "--separator=\\;") -string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}") -MESSAGE(STATUS "Running: ${DTABLE_STRING}") -EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} OUTPUT_VARIABLE CCPP_CAPS - RESULT_VARIABLE RES - OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) -message(STATUS "CCPP_CAPS = ${CCPP_CAPS}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap files retrieved") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}") -endif(RES EQUAL 0) -list(APPEND LIBRARY_LIST ${CCPP_CAPS}) -add_library(TESTLIB OBJECT ${LIBRARY_LIST}) -ADD_EXECUTABLE(${HOST} ${HOST}.F90 $) - -INCLUDE_DIRECTORIES(${CCPP_CAP_FILES}) - -set_target_properties(${HOST} PROPERTIES - COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}" - LINK_FLAGS "${CMAKE_Fortran_FLAGS}") +set(CCPP_CAP_FILES "${CMAKE_CURRENT_BINARY_DIR}/ccpp") + +# Create lists for Fortran and meta data files from file names +list(TRANSFORM SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SCHEME_FORTRAN_FILES) +list(TRANSFORM SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SCHEME_META_FILES) +list(TRANSFORM SUITE_SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SUITE_SCHEME_FORTRAN_FILES) +list(TRANSFORM SUITE_SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SUITE_SCHEME_META_FILES) +list(TRANSFORM HOST_FILES APPEND ".F90" OUTPUT_VARIABLE CAPGEN_HOST_FORTRAN_FILES) +list(TRANSFORM HOST_FILES APPEND ".meta" OUTPUT_VARIABLE CAPGEN_HOST_METADATA_FILES) + +list(APPEND CAPGEN_HOST_METADATA_FILES "${HOST}.meta") + +ccpp_capgen(CAPGEN_DEBUG ON + VERBOSITY ${CCPP_VERBOSITY} + HOSTFILES ${CAPGEN_HOST_METADATA_FILES} + SCHEMEFILES ${SCHEME_META_FILES} ${SUITE_SCHEME_META_FILES} + SUITES ${SUITE_FILES} + HOST_NAME ${HOST} + OUTPUT_ROOT "${CCPP_CAP_FILES}") + +# Retrieve the list of Fortran files required for test host from datatable.xml and set to CCPP_CAPS_LIST +ccpp_datafile(DATATABLE "${CCPP_CAP_FILES}/datatable.xml" + REPORT_NAME "--ccpp-files") + +# Create test host library +add_library(CAPGEN_TESTLIB OBJECT ${SCHEME_FORTRAN_FILES} + ${SUITE_SCHEME_FORTRAN_FILES} + ${CAPGEN_HOST_FORTRAN_FILES} + ${CCPP_CAPS_LIST}) + +# Setup test executable with needed dependencies +add_executable(capgen_host_integration test_capgen_host_integration.F90 ${HOST}.F90) +target_link_libraries(capgen_host_integration PRIVATE CAPGEN_TESTLIB test_utils) +target_include_directories(capgen_host_integration PRIVATE "$") + +# Add executable to be called with ctest +add_test(NAME ctest_capgen_host_integration COMMAND capgen_host_integration) diff --git a/test/capgen_test/README.md b/test/capgen_test/README.md index 127544e0..f989cbc0 100644 --- a/test/capgen_test/README.md +++ b/test/capgen_test/README.md @@ -1,6 +1,20 @@ -ccpp_capgen test -=========== +# Capgen Test -To build and run the ccpp_capgen test, run ./run_test -This script will build and run the test. -The exit code is zero (0) on PASS and non-zero on FAIL. +Contains tests for overall capgen capabilities such as: +- Multiple suites +- Multiple groups +- General DDT usage +- Dimensions with `ccpp_constant_one:N` and just `N` +- Non-standard dimensions (not just horizontal and vertical) (including integer dimensions) +- Variables that should be promoted to suite level + +## Building/Running + +To explicitly build/run the capgen test host, run: + +```bash +$ cmake -S -B -DCCPP_RUN_CAPGEN_TEST=ON +$ cd +$ make +$ ctest +``` diff --git a/test/capgen_test/capgen_test_reports.py b/test/capgen_test/capgen_test_reports.py new file mode 100644 index 00000000..b5eb60d0 --- /dev/null +++ b/test/capgen_test/capgen_test_reports.py @@ -0,0 +1,150 @@ +#! /usr/bin/env python3 +""" +----------------------------------------------------------------------- + Description: Test capgen database report python interface + + Assumptions: + + Command line arguments: build_dir database_filepath + + Usage: python test_reports +----------------------------------------------------------------------- +""" +import os +import unittest + +from test_stub import BaseTests + +_BUILD_DIR = os.path.join(os.path.abspath(os.environ['BUILD_DIR']), "test", "capgen_test") +_DATABASE = os.path.abspath(os.path.join(_BUILD_DIR, "ccpp", "datatable.xml")) + +_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) +_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") +_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") + +# Check data +_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] +_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] +_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), + os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), + os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), + os.path.join(_SRC_DIR, "ccpp_hashable.F90"), + os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] +_CCPP_FILES = _UTILITY_FILES + \ + [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] +_DEPENDENCIES = [os.path.join(_TEST_DIR, "adjust", "qux.F90"), + os.path.join(_TEST_DIR, "bar.F90"), + os.path.join(_TEST_DIR, "foo.F90")] +_PROCESS_LIST = ["setter=temp_set", "adjusting=temp_calc_adjust"] +_MODULE_LIST = ["environ_conditions", "make_ddt", "setup_coeffs", "temp_adjust", + "temp_calc_adjust", "temp_set"] +_SUITE_LIST = ["ddt_suite", "temp_suite"] +_INPUT_VARS_DDT = ["model_times", "number_of_model_times", + "horizontal_loop_begin", "horizontal_loop_end", + "surface_air_pressure", "horizontal_dimension"] +_OUTPUT_VARS_DDT = ["ccpp_error_code", "ccpp_error_message", "model_times", + "surface_air_pressure", "number_of_model_times"] +_REQUIRED_VARS_DDT = _INPUT_VARS_DDT + _OUTPUT_VARS_DDT +_PROT_VARS_TEMP = ["horizontal_loop_begin", "horizontal_loop_end", + "horizontal_dimension", "vertical_layer_dimension", + "number_of_tracers", + "lower_bound_of_vertical_dimension_of_soil", + "upper_bound_of_vertical_dimension_of_soil", + "configuration_variable", + # Added for --debug + "index_of_water_vapor_specific_humidity", + "vertical_interface_dimension"] +_REQUIRED_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", + "potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "potential_temperature_increment", + "surface_air_pressure", "time_step_for_physics", + "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] +_INPUT_VARS_TEMP = ["potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "potential_temperature_increment", + "surface_air_pressure", "time_step_for_physics", + "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] +_OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", + "potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "surface_air_pressure", "water_vapor_specific_humidity", + "soil_levels", + "temperature_at_diagnostic_levels", + "array_variable_for_testing"] + + +class TestCapgenHostDataTables(unittest.TestCase, BaseTests.TestHostDataTables): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + + +class CommandLineCapgenHostDatafileRequiredFiles(unittest.TestCase, BaseTests.TestHostCommandLineDataFiles): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestCapgenDdtSuite(unittest.TestCase, BaseTests.TestSuite): + database = _DATABASE + required_vars = _REQUIRED_VARS_DDT + input_vars = _INPUT_VARS_DDT + output_vars = _OUTPUT_VARS_DDT + suite_name = "ddt_suite" + + +class CommandLineCapgenDdtSuite(unittest.TestCase, BaseTests.TestSuiteCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_DDT + input_vars = _INPUT_VARS_DDT + output_vars = _OUTPUT_VARS_DDT + suite_name = "ddt_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestCapgenTempSuite(unittest.TestCase, BaseTests.TestSuiteExcludeProtected): + database = _DATABASE + required_vars = _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP + input_vars = _INPUT_VARS_TEMP + _PROT_VARS_TEMP + required_vars_excluding_protected = _REQUIRED_VARS_TEMP + input_vars_excluding_protected = _INPUT_VARS_TEMP + output_vars = _OUTPUT_VARS_TEMP + suite_name = "temp_suite" + + +class CommandLineCapgenTempSuite(unittest.TestCase, BaseTests.TestSuiteExcludeProtectedCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP + input_vars = _INPUT_VARS_TEMP + _PROT_VARS_TEMP + required_vars_excluding_protected = _REQUIRED_VARS_TEMP + input_vars_excluding_protected = _INPUT_VARS_TEMP + output_vars = _OUTPUT_VARS_TEMP + suite_name = "temp_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" diff --git a/test/capgen_test/ddt_suite_files.txt b/test/capgen_test/ddt_suite_files.txt deleted file mode 100644 index 7f96a84c..00000000 --- a/test/capgen_test/ddt_suite_files.txt +++ /dev/null @@ -1,2 +0,0 @@ -make_ddt.meta -environ_conditions.meta diff --git a/test/capgen_test/run_test b/test/capgen_test/run_test deleted file mode 100755 index 99ffc6a2..00000000 --- a/test/capgen_test/run_test +++ /dev/null @@ -1,299 +0,0 @@ -#! /bin/bash - -currdir="`pwd -P`" -scriptdir="$( cd $( dirname $0 ); pwd -P )" - -## -## Option default values -## -defdir="ct_build" -build_dir="${currdir}/${defdir}" -cleanup="PASS" # Other supported options are ALWAYS and NEVER -verbosity=0 - -## -## General syntax help function -## Usage: help -## -help () { - local hname="Usage: `basename ${0}`" - local hprefix="`echo ${hname} | tr '[!-~]' ' '`" - echo "${hname} [ --build-dir ] [ --cleanup ]" - echo "${hprefix} [ --verbosity <#> ]" - hprefix=" " - echo "" - echo "${hprefix} : Directory for building and running the test" - echo "${hprefix} default is /${defdir}" - echo "${hprefix} : Cleanup option is ALWAYS, NEVER, or PASS" - echo "${hprefix} default is PASS" - echo "${hprefix} verbosity: 0, 1, or 2" - echo "${hprefix} default is 0" - exit $1 -} - -## -## Error output function (should be handed a string) -## -perr() { - >&2 echo -e "\nERROR: ${@}\n" - exit 1 -} - -## -## Cleanup the build and test directory -## -docleanup() { - # We start off in the build directory - if [ "${build_dir}" == "${currdir}" ]; then - echo "WARNING: Cannot clean ${build_dir}" - else - cd ${currdir} - rm -rf ${build_dir} - fi -} - -## Process our input arguments -while [ $# -gt 0 ]; do - case $1 in - --h | -h | --help | -help) - help 0 - ;; - --build-dir) - if [ $# -lt 2 ]; then - perr "${1} requires a build directory" - fi - build_dir="${2}" - shift - ;; - --cleanup) - if [ $# -lt 2 ]; then - perr "${1} requies a cleanup option (ALWAYS, NEVER, PASS)" - fi - if [ "${2}" == "ALWAYS" -o "${2}" == "NEVER" -o "${2}" == "PASS" ]; then - cleanup="${2}" - else - perr "Allowed cleanup options: ALWAYS, NEVER, PASS" - fi - shift - ;; - --verbosity) - if [ $# -lt 2 ]; then - perr "${1} requires a verbosity value (0, 1, or 2)" - fi - if [ "${2}" == "0" -o "${2}" == "1" -o "${2}" == "2" ]; then - verbosity=$2 - else - perr "allowed verbosity levels are 0, 1, 2" - fi - shift - ;; - *) - perr "Unrecognized option, \"${1}\"" - ;; - esac - shift -done - -# Create the build directory, if necessary -if [ -d "${build_dir}" ]; then - # Always make sure build_dir is not in the test dir - if [ "$( cd ${build_dir}; pwd -P )" == "${currdir}" ]; then - build_dir="${build_dir}/${defdir}" - fi -else - mkdir -p ${build_dir} - res=$? - if [ $res -ne 0 ]; then - perr "Unable to create build directory, '${build_dir}'" - fi -fi -build_dir="$( cd ${build_dir}; pwd -P )" - -## framework is the CCPP Framework root dir -framework="$( cd $( dirname $( dirname ${scriptdir} ) ); pwd -P )" -frame_src="${framework}/src" - -## -## check strings for datafile command-list test -## NB: This has to be after build_dir is finalized -## -host_files="${build_dir}/ccpp/test_host_ccpp_cap.F90" -suite_files="${build_dir}/ccpp/ccpp_ddt_suite_cap.F90" -suite_files="${suite_files},${build_dir}/ccpp/ccpp_temp_suite_cap.F90" -utility_files="${build_dir}/ccpp/ccpp_kinds.F90" -utility_files="${utility_files},${frame_src}/ccpp_constituent_prop_mod.F90" -utility_files="${utility_files},${frame_src}/ccpp_scheme_utils.F90" -utility_files="${utility_files},${frame_src}/ccpp_hashable.F90" -utility_files="${utility_files},${frame_src}/ccpp_hash_table.F90" -ccpp_files="${utility_files}" -ccpp_files="${ccpp_files},${build_dir}/ccpp/test_host_ccpp_cap.F90" -ccpp_files="${ccpp_files},${build_dir}/ccpp/ccpp_ddt_suite_cap.F90" -ccpp_files="${ccpp_files},${build_dir}/ccpp/ccpp_temp_suite_cap.F90" -process_list="adjusting=temp_calc_adjust,setter=temp_set" -module_list="environ_conditions,make_ddt,setup_coeffs,temp_adjust,temp_calc_adjust,temp_set" -dependencies="${scriptdir}/adjust/qux.F90,${scriptdir}/bar.F90,${scriptdir}/foo.F90" -suite_list="ddt_suite;temp_suite" -required_vars_ddt="ccpp_error_code,ccpp_error_message,horizontal_dimension" -required_vars_ddt="${required_vars_ddt},horizontal_loop_begin" -required_vars_ddt="${required_vars_ddt},horizontal_loop_end" -required_vars_ddt="${required_vars_ddt},model_times" -required_vars_ddt="${required_vars_ddt},number_of_model_times" -required_vars_ddt="${required_vars_ddt},surface_air_pressure" -input_vars_ddt="horizontal_dimension" -input_vars_ddt="${input_vars_ddt},horizontal_loop_begin" -input_vars_ddt="${input_vars_ddt},horizontal_loop_end" -input_vars_ddt="${input_vars_ddt},model_times,number_of_model_times" -input_vars_ddt="${input_vars_ddt},surface_air_pressure" -output_vars_ddt="ccpp_error_code,ccpp_error_message" -output_vars_ddt="${output_vars_ddt},model_times,number_of_model_times,surface_air_pressure" -required_vars_temp="array_variable_for_testing" -required_vars_temp="${required_vars_temp},ccpp_error_code,ccpp_error_message" -required_vars_temp="${required_vars_temp},coefficients_for_interpolation" -required_vars_temp="${required_vars_temp},configuration_variable" -required_vars_temp="${required_vars_temp},horizontal_dimension" -required_vars_temp="${required_vars_temp},horizontal_loop_begin" -required_vars_temp="${required_vars_temp},horizontal_loop_end" -required_vars_temp="${required_vars_temp},index_of_water_vapor_specific_humidity" -required_vars_temp="${required_vars_temp},lower_bound_of_vertical_dimension_of_soil" -required_vars_temp="${required_vars_temp},number_of_tracers" -required_vars_temp="${required_vars_temp},potential_temperature" -required_vars_temp="${required_vars_temp},potential_temperature_at_interface" -required_vars_temp="${required_vars_temp},potential_temperature_increment" -required_vars_temp="${required_vars_temp},soil_levels" -required_vars_temp="${required_vars_temp},surface_air_pressure" -required_vars_temp="${required_vars_temp},temperature_at_diagnostic_levels" -required_vars_temp="${required_vars_temp},time_step_for_physics" -required_vars_temp="${required_vars_temp},upper_bound_of_vertical_dimension_of_soil" -required_vars_temp="${required_vars_temp},vertical_interface_dimension" -required_vars_temp="${required_vars_temp},vertical_layer_dimension" -required_vars_temp="${required_vars_temp},water_vapor_specific_humidity" -input_vars_temp="array_variable_for_testing" -input_vars_temp="${input_vars_temp},coefficients_for_interpolation" -input_vars_temp="${input_vars_temp},configuration_variable" -input_vars_temp="${input_vars_temp},horizontal_dimension" -input_vars_temp="${input_vars_temp},horizontal_loop_begin" -input_vars_temp="${input_vars_temp},horizontal_loop_end" -input_vars_temp="${input_vars_temp},index_of_water_vapor_specific_humidity" -input_vars_temp="${input_vars_temp},lower_bound_of_vertical_dimension_of_soil" -input_vars_temp="${input_vars_temp},number_of_tracers" -input_vars_temp="${input_vars_temp},potential_temperature" -input_vars_temp="${input_vars_temp},potential_temperature_at_interface" -input_vars_temp="${input_vars_temp},potential_temperature_increment" -input_vars_temp="${input_vars_temp},soil_levels" -input_vars_temp="${input_vars_temp},surface_air_pressure" -input_vars_temp="${input_vars_temp},temperature_at_diagnostic_levels" -input_vars_temp="${input_vars_temp},time_step_for_physics" -input_vars_temp="${input_vars_temp},upper_bound_of_vertical_dimension_of_soil" -input_vars_temp="${input_vars_temp},vertical_interface_dimension" -input_vars_temp="${input_vars_temp},vertical_layer_dimension" -input_vars_temp="${input_vars_temp},water_vapor_specific_humidity" -output_vars_temp="array_variable_for_testing" -output_vars_temp="${output_vars_temp},ccpp_error_code,ccpp_error_message" -output_vars_temp="${output_vars_temp},coefficients_for_interpolation" -output_vars_temp="${output_vars_temp},potential_temperature" -output_vars_temp="${output_vars_temp},potential_temperature_at_interface" -output_vars_temp="${output_vars_temp},soil_levels" -output_vars_temp="${output_vars_temp},surface_air_pressure" -output_vars_temp="${output_vars_temp},temperature_at_diagnostic_levels" -output_vars_temp="${output_vars_temp},water_vapor_specific_humidity" - -## -## Run a database report and check the return string -## $1 is the report program file -## $2 is the database file -## $3 is the report string -## $4 is the check string -## $5+ are any optional arguments -## -check_datatable() { - local checkstr=${4} - local teststr - local prog=${1} - local database=${2} - local report=${3} - shift 4 - echo "Checking ${report} report" - teststr="`${prog} ${database} ${report} $@`" - if [ "${teststr}" != "${checkstr}" ]; then - perr "datatable check:\nExpected: '${checkstr}'\nGot: '${teststr}'" - fi -} - -# cd to the build directory -cd ${build_dir} -res=$? -if [ $res -ne 0 ]; then - perr "Unable to cd to build directory, '${build_dir}'" -fi -# Clean build directory -rm -rf * -res=$? -if [ $res -ne 0 ]; then - perr "Unable to clean build directory, '${build_dir}'" -fi -# Run CMake -opts="" -if [ $verbosity -gt 0 ]; then - opts="${opts} -DVERBOSITY=${verbosity}" -fi -# Run cmake -cmake ${scriptdir} ${opts} -res=$? -if [ $res -ne 0 ]; then - perr "CMake failed with exit code, ${res}" -fi -# Test the datafile user interface -report_prog="${framework}/scripts/ccpp_datafile.py" -datafile="${build_dir}/ccpp/datatable.xml" -echo "Running python interface tests" -python3 ${scriptdir}/test_reports.py ${build_dir} ${datafile} -res=$? -if [ $res -ne 0 ]; then - perr "python interface tests failed" -fi -echo "Running command line tests" -echo "Checking required files from command line:" -check_datatable ${report_prog} ${datafile} "--host-files" ${host_files} -check_datatable ${report_prog} ${datafile} "--suite-files" ${suite_files} -check_datatable ${report_prog} ${datafile} "--utility-files" ${utility_files} -check_datatable ${report_prog} ${datafile} "--ccpp-files" ${ccpp_files} -echo -e "\nChecking lists from command line" -check_datatable ${report_prog} ${datafile} "--process-list" ${process_list} -check_datatable ${report_prog} ${datafile} "--module-list" ${module_list} -check_datatable ${report_prog} ${datafile} "--dependencies" ${dependencies} -check_datatable ${report_prog} ${datafile} "--suite-list" ${suite_list} \ - --sep ";" -echo -e "\nChecking variables for DDT suite from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars_ddt} "ddt_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars_ddt} "ddt_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars_ddt} "ddt_suite" -echo -e "\nChecking variables for temp suite from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars_temp} "temp_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars_temp} "temp_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars_temp} "temp_suite" -# Run make -make -res=$? -if [ $res -ne 0 ]; then - perr "make failed with exit code, ${res}" -fi -# Run test -./test_host -res=$? -if [ $res -ne 0 ]; then - perr "test_host failed with exit code, ${res}" -fi - -if [ "${cleanup}" == "ALWAYS" ]; then - docleanup -elif [ $res -eq 0 -a "${cleanup}" == "PASS" ]; then - docleanup -fi - -exit $res diff --git a/test/capgen_test/temp_scheme_files.txt b/test/capgen_test/temp_scheme_files.txt deleted file mode 100644 index 6c831539..00000000 --- a/test/capgen_test/temp_scheme_files.txt +++ /dev/null @@ -1,4 +0,0 @@ -setup_coeffs.meta -temp_set.meta -temp_adjust.meta -temp_calc_adjust.meta diff --git a/test/capgen_test/test_capgen_host_integration.F90 b/test/capgen_test/test_capgen_host_integration.F90 new file mode 100644 index 00000000..745e5678 --- /dev/null +++ b/test/capgen_test/test_capgen_host_integration.F90 @@ -0,0 +1,86 @@ +program test + use test_prog, only: test_host, suite_info, cm, cs + + implicit none + + character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & + 'physics2 ' /) + character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) + character(len=cm), target :: test_invars1(10) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'potential_temperature_increment ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & + 'time_step_for_physics ', & + 'array_variable_for_testing ' /) + character(len=cm), target :: test_outvars1(10) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'array_variable_for_testing ' /) + character(len=cm), target :: test_reqvars1(12) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'potential_temperature_increment ', & + 'time_step_for_physics ', & + 'soil_levels ', & + 'temperature_at_diagnostic_levels ', & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'array_variable_for_testing ' /) + + character(len=cm), target :: test_invars2(3) = (/ & + 'model_times ', & + 'number_of_model_times ', & + 'surface_air_pressure ' /) + + character(len=cm), target :: test_outvars2(5) = (/ & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'model_times ', & + 'surface_air_pressure ', & + 'number_of_model_times ' /) + + character(len=cm), target :: test_reqvars2(5) = (/ & + 'model_times ', & + 'number_of_model_times ', & + 'surface_air_pressure ', & + 'ccpp_error_code ', & + 'ccpp_error_message ' /) + type(suite_info) :: test_suites(2) + logical :: run_okay + + ! Setup expected test suite info + test_suites(1)%suite_name = 'temp_suite' + test_suites(1)%suite_parts => test_parts1 + test_suites(1)%suite_input_vars => test_invars1 + test_suites(1)%suite_output_vars => test_outvars1 + test_suites(1)%suite_required_vars => test_reqvars1 + test_suites(2)%suite_name = 'ddt_suite' + test_suites(2)%suite_parts => test_parts2 + test_suites(2)%suite_input_vars => test_invars2 + test_suites(2)%suite_output_vars => test_outvars2 + test_suites(2)%suite_required_vars => test_reqvars2 + + call test_host(run_okay, test_suites) + + if (run_okay) then + STOP 0 + else + STOP -1 + end if + +end program test diff --git a/test/capgen_test/test_host.F90 b/test/capgen_test/test_host.F90 index fd31b145..9ed3f47c 100644 --- a/test/capgen_test/test_host.F90 +++ b/test/capgen_test/test_host.F90 @@ -24,92 +24,10 @@ module test_prog CONTAINS - logical function check_list(test_list, chk_list, list_desc, suite_name) - ! Check a list () against its expected value () - - ! Dummy arguments - character(len=*), intent(in) :: test_list(:) - character(len=*), intent(in) :: chk_list(:) - character(len=*), intent(in) :: list_desc - character(len=*), optional, intent(in) :: suite_name - - ! Local variables - logical :: found - integer :: num_items - integer :: lindex, tindex - integer, allocatable :: check_unique(:) - character(len=2) :: sep - character(len=256) :: errmsg - - check_list = .true. - errmsg = '' - - ! Check the list size - num_items = size(chk_list) - if (size(test_list) /= num_items) then - write(errmsg, '(a,i0,2a)') 'ERROR: Found ', size(test_list), & - ' ', trim(list_desc) - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' for suite, ', & - trim(suite_name) - end if - write(errmsg(len_trim(errmsg)+1:), '(a,i0)') ', should be ', num_items - write(6, *) trim(errmsg) - errmsg = '' - check_list = .false. - end if - - ! Now, check the list contents for 1-1 correspondence - if (check_list) then - allocate(check_unique(num_items)) - check_unique = -1 - do lindex = 1, num_items - found = .false. - do tindex = 1, num_items - if (trim(test_list(lindex)) == trim(chk_list(tindex))) then - check_unique(tindex) = lindex - found = .true. - exit - end if - end do - if (.not. found) then - check_list = .false. - write(errmsg, '(5a)') 'ERROR: ', trim(list_desc), ' item, ', & - trim(test_list(lindex)), ', was not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - write(6, *) trim(errmsg) - errmsg = '' - end if - end do - if (check_list .and. ANY(check_unique < 0)) then - check_list = .false. - write(errmsg, '(3a)') 'ERROR: The following ', trim(list_desc), & - ' items were not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - sep = '; ' - do lindex = 1, num_items - if (check_unique(lindex) < 0) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') sep, & - trim(chk_list(lindex)) - sep = ', ' - end if - end do - write(6, *) trim(errmsg) - errmsg = '' - end if - end if - - end function check_list - logical function check_suite(test_suite) use test_host_ccpp_cap, only: ccpp_physics_suite_part_list use test_host_ccpp_cap, only: ccpp_physics_suite_variables + use test_utils, only: check_list ! Dummy argument type(suite_info), intent(in) :: test_suite @@ -195,6 +113,7 @@ subroutine test_host(retval, test_suites) use test_host_ccpp_cap, only: test_host_ccpp_physics_finalize use test_host_ccpp_cap, only: ccpp_physics_suite_list use test_host_mod, only: init_data, compare_data, check_model_times + use test_utils, only: check_list type(suite_info), intent(in) :: test_suites(:) logical, intent(out) :: retval @@ -359,90 +278,3 @@ subroutine test_host(retval, test_suites) end subroutine test_host end module test_prog - - program test - use test_prog, only: test_host, suite_info, cm, cs - - implicit none - - character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & - 'physics2 ' /) - character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) - character(len=cm), target :: test_invars1(10) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'potential_temperature_increment ', & - 'soil_levels ', & - 'temperature_at_diagnostic_levels ', & - 'time_step_for_physics ', & - 'array_variable_for_testing ' /) - character(len=cm), target :: test_outvars1(10) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'soil_levels ', & - 'temperature_at_diagnostic_levels ', & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'array_variable_for_testing ' /) - character(len=cm), target :: test_reqvars1(12) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'potential_temperature_increment ', & - 'time_step_for_physics ', & - 'soil_levels ', & - 'temperature_at_diagnostic_levels ', & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'array_variable_for_testing ' /) - - character(len=cm), target :: test_invars2(3) = (/ & - 'model_times ', & - 'number_of_model_times ', & - 'surface_air_pressure ' /) - - character(len=cm), target :: test_outvars2(5) = (/ & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'model_times ', & - 'surface_air_pressure ', & - 'number_of_model_times ' /) - - character(len=cm), target :: test_reqvars2(5) = (/ & - 'model_times ', & - 'number_of_model_times ', & - 'surface_air_pressure ', & - 'ccpp_error_code ', & - 'ccpp_error_message ' /) - type(suite_info) :: test_suites(2) - logical :: run_okay - - ! Setup expected test suite info - test_suites(1)%suite_name = 'temp_suite' - test_suites(1)%suite_parts => test_parts1 - test_suites(1)%suite_input_vars => test_invars1 - test_suites(1)%suite_output_vars => test_outvars1 - test_suites(1)%suite_required_vars => test_reqvars1 - test_suites(2)%suite_name = 'ddt_suite' - test_suites(2)%suite_parts => test_parts2 - test_suites(2)%suite_input_vars => test_invars2 - test_suites(2)%suite_output_vars => test_outvars2 - test_suites(2)%suite_required_vars => test_reqvars2 - - call test_host(run_okay, test_suites) - - if (run_okay) then - STOP 0 - else - STOP -1 - end if - -end program test diff --git a/test/capgen_test/test_reports.py b/test/capgen_test/test_reports.py deleted file mode 100644 index fb38a4dc..00000000 --- a/test/capgen_test/test_reports.py +++ /dev/null @@ -1,210 +0,0 @@ -#! /usr/bin/env python3 -""" ------------------------------------------------------------------------ - Description: Test capgen database report python interface - - Assumptions: - - Command line arguments: build_dir database_filepath - - Usage: python test_reports ------------------------------------------------------------------------ -""" -import sys -import os - -_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) -_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") -_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") - -if not os.path.exists(_SCRIPTS_DIR): - raise ImportError("Cannot find scripts directory") -# end if - -if ((sys.version_info[0] < 3) or - (sys.version_info[0] == 3) and (sys.version_info[1] < 8)): - raise Exception("Python 3.8 or greater required") -# end if - -sys.path.append(_SCRIPTS_DIR) -# pylint: disable=wrong-import-position -from ccpp_datafile import datatable_report, DatatableReport -# pylint: enable=wrong-import-position - -def usage(errmsg=None): - """Raise an exception with optional error message and usage message""" - emsg = "usage: {} " - if errmsg: - emsg = errmsg + '\n' + emsg - # end if - raise ValueError(emsg.format(sys.argv[0])) - -if len(sys.argv) != 3: - usage() -# end if - -_BUILD_DIR = os.path.abspath(sys.argv[1]) -_DATABASE = os.path.abspath(sys.argv[2]) -if not os.path.isdir(_BUILD_DIR): - _EMSG = " must be an existing build directory" - usage(_EMSG) -# end if -if (not os.path.exists(_DATABASE)) or (not os.path.isfile(_DATABASE)): - _EMSG = " must be an existing CCPP database file" - usage(_EMSG) -# end if - -# Check data -_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] -_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] -_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), - os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), - os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), - os.path.join(_SRC_DIR, "ccpp_hashable.F90"), - os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] -_CCPP_FILES = _UTILITY_FILES + \ - [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] -_PROCESS_LIST = ["setter=temp_set", "adjusting=temp_calc_adjust"] -_MODULE_LIST = ["environ_conditions", "make_ddt", "setup_coeffs", "temp_adjust", - "temp_calc_adjust", "temp_set"] -_SUITE_LIST = ["ddt_suite", "temp_suite"] -_INPUT_VARS_DDT = ["model_times", "number_of_model_times", - "horizontal_loop_begin", "horizontal_loop_end", - "surface_air_pressure", "horizontal_dimension"] -_OUTPUT_VARS_DDT = ["ccpp_error_code", "ccpp_error_message", "model_times", - "surface_air_pressure", "number_of_model_times"] -_REQUIRED_VARS_DDT = _INPUT_VARS_DDT + _OUTPUT_VARS_DDT -_PROT_VARS_TEMP = ["horizontal_loop_begin", "horizontal_loop_end", - "horizontal_dimension", "vertical_layer_dimension", - "number_of_tracers", - "lower_bound_of_vertical_dimension_of_soil", - "upper_bound_of_vertical_dimension_of_soil", - "configuration_variable", - # Added for --debug - "index_of_water_vapor_specific_humidity", - "vertical_interface_dimension"] -_REQUIRED_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", - "potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "potential_temperature_increment", - "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity", - "soil_levels", - "temperature_at_diagnostic_levels", - "array_variable_for_testing"] -_INPUT_VARS_TEMP = ["potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "potential_temperature_increment", - "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity", - "soil_levels", - "temperature_at_diagnostic_levels", - "array_variable_for_testing"] -_OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", - "potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "surface_air_pressure", "water_vapor_specific_humidity", - "soil_levels", - "temperature_at_diagnostic_levels", - "array_variable_for_testing"] - -def fields_string(field_type, field_list, sep): - """Create an error string for field(s), . - is used to separate items in """ - indent = ' '*11 - if field_list: - if len(field_list) > 1: - field_str = "{} Fields: ".format(field_type) - else: - field_str = "{} Field: ".format(field_type) - # end if - fmsg = "\n{}{}{}".format(indent, field_str, sep.join(field_list)) - else: - fmsg = "" - # end if - return fmsg - -def check_datatable(database, report_type, check_list, - sep=',', exclude_protected=False): - """Run a database report and check the return string. - If an error is found, print an error message. - Return the number of errors""" - if sep is None: - sep = ',' - # end if - test_str = datatable_report(database, report_type, sep, exclude_protected=exclude_protected) - test_list = [x for x in test_str.split(sep) if x] - missing = list() - unexpected = list() - for item in check_list: - if item not in test_list: - missing.append(item) - # end if - # end for - for item in test_list: - if item not in check_list: - unexpected.append(item) - # end if - # end for - if missing or unexpected: - vmsg = "ERROR in {} datafile check:".format(report_type.action) - vmsg += fields_string("Missing", missing, sep) - vmsg += fields_string("Unexpected", unexpected, sep) - print(vmsg) - else: - print("{} report okay".format(report_type.action)) - # end if - return len(missing) + len(unexpected) - -NUM_ERRORS = 0 -print("Checking required files from python:") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("host_files"), - _HOST_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_files"), - _SUITE_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("utility_files"), - _UTILITY_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("ccpp_files"), - _CCPP_FILES) -print("\nChecking lists from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("process_list"), - _PROCESS_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("module_list"), - _MODULE_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_list"), - _SUITE_LIST) -print("\nChecking variables for DDT suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="ddt_suite"), - _REQUIRED_VARS_DDT) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="ddt_suite"), - _INPUT_VARS_DDT) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="ddt_suite"), - _OUTPUT_VARS_DDT) -print("\nChecking variables for temp suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="temp_suite"), - _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="temp_suite"), - _REQUIRED_VARS_TEMP, exclude_protected=True) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="temp_suite"), - _INPUT_VARS_TEMP + _PROT_VARS_TEMP) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="temp_suite"), - _INPUT_VARS_TEMP, exclude_protected=True) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="temp_suite"), - _OUTPUT_VARS_TEMP) - -sys.exit(NUM_ERRORS) diff --git a/test/ddthost_test/CMakeLists.txt b/test/ddthost_test/CMakeLists.txt index 5bbe196d..cc257619 100644 --- a/test/ddthost_test/CMakeLists.txt +++ b/test/ddthost_test/CMakeLists.txt @@ -1,191 +1,54 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.15) -PROJECT(test_host) -ENABLE_LANGUAGE(Fortran) - -include(CMakeForceCompiler) - -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) -#------------------------------------------------------------------------------ -# -# Set where the CCPP Framework lives -# -#------------------------------------------------------------------------------ -get_filename_component(TEST_ROOT "${CMAKE_SOURCE_DIR}" DIRECTORY) -get_filename_component(CCPP_ROOT "${TEST_ROOT}" DIRECTORY) #------------------------------------------------------------------------------ # # Create list of SCHEME_FILES, HOST_FILES, and SUITE_FILES # Paths should be relative to CMAKE_SOURCE_DIR (this file's directory) # #------------------------------------------------------------------------------ -LIST(APPEND SCHEME_FILES "temp_scheme_files.txt" "ddt_suite_files.txt") -LIST(APPEND HOST_FILES "test_host_data" "test_host_mod" "host_ccpp_ddt") -LIST(APPEND SUITE_FILES "ddt_suite.xml" "temp_suite.xml") +set(SCHEME_FILES "setup_coeffs" "temp_set" "temp_adjust" "temp_calc_adjust") +set(SUITE_SCHEME_FILES "make_ddt" "environ_conditions") +set(HOST_FILES "test_host_data" "test_host_mod" "host_ccpp_ddt") +set(SUITE_FILES "ddt_suite.xml" "temp_suite.xml") # HOST is the name of the executable we will build. # We assume there are files ${HOST}.meta and ${HOST}.F90 in CMAKE_SOURCE_DIR -SET(HOST "${CMAKE_PROJECT_NAME}") +set(HOST "test_host") -#------------------------------------------------------------------------------ -# -# End of project-specific input -# -#------------------------------------------------------------------------------ - -# By default, no verbose output -SET(VERBOSITY 0 CACHE STRING "Verbosity level of output (default: 0)") # By default, generated caps go in ccpp subdir -SET(CCPP_CAP_FILES "${CMAKE_BINARY_DIR}/ccpp" CACHE - STRING "Location of CCPP-generated cap files") - -SET(CCPP_FRAMEWORK ${CCPP_ROOT}/scripts) - -# Use rpaths on MacOSX -set(CMAKE_MACOSX_RPATH 1) +set(CCPP_CAP_FILES "${CMAKE_CURRENT_BINARY_DIR}/ccpp") -#------------------------------------------------------------------------------ -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - #message(STATUS "Setting build type to 'Debug' as none was specified.") - #set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) - message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -ADD_COMPILE_OPTIONS(-O0) - -if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") -# gfortran -# MESSAGE("gfortran being used.") - ADD_COMPILE_OPTIONS(-fcheck=all) - ADD_COMPILE_OPTIONS(-fbacktrace) - ADD_COMPILE_OPTIONS(-ffpe-trap=zero) - ADD_COMPILE_OPTIONS(-finit-real=nan) - ADD_COMPILE_OPTIONS(-ggdb) - ADD_COMPILE_OPTIONS(-ffree-line-length-none) - ADD_COMPILE_OPTIONS(-cpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") -# ifort -# MESSAGE("ifort being used.") - #ADD_COMPILE_OPTIONS(-check all) - ADD_COMPILE_OPTIONS(-fpe0) - ADD_COMPILE_OPTIONS(-warn) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-debug extended) - ADD_COMPILE_OPTIONS(-fpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "PGI") -# pgf90 -# MESSAGE("pgf90 being used.") - ADD_COMPILE_OPTIONS(-g) - ADD_COMPILE_OPTIONS(-Mipa=noconst) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-Mfree) - ADD_COMPILE_OPTIONS(-Mfptrap) - ADD_COMPILE_OPTIONS(-Mpreprocess) -else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - message (WARNING "This program has only been compiled with gfortran, pgf90 and ifort. If another compiler is needed, the appropriate flags SHOULD be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt") -endif (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - -#------------------------------------------------------------------------------ -# CMake Modules -# Set the CMake module path -list(APPEND CMAKE_MODULE_PATH "${CCPP_FRAMEWORK}/cmake") -#------------------------------------------------------------------------------ -# Set OpenMP flags for C/C++/Fortran -if (OPENMP) - include(detect_openmp) - detect_openmp() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - message(STATUS "Enable OpenMP support for C/C++/Fortran compiler") -else(OPENMP) - message (STATUS "Disable OpenMP support for C/C++/Fortran compiler") -endif() +# Create lists for Fortran and meta data files from file names +list(TRANSFORM SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SCHEME_FORTRAN_FILES) +list(TRANSFORM SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SCHEME_META_FILES) +list(TRANSFORM SUITE_SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SUITE_SCHEME_FORTRAN_FILES) +list(TRANSFORM SUITE_SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SUITE_SCHEME_META_FILES) +list(TRANSFORM HOST_FILES APPEND ".F90" OUTPUT_VARIABLE DDT_HOST_FORTRAN_FILES) +list(TRANSFORM HOST_FILES APPEND ".meta" OUTPUT_VARIABLE DDT_HOST_METADATA_FILES) -# Create metadata and source file lists -FOREACH(FILE ${SCHEME_FILES}) - FILE(STRINGS ${FILE} FILENAMES) - LIST(APPEND SCHEME_FILENAMES ${FILENAMES}) -ENDFOREACH(FILE) -string(REPLACE ";" "," SCHEME_METADATA "${SCHEME_FILES}") - -FOREACH(FILE ${SCHEME_FILENAMES}) - # target_sources prefers absolute pathnames - string(REPLACE ".meta" ".F90" TEMP "${FILE}") - get_filename_component(ABS_PATH "${TEMP}" ABSOLUTE) - list(APPEND LIBRARY_LIST ${ABS_PATH}) -ENDFOREACH(FILE) - -FOREACH(FILE ${HOST_FILES}) - LIST(APPEND HOST_METADATA "${FILE}.meta") - # target_sources prefers absolute pathnames - get_filename_component(ABS_PATH "${FILE}.F90" ABSOLUTE) - LIST(APPEND HOST_SOURCE "${ABS_PATH}") -ENDFOREACH(FILE) -list(APPEND LIBRARY_LIST ${HOST_SOURCE}) -string(REPLACE ";" ".meta," HOST_METADATA "${HOST_FILES}") -set(HOST_METADATA "${HOST_METADATA}.meta,${HOST}.meta") - -string(REPLACE ";" "," SUITE_XML "${SUITE_FILES}") +list(APPEND DDT_HOST_METADATA_FILES "${HOST}.meta") # Run ccpp_capgen -set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py") -list(APPEND CAPGEN_CMD "--host-files") -list(APPEND CAPGEN_CMD "${HOST_METADATA}") -list(APPEND CAPGEN_CMD "--scheme-files") -list(APPEND CAPGEN_CMD "${SCHEME_METADATA}") -list(APPEND CAPGEN_CMD "--suites") -list(APPEND CAPGEN_CMD "${SUITE_XML}") -list(APPEND CAPGEN_CMD "--host-name") -list(APPEND CAPGEN_CMD "test_host") -list(APPEND CAPGEN_CMD "--output-root") -list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}") -string(REPEAT "--verbose;" ${VERBOSITY} VERBOSE_REPEATED) -list(APPEND CAPGEN_CMD ${VERBOSE_REPEATED}) -list(APPEND CAPGEN_CMD "--debug") -string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}") -MESSAGE(STATUS "Running: ${CAPGEN_STRING}") -EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE CAPGEN_OUT - ERROR_VARIABLE CAPGEN_OUT - RESULT_VARIABLE RES) -MESSAGE(STATUS "${CAPGEN_OUT}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap generation completed") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}") -endif(RES EQUAL 0) - -# Retrieve the list of files from datatable.xml and set to CCPP_CAPS -set(DTABLE_CMD "${CCPP_FRAMEWORK}/ccpp_datafile.py") -list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml") -list(APPEND DTABLE_CMD "--ccpp-files") -list(APPEND DTABLE_CMD "--separator=\\;") -string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}") -MESSAGE(STATUS "Running: ${DTABLE_STRING}") -EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} - OUTPUT_VARIABLE CCPP_CAPS - RESULT_VARIABLE RES - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE) -message(STATUS "CCPP_CAPS = ${CCPP_CAPS}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap files retrieved") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}") -endif(RES EQUAL 0) -list(APPEND LIBRARY_LIST ${CCPP_CAPS}) -add_library(TESTLIB OBJECT ${LIBRARY_LIST}) -ADD_EXECUTABLE(${HOST} ${HOST}.F90 $) - -INCLUDE_DIRECTORIES(${CCPP_CAP_FILES}) - -set_target_properties(${HOST} PROPERTIES - COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}" - LINK_FLAGS "${CMAKE_Fortran_FLAGS}") +ccpp_capgen(CAPGEN_DEBUG ON + VERBOSITY ${CCPP_VERBOSITY} + HOSTFILES ${DDT_HOST_METADATA_FILES} + SCHEMEFILES ${SCHEME_META_FILES} ${SUITE_SCHEME_META_FILES} + SUITES ${SUITE_FILES} + HOST_NAME ${HOST} + OUTPUT_ROOT "${CCPP_CAP_FILES}") + +# Retrieve the list of Fortran files required for test host from datatable.xml and set to CCPP_CAPS_LIST +ccpp_datafile(DATATABLE "${CCPP_CAP_FILES}/datatable.xml" + REPORT_NAME "--ccpp-files") + +# Create test host library +add_library(DDT_TESTLIB OBJECT ${SCHEME_FORTRAN_FILES} + ${SUITE_SCHEME_FORTRAN_FILES} + ${DDT_HOST_FORTRAN_FILES} + ${CCPP_CAPS_LIST}) + +# Setup test executable with needed dependencies +add_executable(ddt_host_integration test_ddt_host_integration.F90 ${HOST}.F90) +target_link_libraries(ddt_host_integration PRIVATE DDT_TESTLIB test_utils) +target_include_directories(ddt_host_integration PRIVATE "$") + +# Add executable to be called with ctest +add_test(NAME ctest_ddt_host_integration COMMAND ddt_host_integration) diff --git a/test/ddthost_test/README.md b/test/ddthost_test/README.md index 127544e0..292722dc 100644 --- a/test/ddthost_test/README.md +++ b/test/ddthost_test/README.md @@ -1,6 +1,16 @@ -ccpp_capgen test -=========== +# DDT Host Test -To build and run the ccpp_capgen test, run ./run_test -This script will build and run the test. -The exit code is zero (0) on PASS and non-zero on FAIL. +Contains tests to exercise more DDT functionality: +- Passing around and modifying a DDT +- Making DDT in host model & using it in CCPP-ized physics code + +## Building/Running + +To explicitly build/run the ddt test host, run: + +```bash +$ cmake -S -B -DCCPP_RUN_DDT_HOST_TEST=ON +$ cd +$ make +$ ctest +``` diff --git a/test/ddthost_test/ddt_suite_files.txt b/test/ddthost_test/ddt_suite_files.txt deleted file mode 100644 index 7f96a84c..00000000 --- a/test/ddthost_test/ddt_suite_files.txt +++ /dev/null @@ -1,2 +0,0 @@ -make_ddt.meta -environ_conditions.meta diff --git a/test/ddthost_test/ddthost_test_reports.py b/test/ddthost_test/ddthost_test_reports.py new file mode 100644 index 00000000..612cbbbf --- /dev/null +++ b/test/ddthost_test/ddthost_test_reports.py @@ -0,0 +1,139 @@ +#! /usr/bin/env python3 +""" +----------------------------------------------------------------------- + Description: Test DDT host database report python interface + + Assumptions: + + Command line arguments: build_dir database_filepath + + Usage: python test_reports +----------------------------------------------------------------------- +""" +import os +import unittest + +from test_stub import BaseTests + +_BUILD_DIR = os.path.join(os.path.abspath(os.environ['BUILD_DIR']), "test", "ddthost_test") +_DATABASE = os.path.abspath(os.path.join(_BUILD_DIR, "ccpp", "datatable.xml")) + +_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) +_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") +_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") + + +# Check data +_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] +_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] +_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), + os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), + os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), + os.path.join(_SRC_DIR, "ccpp_hashable.F90"), + os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] +_CCPP_FILES = _UTILITY_FILES + \ + [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] +_DEPENDENCIES = [os.path.join(_TEST_DIR, "adjust", "qux.F90"), + os.path.join(_TEST_DIR, "bar.F90"), + os.path.join(_TEST_DIR, "foo.F90")] +_PROCESS_LIST = ["setter=temp_set", "adjusting=temp_calc_adjust"] +_MODULE_LIST = ["environ_conditions", "make_ddt", "setup_coeffs", "temp_adjust", + "temp_calc_adjust", "temp_set"] +_SUITE_LIST = ["ddt_suite", "temp_suite"] +_INPUT_VARS_DDT = ["model_times", "number_of_model_times", + "horizontal_loop_begin", "horizontal_loop_end", + "surface_air_pressure", "horizontal_dimension", + "host_standard_ccpp_type"] +_OUTPUT_VARS_DDT = ["ccpp_error_code", "ccpp_error_message", "model_times", + "number_of_model_times", "surface_air_pressure"] +_REQUIRED_VARS_DDT = _INPUT_VARS_DDT + _OUTPUT_VARS_DDT +_PROT_VARS_TEMP = ["horizontal_loop_begin", "horizontal_loop_end", + "horizontal_dimension", "vertical_layer_dimension", + "number_of_tracers", + # Added for --debug + "index_of_water_vapor_specific_humidity", + "vertical_interface_dimension"] +_REQUIRED_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", + "potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "potential_temperature_increment", + "surface_air_pressure", "time_step_for_physics", + "water_vapor_specific_humidity"] +_INPUT_VARS_TEMP = ["potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "potential_temperature_increment", + "surface_air_pressure", "time_step_for_physics", + "water_vapor_specific_humidity"] +_OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", + "potential_temperature", + "potential_temperature_at_interface", + "coefficients_for_interpolation", + "surface_air_pressure", "water_vapor_specific_humidity"] + +class TestDdtHostDataTables(unittest.TestCase, BaseTests.TestHostDataTables): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + + +class CommandLineDdtHostDatafileRequiredFiles(unittest.TestCase, BaseTests.TestHostCommandLineDataFiles): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestDdtSuite(unittest.TestCase, BaseTests.TestSuite): + database = _DATABASE + required_vars = _REQUIRED_VARS_DDT + input_vars = _INPUT_VARS_DDT + output_vars = _OUTPUT_VARS_DDT + suite_name = "ddt_suite" + + +class CommandLineDdtSuite(unittest.TestCase, BaseTests.TestSuiteCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_DDT + input_vars = _INPUT_VARS_DDT + output_vars = _OUTPUT_VARS_DDT + suite_name = "ddt_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestDdtTempSuite(unittest.TestCase, BaseTests.TestSuiteExcludeProtected): + database = _DATABASE + required_vars = _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP + input_vars = _INPUT_VARS_TEMP + _PROT_VARS_TEMP + required_vars_excluding_protected = _REQUIRED_VARS_TEMP + input_vars_excluding_protected = _INPUT_VARS_TEMP + output_vars = _OUTPUT_VARS_TEMP + suite_name = "temp_suite" + + +class CommandLineDdtTempSuite(unittest.TestCase, BaseTests.TestSuiteExcludeProtectedCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP + input_vars = _INPUT_VARS_TEMP + _PROT_VARS_TEMP + required_vars_excluding_protected = _REQUIRED_VARS_TEMP + input_vars_excluding_protected = _INPUT_VARS_TEMP + output_vars = _OUTPUT_VARS_TEMP + suite_name = "temp_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" diff --git a/test/ddthost_test/run_test b/test/ddthost_test/run_test deleted file mode 100755 index 964bface..00000000 --- a/test/ddthost_test/run_test +++ /dev/null @@ -1,285 +0,0 @@ -#! /bin/bash - -currdir="`pwd -P`" -scriptdir="$( cd $( dirname $0 ); pwd -P )" - -## -## Option default values -## -defdir="ddt_build" -build_dir="${currdir}/${defdir}" -cleanup="PASS" # Other supported options are ALWAYS and NEVER -verbosity=2 - -## -## General syntax help function -## Usage: help -## -help () { - local hname="Usage: `basename ${0}`" - local hprefix="`echo ${hname} | tr '[!-~]' ' '`" - echo "${hname} [ --build-dir ] [ --cleanup ]" - echo "${hprefix} [ --verbosity <#> ]" - hprefix=" " - echo "" - echo "${hprefix} : Directory for building and running the test" - echo "${hprefix} default is /${defdir}" - echo "${hprefix} : Cleanup option is ALWAYS, NEVER, or PASS" - echo "${hprefix} default is PASS" - echo "${hprefix} verbosity: 0, 1, or 2 (default=${verbosity})" - echo "${hprefix} default is 0" - exit $1 -} - -## -## Error output function (should be handed a string) -## -perr() { - >&2 echo -e "\nERROR: ${@}\n" - exit 1 -} - -## -## Cleanup the build and test directory -## -docleanup() { - # We start off in the build directory - if [ "${build_dir}" == "${currdir}" ]; then - echo "WARNING: Cannot clean ${build_dir}" - else - cd ${currdir} - rm -rf ${build_dir} - fi -} - -## Process our input arguments -while [ $# -gt 0 ]; do - case $1 in - --h | -h | --help | -help) - help 0 - ;; - --build-dir) - if [ $# -lt 2 ]; then - perr "${1} requires a build directory" - fi - build_dir="${2}" - shift - ;; - --cleanup) - if [ $# -lt 2 ]; then - perr "${1} requies a cleanup option (ALWAYS, NEVER, PASS)" - fi - if [ "${2}" == "ALWAYS" -o "${2}" == "NEVER" -o "${2}" == "PASS" ]; then - cleanup="${2}" - else - perr "Allowed cleanup options: ALWAYS, NEVER, PASS" - fi - shift - ;; - --verbosity) - if [ $# -lt 2 ]; then - perr "${1} requires a verbosity value (0, 1, or 2)" - fi - if [ "${2}" == "0" -o "${2}" == "1" -o "${2}" == "2" ]; then - verbosity=$2 - else - perr "allowed verbosity levels are 0, 1, 2" - fi - shift - ;; - *) - perr "Unrecognized option, \"${1}\"" - ;; - esac - shift -done - -# Create the build directory, if necessary -if [ -d "${build_dir}" ]; then - # Always make sure build_dir is not in the test dir - if [ "$( cd ${build_dir}; pwd -P )" == "${currdir}" ]; then - build_dir="${build_dir}/${defdir}" - fi -else - mkdir -p ${build_dir} - res=$? - if [ $res -ne 0 ]; then - perr "Unable to create build directory, '${build_dir}'" - fi -fi -build_dir="$( cd ${build_dir}; pwd -P )" - -## framework is the CCPP Framework root dir -framework="$( cd $( dirname $( dirname ${scriptdir} ) ); pwd -P )" -frame_src="${framework}/src" - -## -## check strings for datafile command-list test -## NB: This has to be after build_dir is finalized -## -host_files="${build_dir}/ccpp/test_host_ccpp_cap.F90" -suite_files="${build_dir}/ccpp/ccpp_ddt_suite_cap.F90" -suite_files="${suite_files},${build_dir}/ccpp/ccpp_temp_suite_cap.F90" -utility_files="${build_dir}/ccpp/ccpp_kinds.F90" -utility_files="${utility_files},${frame_src}/ccpp_constituent_prop_mod.F90" -utility_files="${utility_files},${frame_src}/ccpp_scheme_utils.F90" -utility_files="${utility_files},${frame_src}/ccpp_hashable.F90" -utility_files="${utility_files},${frame_src}/ccpp_hash_table.F90" -ccpp_files="${utility_files}" -ccpp_files="${ccpp_files},${build_dir}/ccpp/test_host_ccpp_cap.F90" -ccpp_files="${ccpp_files},${build_dir}/ccpp/ccpp_ddt_suite_cap.F90" -ccpp_files="${ccpp_files},${build_dir}/ccpp/ccpp_temp_suite_cap.F90" -process_list="adjusting=temp_calc_adjust,setter=temp_set" -module_list="environ_conditions,make_ddt,setup_coeffs,temp_adjust,temp_calc_adjust,temp_set" -dependencies="${scriptdir}/adjust/qux.F90,${scriptdir}/bar.F90,${scriptdir}/foo.F90" -suite_list="ddt_suite;temp_suite" -required_vars_ddt="ccpp_error_code,ccpp_error_message,horizontal_dimension" -required_vars_ddt="${required_vars_ddt},horizontal_loop_begin" -required_vars_ddt="${required_vars_ddt},horizontal_loop_end" -required_vars_ddt="${required_vars_ddt},host_standard_ccpp_type" -required_vars_ddt="${required_vars_ddt},model_times" -required_vars_ddt="${required_vars_ddt},number_of_model_times" -required_vars_ddt="${required_vars_ddt},surface_air_pressure" -input_vars_ddt="horizontal_dimension" -input_vars_ddt="${input_vars_ddt},horizontal_loop_begin" -input_vars_ddt="${input_vars_ddt},horizontal_loop_end" -input_vars_ddt="${input_vars_ddt},host_standard_ccpp_type" -input_vars_ddt="${input_vars_ddt},model_times,number_of_model_times" -input_vars_ddt="${input_vars_ddt},surface_air_pressure" -output_vars_ddt="ccpp_error_code,ccpp_error_message" -output_vars_ddt="${output_vars_ddt},model_times,number_of_model_times,surface_air_pressure" -required_vars_temp="ccpp_error_code,ccpp_error_message" -required_vars_temp="${required_vars_temp},coefficients_for_interpolation" -required_vars_temp="${required_vars_temp},horizontal_dimension" -required_vars_temp="${required_vars_temp},horizontal_loop_begin" -required_vars_temp="${required_vars_temp},horizontal_loop_end" -required_vars_temp="${required_vars_temp},index_of_water_vapor_specific_humidity" -required_vars_temp="${required_vars_temp},number_of_tracers" -required_vars_temp="${required_vars_temp},potential_temperature" -required_vars_temp="${required_vars_temp},potential_temperature_at_interface" -required_vars_temp="${required_vars_temp},potential_temperature_increment" -required_vars_temp="${required_vars_temp},surface_air_pressure" -required_vars_temp="${required_vars_temp},time_step_for_physics" -required_vars_temp="${required_vars_temp},vertical_interface_dimension" -required_vars_temp="${required_vars_temp},vertical_layer_dimension" -required_vars_temp="${required_vars_temp},water_vapor_specific_humidity" -input_vars_temp="coefficients_for_interpolation" -input_vars_temp="${input_vars_temp},horizontal_dimension" -input_vars_temp="${input_vars_temp},horizontal_loop_begin" -input_vars_temp="${input_vars_temp},horizontal_loop_end" -input_vars_temp="${input_vars_temp},index_of_water_vapor_specific_humidity" -input_vars_temp="${input_vars_temp},number_of_tracers" -input_vars_temp="${input_vars_temp},potential_temperature" -input_vars_temp="${input_vars_temp},potential_temperature_at_interface" -input_vars_temp="${input_vars_temp},potential_temperature_increment" -input_vars_temp="${input_vars_temp},surface_air_pressure,time_step_for_physics" -input_vars_temp="${input_vars_temp},vertical_interface_dimension" -input_vars_temp="${input_vars_temp},vertical_layer_dimension" -input_vars_temp="${input_vars_temp},water_vapor_specific_humidity" -output_vars_temp="ccpp_error_code,ccpp_error_message" -output_vars_temp="${output_vars_temp},coefficients_for_interpolation" -output_vars_temp="${output_vars_temp},potential_temperature" -output_vars_temp="${output_vars_temp},potential_temperature_at_interface" -output_vars_temp="${output_vars_temp},surface_air_pressure" -output_vars_temp="${output_vars_temp},water_vapor_specific_humidity" - -## -## Run a database report and check the return string -## $1 is the report program file -## $2 is the database file -## $3 is the report string -## $4 is the check string -## $5+ are any optional arguments -## -check_datatable() { - local checkstr=${4} - local teststr - local prog=${1} - local database=${2} - local report=${3} - shift 4 - echo "Checking ${report} report" - teststr="`${prog} ${database} ${report} $@`" - if [ "${teststr}" != "${checkstr}" ]; then - perr "datatable check:\nExpected: '${checkstr}'\nGot: '${teststr}'" - fi -} - -# cd to the build directory -cd ${build_dir} -res=$? -if [ $res -ne 0 ]; then - perr "Unable to cd to build directory, '${build_dir}'" -fi -# Clean build directory -rm -rf * -res=$? -if [ $res -ne 0 ]; then - perr "Unable to clean build directory, '${build_dir}'" -fi -# Run CMake -opts="" -if [ $verbosity -gt 0 ]; then - opts="${opts} -DVERBOSITY=${verbosity}" -fi -# Run cmake -cmake ${scriptdir} ${opts} -res=$? -if [ $res -ne 0 ]; then - perr "CMake failed with exit code, ${res}" -fi -# Test the datafile user interface -report_prog="${framework}/scripts/ccpp_datafile.py" -datafile="${build_dir}/ccpp/datatable.xml" -echo "Running python interface tests" -python3 ${scriptdir}/test_reports.py ${build_dir} ${datafile} -res=$? -if [ $res -ne 0 ]; then - perr "python interface tests failed" -fi -echo "Running command line tests" -echo "Checking required files from command line:" -check_datatable ${report_prog} ${datafile} "--host-files" ${host_files} -check_datatable ${report_prog} ${datafile} "--suite-files" ${suite_files} -check_datatable ${report_prog} ${datafile} "--utility-files" ${utility_files} -check_datatable ${report_prog} ${datafile} "--ccpp-files" ${ccpp_files} -echo -e "\nChecking lists from command line" -check_datatable ${report_prog} ${datafile} "--process-list" ${process_list} -check_datatable ${report_prog} ${datafile} "--module-list" ${module_list} -check_datatable ${report_prog} ${datafile} "--dependencies" ${dependencies} -check_datatable ${report_prog} ${datafile} "--suite-list" ${suite_list} \ - --sep ";" -echo -e "\nChecking variables for DDT suite from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars_ddt} "ddt_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars_ddt} "ddt_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars_ddt} "ddt_suite" -echo -e "\nChecking variables for temp suite from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars_temp} "temp_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars_temp} "temp_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars_temp} "temp_suite" -# Run make -make -res=$? -if [ $res -ne 0 ]; then - perr "make failed with exit code, ${res}" -fi -# Run test -./test_host -res=$? -if [ $res -ne 0 ]; then - perr "test_host failed with exit code, ${res}" -fi - -if [ "${cleanup}" == "ALWAYS" ]; then - docleanup -elif [ $res -eq 0 -a "${cleanup}" == "PASS" ]; then - docleanup -fi - -exit $res diff --git a/test/ddthost_test/temp_scheme_files.txt b/test/ddthost_test/temp_scheme_files.txt deleted file mode 100644 index 6c831539..00000000 --- a/test/ddthost_test/temp_scheme_files.txt +++ /dev/null @@ -1,4 +0,0 @@ -setup_coeffs.meta -temp_set.meta -temp_adjust.meta -temp_calc_adjust.meta diff --git a/test/ddthost_test/test_ddt_host_integration.F90 b/test/ddthost_test/test_ddt_host_integration.F90 new file mode 100644 index 00000000..23a0e53c --- /dev/null +++ b/test/ddthost_test/test_ddt_host_integration.F90 @@ -0,0 +1,79 @@ +program test + use test_prog, only: test_host, suite_info, cm, cs + + implicit none + + character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & + 'physics2 ' /) + character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) + character(len=cm), target :: test_invars1(7) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'potential_temperature_increment ', & + 'time_step_for_physics ' /) + character(len=cm), target :: test_outvars1(7) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'ccpp_error_code ', & + 'ccpp_error_message ' /) + character(len=cm), target :: test_reqvars1(9) = (/ & + 'potential_temperature ', & + 'potential_temperature_at_interface ', & + 'coefficients_for_interpolation ', & + 'surface_air_pressure ', & + 'water_vapor_specific_humidity ', & + 'potential_temperature_increment ', & + 'time_step_for_physics ', & + 'ccpp_error_code ', & + 'ccpp_error_message ' /) + + character(len=cm), target :: test_invars2(4) = (/ & + 'model_times ', & + 'number_of_model_times ', & + 'surface_air_pressure ', & + 'host_standard_ccpp_type ' /) + + character(len=cm), target :: test_outvars2(5) = (/ & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'model_times ', & + 'surface_air_pressure ', & + 'number_of_model_times ' /) + + character(len=cm), target :: test_reqvars2(6) = (/ & + 'model_times ', & + 'number_of_model_times ', & + 'surface_air_pressure ', & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'host_standard_ccpp_type ' /) + type(suite_info) :: test_suites(2) + logical :: run_okay + + ! Setup expected test suite info + test_suites(1)%suite_name = 'temp_suite' + test_suites(1)%suite_parts => test_parts1 + test_suites(1)%suite_input_vars => test_invars1 + test_suites(1)%suite_output_vars => test_outvars1 + test_suites(1)%suite_required_vars => test_reqvars1 + test_suites(2)%suite_name = 'ddt_suite' + test_suites(2)%suite_parts => test_parts2 + test_suites(2)%suite_input_vars => test_invars2 + test_suites(2)%suite_output_vars => test_outvars2 + test_suites(2)%suite_required_vars => test_reqvars2 + + call test_host(run_okay, test_suites) + + if (run_okay) then + STOP 0 + else + STOP -1 + end if + +end program test diff --git a/test/ddthost_test/test_host.F90 b/test/ddthost_test/test_host.F90 index 12c4aeb0..c8213e20 100644 --- a/test/ddthost_test/test_host.F90 +++ b/test/ddthost_test/test_host.F90 @@ -24,92 +24,10 @@ module test_prog contains - logical function check_list(test_list, chk_list, list_desc, suite_name) - ! Check a list () against its expected value () - - ! Dummy arguments - character(len=*), intent(in) :: test_list(:) - character(len=*), intent(in) :: chk_list(:) - character(len=*), intent(in) :: list_desc - character(len=*), optional, intent(in) :: suite_name - - ! Local variables - logical :: found - integer :: num_items - integer :: lindex, tindex - integer, allocatable :: check_unique(:) - character(len=2) :: sep - character(len=256) :: errmsg - - check_list = .true. - errmsg = '' - - ! Check the list size - num_items = size(chk_list) - if (size(test_list) /= num_items) then - write(errmsg, '(a,i0,2a)') 'ERROR: Found ', size(test_list), & - ' ', trim(list_desc) - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' for suite, ', & - trim(suite_name) - end if - write(errmsg(len_trim(errmsg)+1:), '(a,i0)') ', should be ', num_items - write(6, *) trim(errmsg) - errmsg = '' - check_list = .false. - end if - - ! Now, check the list contents for 1-1 correspondence - if (check_list) then - allocate(check_unique(num_items)) - check_unique = -1 - do lindex = 1, num_items - found = .false. - do tindex = 1, num_items - if (trim(test_list(lindex)) == trim(chk_list(tindex))) then - check_unique(tindex) = lindex - found = .true. - exit - end if - end do - if (.not. found) then - check_list = .false. - write(errmsg, '(5a)') 'ERROR: ', trim(list_desc), ' item, ', & - trim(test_list(lindex)), ', was not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - write(6, *) trim(errmsg) - errmsg = '' - end if - end do - if (check_list .and. any(check_unique < 0)) then - check_list = .false. - write(errmsg, '(3a)') 'ERROR: The following ', trim(list_desc), & - ' items were not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - sep = '; ' - do lindex = 1, num_items - if (check_unique(lindex) < 0) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') sep, & - trim(chk_list(lindex)) - sep = ', ' - end if - end do - write(6, *) trim(errmsg) - errmsg = '' - end if - end if - - end function check_list - logical function check_suite(test_suite) use test_host_ccpp_cap, only: ccpp_physics_suite_part_list use test_host_ccpp_cap, only: ccpp_physics_suite_variables + use test_utils, only: check_list ! Dummy argument type(suite_info), intent(in) :: test_suite @@ -195,6 +113,7 @@ subroutine test_host(retval, test_suites) use test_host_ccpp_cap, only: test_host_ccpp_physics_finalize use test_host_ccpp_cap, only: ccpp_physics_suite_list use test_host_mod, only: init_data, compare_data, check_model_times + use test_utils, only: check_list type(suite_info), intent(in) :: test_suites(:) logical, intent(out) :: retval @@ -350,83 +269,3 @@ subroutine test_host(retval, test_suites) end subroutine test_host end module test_prog - - program test - use test_prog, only: test_host, suite_info, cm, cs - - implicit none - - character(len=cs), target :: test_parts1(2) = (/ 'physics1 ', & - 'physics2 ' /) - character(len=cs), target :: test_parts2(1) = (/ 'data_prep ' /) - character(len=cm), target :: test_invars1(7) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'potential_temperature_increment ', & - 'time_step_for_physics ' /) - character(len=cm), target :: test_outvars1(7) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'ccpp_error_code ', & - 'ccpp_error_message ' /) - character(len=cm), target :: test_reqvars1(9) = (/ & - 'potential_temperature ', & - 'potential_temperature_at_interface ', & - 'coefficients_for_interpolation ', & - 'surface_air_pressure ', & - 'water_vapor_specific_humidity ', & - 'potential_temperature_increment ', & - 'time_step_for_physics ', & - 'ccpp_error_code ', & - 'ccpp_error_message ' /) - - character(len=cm), target :: test_invars2(4) = (/ & - 'model_times ', & - 'number_of_model_times ', & - 'surface_air_pressure ', & - 'host_standard_ccpp_type ' /) - - character(len=cm), target :: test_outvars2(5) = (/ & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'model_times ', & - 'surface_air_pressure ', & - 'number_of_model_times ' /) - - character(len=cm), target :: test_reqvars2(6) = (/ & - 'model_times ', & - 'number_of_model_times ', & - 'surface_air_pressure ', & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'host_standard_ccpp_type ' /) - type(suite_info) :: test_suites(2) - logical :: run_okay - - ! Setup expected test suite info - test_suites(1)%suite_name = 'temp_suite' - test_suites(1)%suite_parts => test_parts1 - test_suites(1)%suite_input_vars => test_invars1 - test_suites(1)%suite_output_vars => test_outvars1 - test_suites(1)%suite_required_vars => test_reqvars1 - test_suites(2)%suite_name = 'ddt_suite' - test_suites(2)%suite_parts => test_parts2 - test_suites(2)%suite_input_vars => test_invars2 - test_suites(2)%suite_output_vars => test_outvars2 - test_suites(2)%suite_required_vars => test_reqvars2 - - call test_host(run_okay, test_suites) - - if (run_okay) then - STOP 0 - else - STOP -1 - end if - -end program test diff --git a/test/ddthost_test/test_reports.py b/test/ddthost_test/test_reports.py deleted file mode 100644 index 6fec8be4..00000000 --- a/test/ddthost_test/test_reports.py +++ /dev/null @@ -1,180 +0,0 @@ -#! /usr/bin/env python3 -""" ------------------------------------------------------------------------ - Description: Test capgen database report python interface - - Assumptions: - - Command line arguments: build_dir database_filepath - - Usage: python test_reports ------------------------------------------------------------------------ -""" -import sys -import os - -_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) -_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") -_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") - -if not os.path.exists(_SCRIPTS_DIR): - raise ImportError("Cannot find scripts directory") -# end if - -sys.path.append(_SCRIPTS_DIR) -# pylint: disable=wrong-import-position -from ccpp_datafile import datatable_report, DatatableReport -# pylint: enable=wrong-import-position - -import argparse - -parser = argparse.ArgumentParser(description="Test capgen database report python interface") -parser.add_argument('build_dir') -parser.add_argument('database_filepath') -if len(sys.argv) > 3: - parser.error("Too many arguments") -# end if -args = parser.parse_args() -_BUILD_DIR = os.path.abspath(args.build_dir) -_DATABASE = os.path.abspath(args.database_filepath) -if not os.path.isdir(_BUILD_DIR): - parser.error(" must be an existing build directory") -# end if -if (not os.path.exists(_DATABASE)) or (not os.path.isfile(_DATABASE)): - parser.error(" must be an existing CCPP database file") -# end if - -# Check data -_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] -_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] -_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), - os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), - os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), - os.path.join(_SRC_DIR, "ccpp_hashable.F90"), - os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] -_CCPP_FILES = _UTILITY_FILES + \ - [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_ddt_suite_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_temp_suite_cap.F90")] -_PROCESS_LIST = ["setter=temp_set", "adjusting=temp_calc_adjust"] -_MODULE_LIST = ["environ_conditions", "make_ddt", "setup_coeffs", "temp_adjust", - "temp_calc_adjust", "temp_set"] -_SUITE_LIST = ["ddt_suite", "temp_suite"] -_INPUT_VARS_DDT = ["model_times", "number_of_model_times", - "horizontal_loop_begin", "horizontal_loop_end", - "surface_air_pressure", "horizontal_dimension", - "host_standard_ccpp_type"] -_OUTPUT_VARS_DDT = ["ccpp_error_code", "ccpp_error_message", "model_times", - "number_of_model_times", "surface_air_pressure"] -_REQUIRED_VARS_DDT = _INPUT_VARS_DDT + _OUTPUT_VARS_DDT -_PROT_VARS_TEMP = ["horizontal_loop_begin", "horizontal_loop_end", - "horizontal_dimension", "vertical_layer_dimension", - "number_of_tracers", - # Added for --debug - "index_of_water_vapor_specific_humidity", - "vertical_interface_dimension"] -_REQUIRED_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", - "potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "potential_temperature_increment", - "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity"] -_INPUT_VARS_TEMP = ["potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "potential_temperature_increment", - "surface_air_pressure", "time_step_for_physics", - "water_vapor_specific_humidity"] -_OUTPUT_VARS_TEMP = ["ccpp_error_code", "ccpp_error_message", - "potential_temperature", - "potential_temperature_at_interface", - "coefficients_for_interpolation", - "surface_air_pressure", "water_vapor_specific_humidity"] - -def fields_string(field_type, field_list, sep): - """Create an error string for field(s), . - is used to separate items in """ - indent = ' '*11 - fmsg = "" - if field_list: - if len(field_list) > 1: - field_str = f"{field_type} Fields: " - else: - field_str = f"{field_type} Field: " - # end if - fmsg = f"\n{indent}{field_str}{sep.join(sorted(field_list))}" - # end if - return fmsg - -def check_datatable(database, report_type, check_list, - sep=',', exclude_protected=False): - """Run a database report and check the return string. - If an error is found, print an error message. - Return the number of errors""" - if sep is None: - sep = ',' - # end if - test_str = datatable_report(database, report_type, sep, exclude_protected=exclude_protected) - test_list = [x for x in test_str.split(sep) if x] - tests_run = set(test_list) - expected_tests = set(check_list) - missing = expected_tests - tests_run - unexpected = tests_run - expected_tests - if missing or unexpected: - vmsg = f"ERROR in {report_type.action} datafile check:" - vmsg += fields_string("Missing", missing, sep) - vmsg += fields_string("Unexpected", unexpected, sep) - print(vmsg) - else: - print(f"{report_type.action} report okay") - # end if - return len(missing) + len(unexpected) - -NUM_ERRORS = 0 -print("Checking required files from python:") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("host_files"), - _HOST_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_files"), - _SUITE_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("utility_files"), - _UTILITY_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("ccpp_files"), - _CCPP_FILES) -print("\nChecking lists from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("process_list"), - _PROCESS_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("module_list"), - _MODULE_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_list"), - _SUITE_LIST) -print("\nChecking variables for DDT suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="ddt_suite"), - _REQUIRED_VARS_DDT) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="ddt_suite"), - _INPUT_VARS_DDT) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="ddt_suite"), - _OUTPUT_VARS_DDT) -print("\nChecking variables for temp suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="temp_suite"), - _REQUIRED_VARS_TEMP + _PROT_VARS_TEMP) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="temp_suite"), - _REQUIRED_VARS_TEMP, exclude_protected=True) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="temp_suite"), - _INPUT_VARS_TEMP + _PROT_VARS_TEMP) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="temp_suite"), - _INPUT_VARS_TEMP, exclude_protected=True) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="temp_suite"), - _OUTPUT_VARS_TEMP) - -sys.exit(NUM_ERRORS) diff --git a/test/run_fortran_tests.sh b/test/run_fortran_tests.sh deleted file mode 100755 index 942c0336..00000000 --- a/test/run_fortran_tests.sh +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/bash - -root=$( dirname $( cd $( dirname ${0}); pwd -P ) ) -test_dir=${root}/test - -perr() { - # Print error message ($2) on error ($1) - if [ ${1} -ne 0 ]; then - echo "ERROR: ${2}" - if [ $# -gt 2 ]; then - exit ${3} - else - exit 1 - fi - fi -} - - -cd ${test_dir} -perr $? "Cannot cd to test directory, '${test_dir}'" - -errcnt=0 - -# Run capgen test -./capgen_test/run_test -res=$? -errcnt=$((errcnt + res)) -if [ $res -ne 0 ]; then - echo "Failure running capgen test" -fi - -# Run advection test -./advection_test/run_test -res=$? -errcnt=$((errcnt + res)) -if [ $res -ne 0 ]; then - echo "Failure running advection test" -fi - -# Run DDT host variable test -./ddthost_test/run_test -res=$? -errcnt=$((errcnt + res)) -if [ $res -ne 0 ]; then - echo "Failure running ddthost test" -fi - -# Run var_compatibility test - ./var_compatibility_test/run_test - res=$? - errcnt=$((errcnt + res)) - if [ $res -ne 0 ]; then - echo "Failure running var_compatibility test" - fi - -if [ $errcnt -eq 0 ]; then - echo "All tests PASSed!" -else - if [ $errcnt -eq 1 ]; then - echo "${errcnt} test FAILed" - else - echo "${errcnt} tests FAILed" - fi - #Exit with non-zero exit code - exit 1 -fi diff --git a/test/test_stub.py b/test/test_stub.py new file mode 100644 index 00000000..f54597c3 --- /dev/null +++ b/test/test_stub.py @@ -0,0 +1,162 @@ +from ccpp_datafile import datatable_report, DatatableReport +import subprocess + + +class BaseTests: + + + class TestHostDataTables: + _SEP = "," + + def test_host_files(self): + test_str = datatable_report(self.database, DatatableReport("host_files"), self._SEP) + self.assertSetEqual(set(self.host_files), set(test_str.split(self._SEP))) + + def test_suite_files(self): + test_str = datatable_report(self.database, DatatableReport("suite_files"), self._SEP) + self.assertSetEqual(set(self.suite_files), set(test_str.split(self._SEP))) + + def test_utility_files(self): + test_str = datatable_report(self.database, DatatableReport("utility_files"), self._SEP) + self.assertSetEqual(set(self.utility_files), set(test_str.split(self._SEP))) + + def test_ccpp_files(self): + test_str = datatable_report(self.database, DatatableReport("ccpp_files"), self._SEP) + self.assertSetEqual(set(self.ccpp_files), set(test_str.split(self._SEP))) + + def test_process_list(self): + test_str = datatable_report(self.database, DatatableReport("process_list"), self._SEP) + self.assertSetEqual(set(self.process_list), set(test_str.split(self._SEP))) + + def test_module_list(self): + test_str = datatable_report(self.database, DatatableReport("module_list"), self._SEP) + self.assertSetEqual(set(self.module_list), set(test_str.split(self._SEP))) + + def test_dependencies_list(self): + test_str = datatable_report(self.database, DatatableReport("dependencies"), self._SEP) + self.assertSetEqual(set(self.dependencies), set(test_str.split(self._SEP))) + + def test_suite_list(self): + test_str = datatable_report(self.database, DatatableReport("suite_list"), self._SEP) + self.assertSetEqual(set(self.suite_list), set(test_str.split(self._SEP))) + + + class TestHostCommandLineDataFiles: + _SEP = "," + + def test_host_files(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--host-files"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.host_files), completedProcess.stdout.strip()) + + def test_suite_files(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--suite-files"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.suite_files), completedProcess.stdout.strip()) + + def test_utility_files(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--utility-files"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.utility_files), completedProcess.stdout.strip()) + + def test_ccpp_files(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--ccpp-files"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.ccpp_files), completedProcess.stdout.strip()) + + def test_process_list(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--process-list"], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.process_list), actualOutput) + + def test_module_list(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--module-list"], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.module_list), actualOutput) + + def test_dependencies(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--dependencies"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.dependencies), completedProcess.stdout.strip()) + + def test_suite_list(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--suite-list"], + capture_output=True, + text=True) + self.assertEqual(self._SEP.join(self.suite_list), completedProcess.stdout.strip()) + + + class TestSuite: + _SEP = "," + + def test_required_variables(self): + test_str = datatable_report(self.database, DatatableReport("required_variables", value=self.suite_name), self._SEP) + self.assertSetEqual(set(self.required_vars), set(test_str.split(self._SEP))) + + def test_input_variables(self): + test_str = datatable_report(self.database, DatatableReport("input_variables", value=self.suite_name), self._SEP) + self.assertSetEqual(set(self.input_vars), set(test_str.split(self._SEP))) + + def test_output_variables(self): + test_str = datatable_report(self.database, DatatableReport("output_variables", value=self.suite_name), self._SEP) + self.assertSetEqual(set(self.output_vars), set(test_str.split(self._SEP))) + + + class TestSuiteCommandLine: + _SEP = "," + + def test_required_variables(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--required-variables", self.suite_name], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.required_vars), actualOutput) + + def test_input_variables(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--input-variables", self.suite_name], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.input_vars), actualOutput) + + def test_output_variables(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--output-variables", self.suite_name], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.output_vars), actualOutput) + + + class TestSuiteExcludeProtected(TestSuite): + def test_required_variables_excluding_protected(self): + test_str = datatable_report(self.database, DatatableReport("required_variables", value="temp_suite"), self._SEP, exclude_protected=True) + self.assertSetEqual(set(self.required_vars_excluding_protected), set(test_str.split(self._SEP))) + + def test_input_variables_excluding_protected(self): + test_str = datatable_report(self.database, DatatableReport("input_variables", value="temp_suite"), self._SEP, exclude_protected=True) + self.assertSetEqual(set(self.input_vars_excluding_protected), set(test_str.split(self._SEP))) + + + class TestSuiteExcludeProtectedCommandLine(TestSuiteCommandLine): + def test_required_variables_excluding_protected(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--exclude-protected", "--required-variables", self.suite_name], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.required_vars_excluding_protected), actualOutput) + + def test_input_variables_excluding_protected(self): + completedProcess = subprocess.run([self.datafile_script, self.database, "--exclude-protected", "--input-variables", self.suite_name], + capture_output=True, + text=True) + actualOutput = {s.strip() for s in completedProcess.stdout.split(self._SEP)} + self.assertSetEqual(set(self.input_vars_excluding_protected), actualOutput) diff --git a/test/unit_tests/README.md b/test/unit_tests/README.md index cfd17c74..5a2353ee 100644 --- a/test/unit_tests/README.md +++ b/test/unit_tests/README.md @@ -1,45 +1,29 @@ -# How to build the test/capgen_test (on hera) +# How to run the python unit-tests ## Quick start: +To run all unit-tests: +```bash +$ export PYTHONPATH=/scripts:/scripts/parse_tools +$ pytest -v test/ ``` -cd test/capgen_test -mkdir build -cd build -cmake .. -make -./test_host -``` - -The command to run ccpp_capgen.py is: - -`/scripts/ccpp_capgen.py \ - --host-files test_host_data.meta,test_host_mod.meta,test_host.meta \ - --scheme-files temp_scheme_files.txt,ddt_suite_files.txt \ - --suites ddt_suite.xml,temp_suite.xml\ - --output-root /test/capgen_test/build/ccpp\ - --generate-host-cap` - -where `` is the path to your ccpp/framework directory. - -Modify a *meta* file in `capgen_test` and write a test that passes when fixed. -To run the unit tests: -``` -cd /test/unit_tests -python test_metadata_table.py +To run a specific unit tests: +```bash +$ cd /test/unit_tests +$ python test_metadata_table.py ``` For more verbose output: -``` -python test_metadata_table.py -v +```bash +$ python test_metadata_table.py -v ``` If you have `coverage` installed, to get test coverage: -``` -coverage run test_metadata_table.py -coverage report -m +```bash +$ coverage run test_metadata_table.py +$ coverage report -m ``` To check source code quality with pylint: -``` -cd -env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_table.py -env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_scheme_file.py +```bash +$ cd +$ env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_table.py +$ env PYTHONPATH=scripts:${PYTHONPATH} pylint --rcfile ./test/.pylintrc ./test/unit_tests/test_metadata_scheme_file.py ``` diff --git a/test/utils/CMakeLists.txt b/test/utils/CMakeLists.txt new file mode 100644 index 00000000..dee888ca --- /dev/null +++ b/test/utils/CMakeLists.txt @@ -0,0 +1 @@ +add_library(test_utils STATIC test_utils.F90) diff --git a/test/utils/test_utils.F90 b/test/utils/test_utils.F90 new file mode 100644 index 00000000..088c347d --- /dev/null +++ b/test/utils/test_utils.F90 @@ -0,0 +1,88 @@ +module test_utils + + public :: check_list + +contains + logical function check_list(test_list, chk_list, list_desc, suite_name) + ! Check a list () against its expected value () + + ! Dummy arguments + character(len=*), intent(in) :: test_list(:) + character(len=*), intent(in) :: chk_list(:) + character(len=*), intent(in) :: list_desc + character(len=*), optional, intent(in) :: suite_name + + ! Local variables + logical :: found + integer :: num_items + integer :: lindex, tindex + integer, allocatable :: check_unique(:) + character(len=2) :: sep + character(len=256) :: errmsg + + check_list = .true. + errmsg = '' + + ! Check the list size + num_items = size(chk_list) + if (size(test_list) /= num_items) then + write(errmsg, '(a,i0,2a)') 'ERROR: Found ', size(test_list), & + ' ', trim(list_desc) + if (present(suite_name)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') ' for suite, ', & + trim(suite_name) + end if + write(errmsg(len_trim(errmsg)+1:), '(a,i0)') ', should be ', num_items + write(6, *) trim(errmsg) + errmsg = '' + check_list = .false. + end if + + ! Now, check the list contents for 1-1 correspondence + if (check_list) then + allocate(check_unique(num_items)) + check_unique = -1 + do lindex = 1, num_items + found = .false. + do tindex = 1, num_items + if (trim(test_list(lindex)) == trim(chk_list(tindex))) then + check_unique(tindex) = lindex + found = .true. + exit + end if + end do + if (.not. found) then + check_list = .false. + write(errmsg, '(5a)') 'ERROR: ', trim(list_desc), ' item, ', & + trim(test_list(lindex)), ', was not found' + if (present(suite_name)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & + trim(suite_name) + end if + write(6, *) trim(errmsg) + errmsg = '' + end if + end do + if (check_list .and. any(check_unique < 0)) then + check_list = .false. + write(errmsg, '(3a)') 'ERROR: The following ', trim(list_desc), & + ' items were not found' + if (present(suite_name)) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & + trim(suite_name) + end if + sep = '; ' + do lindex = 1, num_items + if (check_unique(lindex) < 0) then + write(errmsg(len_trim(errmsg)+1:), '(2a)') sep, & + trim(chk_list(lindex)) + sep = ', ' + end if + end do + write(6, *) trim(errmsg) + errmsg = '' + end if + end if + + end function check_list +end module test_utils diff --git a/test/var_compatibility_test/.gitignore b/test/var_compatibility_test/.gitignore deleted file mode 100644 index 378eac25..00000000 --- a/test/var_compatibility_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build diff --git a/test/var_compatibility_test/CMakeLists.txt b/test/var_compatibility_test/CMakeLists.txt index e25f3fda..9204498d 100644 --- a/test/var_compatibility_test/CMakeLists.txt +++ b/test/var_compatibility_test/CMakeLists.txt @@ -1,188 +1,50 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) -PROJECT(test_host) -ENABLE_LANGUAGE(Fortran) - -include(CMakeForceCompiler) - -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) -#------------------------------------------------------------------------------ -# -# Set where the CCPP Framework lives -# -#------------------------------------------------------------------------------ -get_filename_component(TEST_ROOT "${CMAKE_SOURCE_DIR}" DIRECTORY) -get_filename_component(CCPP_ROOT "${TEST_ROOT}" DIRECTORY) #------------------------------------------------------------------------------ # # Create list of SCHEME_FILES, HOST_FILES, and SUITE_FILES # Paths should be relative to CMAKE_SOURCE_DIR (this file's directory) # #------------------------------------------------------------------------------ -LIST(APPEND SCHEME_FILES "var_compatibility_files.txt") -LIST(APPEND HOST_FILES "module_rad_ddt" "test_host_data" "test_host_mod") -LIST(APPEND SUITE_FILES "var_compatibility_suite.xml") +set(SCHEME_FILES "effr_calc" "effr_diag" "effr_pre" "effr_post" "rad_lw" "rad_sw") +set(HOST_FILES "module_rad_ddt" "test_host_data" "test_host_mod") +set(SUITE_FILES "var_compatibility_suite.xml") # HOST is the name of the executable we will build. # We assume there are files ${HOST}.meta and ${HOST}.F90 in CMAKE_SOURCE_DIR -SET(HOST "${CMAKE_PROJECT_NAME}") +set(HOST "test_host") -#------------------------------------------------------------------------------ -# -# End of project-specific input -# -#------------------------------------------------------------------------------ - -# By default, no verbose output -SET(VERBOSITY 0 CACHE STRING "Verbosity level of output (default: 0)") # By default, generated caps go in ccpp subdir -SET(CCPP_CAP_FILES "${CMAKE_BINARY_DIR}/ccpp" CACHE - STRING "Location of CCPP-generated cap files") - -SET(CCPP_FRAMEWORK ${CCPP_ROOT}/scripts) - -# Use rpaths on MacOSX -set(CMAKE_MACOSX_RPATH 1) +set(CCPP_CAP_FILES "${CMAKE_CURRENT_BINARY_DIR}/ccpp") -#------------------------------------------------------------------------------ -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - #message(STATUS "Setting build type to 'Debug' as none was specified.") - #set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) - message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() - -ADD_COMPILE_OPTIONS(-O0) - -if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") -# gfortran -# MESSAGE("gfortran being used.") - ADD_COMPILE_OPTIONS(-fcheck=all) - ADD_COMPILE_OPTIONS(-fbacktrace) - ADD_COMPILE_OPTIONS(-ffpe-trap=zero) - ADD_COMPILE_OPTIONS(-finit-real=nan) - ADD_COMPILE_OPTIONS(-ggdb) - ADD_COMPILE_OPTIONS(-ffree-line-length-none) - ADD_COMPILE_OPTIONS(-cpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") -# ifort -# MESSAGE("ifort being used.") - #ADD_COMPILE_OPTIONS(-check all) - ADD_COMPILE_OPTIONS(-fpe0) - ADD_COMPILE_OPTIONS(-warn) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-debug extended) - ADD_COMPILE_OPTIONS(-fpp) -elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "PGI") -# pgf90 -# MESSAGE("pgf90 being used.") - ADD_COMPILE_OPTIONS(-g) - ADD_COMPILE_OPTIONS(-Mipa=noconst) - ADD_COMPILE_OPTIONS(-traceback) - ADD_COMPILE_OPTIONS(-Mfree) - ADD_COMPILE_OPTIONS(-Mfptrap) - ADD_COMPILE_OPTIONS(-Mpreprocess) -else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - message (WARNING "This program has only been compiled with gfortran, pgf90 and ifort. If another compiler is needed, the appropriate flags SHOULD be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt") -endif (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") - -#------------------------------------------------------------------------------ -# CMake Modules -# Set the CMake module path -list(APPEND CMAKE_MODULE_PATH "${CCPP_FRAMEWORK}/cmake") -#------------------------------------------------------------------------------ -# Set OpenMP flags for C/C++/Fortran -if (OPENMP) - include(detect_openmp) - detect_openmp() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - message(STATUS "Enable OpenMP support for C/C++/Fortran compiler") -else(OPENMP) - message (STATUS "Disable OpenMP support for C/C++/Fortran compiler") -endif() +# Create lists for Fortran and meta data files from file names +list(TRANSFORM SCHEME_FILES APPEND ".F90" OUTPUT_VARIABLE SCHEME_FORTRAN_FILES) +list(TRANSFORM SCHEME_FILES APPEND ".meta" OUTPUT_VARIABLE SCHEME_META_FILES) +list(TRANSFORM HOST_FILES APPEND ".F90" OUTPUT_VARIABLE VAR_COMPATIBILITY_HOST_FORTRAN_FILES) +list(TRANSFORM HOST_FILES APPEND ".meta" OUTPUT_VARIABLE VAR_COMPATIBILITY_HOST_METADATA_FILES) -# Create metadata and source file lists -FOREACH(FILE ${SCHEME_FILES}) - FILE(STRINGS ${FILE} FILENAMES) - LIST(APPEND SCHEME_FILENAMES ${FILENAMES}) -ENDFOREACH(FILE) -string(REPLACE ";" "," SCHEME_METADATA "${SCHEME_FILES}") - -FOREACH(FILE ${SCHEME_FILENAMES}) - # target_sources prefers absolute pathnames - string(REPLACE ".meta" ".F90" TEMP "${FILE}") - get_filename_component(ABS_PATH "${TEMP}" ABSOLUTE) - list(APPEND LIBRARY_LIST ${ABS_PATH}) -ENDFOREACH(FILE) - -FOREACH(FILE ${HOST_FILES}) - LIST(APPEND HOST_METADATA "${FILE}.meta") - # target_sources prefers absolute pathnames - get_filename_component(ABS_PATH "${FILE}.F90" ABSOLUTE) - LIST(APPEND HOST_SOURCE "${ABS_PATH}") -ENDFOREACH(FILE) -list(APPEND LIBRARY_LIST ${HOST_SOURCE}) -string(REPLACE ";" ".meta," HOST_METADATA "${HOST_FILES}") -set(HOST_METADATA "${HOST_METADATA}.meta,${HOST}.meta") - -string(REPLACE ";" "," SUITE_XML "${SUITE_FILES}") +list(APPEND VAR_COMPATIBILITY_HOST_METADATA_FILES "${HOST}.meta") # Run ccpp_capgen -set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py") -list(APPEND CAPGEN_CMD "--host-files") -list(APPEND CAPGEN_CMD "${HOST_METADATA}") -list(APPEND CAPGEN_CMD "--scheme-files") -list(APPEND CAPGEN_CMD "${SCHEME_METADATA}") -list(APPEND CAPGEN_CMD "--suites") -list(APPEND CAPGEN_CMD "${SUITE_XML}") -list(APPEND CAPGEN_CMD "--host-name") -list(APPEND CAPGEN_CMD "test_host") -list(APPEND CAPGEN_CMD "--output-root") -list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}") -while (VERBOSITY GREATER 0) - list(APPEND CAPGEN_CMD "--verbose") - MATH(EXPR VERBOSITY "${VERBOSITY} - 1") -endwhile () -list(APPEND CAPGEN_CMD "--debug") -string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}") -MESSAGE(STATUS "Running: ${CAPGEN_STRING}") -EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE CAPGEN_OUT ERROR_VARIABLE CAPGEN_OUT RESULT_VARIABLE RES) -MESSAGE(STATUS "${CAPGEN_OUT}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap generation completed") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}") -endif(RES EQUAL 0) - -# Retrieve the list of files from datatable.xml and set to CCPP_CAPS -set(DTABLE_CMD "${CCPP_FRAMEWORK}/ccpp_datafile.py") -list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml") -list(APPEND DTABLE_CMD "--ccpp-files") -list(APPEND DTABLE_CMD "--separator=\\;") -string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}") -MESSAGE(STATUS "Running: ${DTABLE_STRING}") -EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} OUTPUT_VARIABLE CCPP_CAPS - RESULT_VARIABLE RES - OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE) -message(STATUS "CCPP_CAPS = ${CCPP_CAPS}") -if (RES EQUAL 0) - MESSAGE(STATUS "CCPP cap files retrieved") -else(RES EQUAL 0) - MESSAGE(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}") -endif(RES EQUAL 0) -list(APPEND LIBRARY_LIST ${CCPP_CAPS}) -add_library(TESTLIB OBJECT ${LIBRARY_LIST}) -ADD_EXECUTABLE(${HOST} ${HOST}.F90 $) - -INCLUDE_DIRECTORIES(${CCPP_CAP_FILES}) - -set_target_properties(${HOST} PROPERTIES - COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}" - LINK_FLAGS "${CMAKE_Fortran_FLAGS}") +ccpp_capgen(CAPGEN_DEBUG ON + VERBOSITY ${CCPP_VERBOSITY} + HOSTFILES ${VAR_COMPATIBILITY_HOST_METADATA_FILES} + SCHEMEFILES ${SCHEME_META_FILES} + SUITES ${SUITE_FILES} + HOST_NAME ${HOST} + OUTPUT_ROOT "${CCPP_CAP_FILES}") + +# Retrieve the list of Fortran files required for test host from datatable.xml and set to CCPP_CAPS_LIST +ccpp_datafile(DATATABLE "${CCPP_CAP_FILES}/datatable.xml" + REPORT_NAME "--ccpp-files") + +# Create test host library +add_library(VAR_COMPATIBILITY_TESTLIB OBJECT ${SCHEME_FORTRAN_FILES} + ${VAR_COMPATIBILITY_HOST_FORTRAN_FILES} + ${CCPP_CAPS_LIST}) + +# Setup test executable with needed dependencies +add_executable(var_compatibility_host_integration test_var_compatibility_integration.F90 ${HOST}.F90) +target_link_libraries(var_compatibility_host_integration PRIVATE VAR_COMPATIBILITY_TESTLIB test_utils) +target_include_directories(var_compatibility_host_integration PRIVATE "$") + +# Add executable to be called with ctest +add_test(NAME ctest_var_compatibility_host_integration COMMAND var_compatibility_host_integration) diff --git a/test/var_compatibility_test/README.md b/test/var_compatibility_test/README.md index 9b56ec9c..4e589cd6 100644 --- a/test/var_compatibility_test/README.md +++ b/test/var_compatibility_test/README.md @@ -1,6 +1,18 @@ -var_compatibility test -================ +# Variable Compatibility Test -To build and run the var_compatibility test, run ./run_test -This script will build and run the test. -The exit code is zero (0) on PASS and non-zero on FAIL. +Tests the variable compatibility object (`VarCompatObj`): +- Unit conversions (forward & reverse) +- Vertical array flipping (`top_at_one=true`) +- Kind conversions (`kind_phys <-> 8`) +- And various combinations thereof of the above cases + +## Building/Running + +To explicitly build/run the variable compatibility test host, run: + +```bash +$ cmake -S -B -DCCPP_RUN_VAR_COMPATIBILITY_TEST=ON +$ cd +$ make +$ ctest +``` diff --git a/test/var_compatibility_test/run_test b/test/var_compatibility_test/run_test deleted file mode 100755 index fb70bde1..00000000 --- a/test/var_compatibility_test/run_test +++ /dev/null @@ -1,287 +0,0 @@ -#! /bin/bash - -currdir="$(pwd -P)" -scriptdir="$( cd $( dirname $0 ); pwd -P )" - -## -## Option default values -## -defdir="vc_build" -build_dir="${currdir}/${defdir}" -cleanup="PASS" # Other supported options are ALWAYS and NEVER -verbosity=0 - -## -## General syntax help function -## Usage: help -## -help () { - local hname="Usage: `basename ${0}`" - local hprefix="`echo ${hname} | tr '[!-~]' ' '`" - echo "${hname} [ --build-dir ] [ --cleanup ]" - echo "${hprefix} [ --verbosity <#> ]" - hprefix=" " - echo "" - echo "${hprefix} : Directory for building and running the test" - echo "${hprefix} default is /${defdir}" - echo "${hprefix} : Cleanup option is ALWAYS, NEVER, or PASS" - echo "${hprefix} default is PASS" - echo "${hprefix} verbosity: 0, 1, or 2" - echo "${hprefix} default is 0" - exit $1 -} - -## -## Error output function (should be handed a string) -## -perr() { - >&2 echo -e "\nERROR: ${@}\n" - exit 1 -} - -## -## Cleanup the build and test directory -## -docleanup() { - # We start off in the build directory - if [ "${build_dir}" == "${currdir}" ]; then - echo "WARNING: Cannot clean ${build_dir}" - else - cd ${currdir} - rm -rf ${build_dir} - fi -} - -## Process our input arguments -while [ $# -gt 0 ]; do - case $1 in - --h | -h | --help | -help) - help 0 - ;; - --build-dir) - if [ $# -lt 2 ]; then - perr "${1} requires a build directory" - fi - build_dir="${2}" - shift - ;; - --cleanup) - if [ $# -lt 2 ]; then - perr "${1} requies a cleanup option (ALWAYS, NEVER, PASS)" - fi - if [ "${2}" == "ALWAYS" -o "${2}" == "NEVER" -o "${2}" == "PASS" ]; then - cleanup="${2}" - else - perr "Allowed cleanup options: ALWAYS, NEVER, PASS" - fi - shift - ;; - --verbosity) - if [ $# -lt 2 ]; then - perr "${1} requires a verbosity value (0, 1, or 2)" - fi - if [ "${2}" == "0" -o "${2}" == "1" -o "${2}" == "2" ]; then - verbosity=$2 - else - perr "allowed verbosity levels are 0, 1, 2" - fi - shift - ;; - *) - perr "Unrecognized option, \"${1}\"" - ;; - esac - shift -done - -# Create the build directory, if necessary -if [ -d "${build_dir}" ]; then - # Always make sure build_dir is not in the test dir - if [ "$( cd ${build_dir}; pwd -P )" == "${currdir}" ]; then - build_dir="${build_dir}/${defdir}" - fi -else - mkdir -p ${build_dir} - res=$? - if [ $res -ne 0 ]; then - perr "Unable to create build directory, '${build_dir}'" - fi -fi -build_dir="$( cd ${build_dir}; pwd -P )" - -## framework is the CCPP Framework root dir -framework="$( cd $( dirname $( dirname ${scriptdir} ) ); pwd -P )" -frame_src="${framework}/src" - -## -## check strings for datafile command-list test -## NB: This has to be after build_dir is finalized -## -host_files="${build_dir}/ccpp/test_host_ccpp_cap.F90" -suite_files="${build_dir}/ccpp/ccpp_var_compatibility_suite_cap.F90" -utility_files="${build_dir}/ccpp/ccpp_kinds.F90" -utility_files="${utility_files},${frame_src}/ccpp_constituent_prop_mod.F90" -utility_files="${utility_files},${frame_src}/ccpp_scheme_utils.F90" -utility_files="${utility_files},${frame_src}/ccpp_hashable.F90" -utility_files="${utility_files},${frame_src}/ccpp_hash_table.F90" -ccpp_files="${utility_files}" -ccpp_files="${ccpp_files},${build_dir}/ccpp/test_host_ccpp_cap.F90" -ccpp_files="${ccpp_files},${build_dir}/ccpp/ccpp_var_compatibility_suite_cap.F90" -#process_list="" -module_list="effr_calc,effr_diag,effr_post,mod_effr_pre,rad_lw,rad_sw" -dependencies="${scriptdir}/module_rad_ddt.F90" -suite_list="var_compatibility_suite" -required_vars_var_compatibility="ccpp_error_code,ccpp_error_message" -required_vars_var_compatibility="${required_vars_var_compatibility},cloud_graupel_number_concentration" -required_vars_var_compatibility="${required_vars_var_compatibility},cloud_ice_number_concentration" -required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_graupel" -required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_ice_particle" -required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" -required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_rain_particle" -required_vars_var_compatibility="${required_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" -required_vars_var_compatibility="${required_vars_var_compatibility},flag_indicating_cloud_microphysics_has_graupel" -required_vars_var_compatibility="${required_vars_var_compatibility},flag_indicating_cloud_microphysics_has_ice" -required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_dimension" -required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_loop_begin" -required_vars_var_compatibility="${required_vars_var_compatibility},horizontal_loop_end" -required_vars_var_compatibility="${required_vars_var_compatibility},longwave_radiation_fluxes" -required_vars_var_compatibility="${required_vars_var_compatibility},num_subcycles_for_effr" -required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing" -required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing_a" -required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing_b" -required_vars_var_compatibility="${required_vars_var_compatibility},scalar_variable_for_testing_c" -required_vars_var_compatibility="${required_vars_var_compatibility},scheme_order_in_suite" -required_vars_var_compatibility="${required_vars_var_compatibility},surface_downwelling_shortwave_radiation_flux" -required_vars_var_compatibility="${required_vars_var_compatibility},surface_upwelling_shortwave_radiation_flux" -required_vars_var_compatibility="${required_vars_var_compatibility},turbulent_kinetic_energy" -required_vars_var_compatibility="${required_vars_var_compatibility},turbulent_kinetic_energy2" -required_vars_var_compatibility="${required_vars_var_compatibility},vertical_layer_dimension" -input_vars_var_compatibility="cloud_graupel_number_concentration" -input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_graupel" -input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" -input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_rain_particle" -input_vars_var_compatibility="${input_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" -input_vars_var_compatibility="${input_vars_var_compatibility},flag_indicating_cloud_microphysics_has_graupel" -input_vars_var_compatibility="${input_vars_var_compatibility},flag_indicating_cloud_microphysics_has_ice" -input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_dimension" -input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_loop_begin" -input_vars_var_compatibility="${input_vars_var_compatibility},horizontal_loop_end" -input_vars_var_compatibility="${input_vars_var_compatibility},longwave_radiation_fluxes" -input_vars_var_compatibility="${input_vars_var_compatibility},num_subcycles_for_effr" -input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing" -input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing_a" -input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing_b" -input_vars_var_compatibility="${input_vars_var_compatibility},scalar_variable_for_testing_c" -input_vars_var_compatibility="${input_vars_var_compatibility},scheme_order_in_suite" -input_vars_var_compatibility="${input_vars_var_compatibility},surface_downwelling_shortwave_radiation_flux" -input_vars_var_compatibility="${input_vars_var_compatibility},surface_upwelling_shortwave_radiation_flux" -input_vars_var_compatibility="${input_vars_var_compatibility},turbulent_kinetic_energy" -input_vars_var_compatibility="${input_vars_var_compatibility},turbulent_kinetic_energy2" -input_vars_var_compatibility="${input_vars_var_compatibility},vertical_layer_dimension" -output_vars_var_compatibility="ccpp_error_code,ccpp_error_message" -output_vars_var_compatibility="${output_vars_var_compatibility},cloud_ice_number_concentration" -output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_ice_particle" -output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_liquid_water_particle" -output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_rain_particle" -output_vars_var_compatibility="${output_vars_var_compatibility},effective_radius_of_stratiform_cloud_snow_particle" -output_vars_var_compatibility="${output_vars_var_compatibility},longwave_radiation_fluxes" -output_vars_var_compatibility="${output_vars_var_compatibility},scalar_variable_for_testing" -output_vars_var_compatibility="${output_vars_var_compatibility},scheme_order_in_suite" -output_vars_var_compatibility="${output_vars_var_compatibility},surface_downwelling_shortwave_radiation_flux" -output_vars_var_compatibility="${output_vars_var_compatibility},surface_upwelling_shortwave_radiation_flux" -output_vars_var_compatibility="${output_vars_var_compatibility},turbulent_kinetic_energy" -output_vars_var_compatibility="${output_vars_var_compatibility},turbulent_kinetic_energy2" - -## -## Run a database report and check the return string -## $1 is the report program file -## $2 is the database file -## $3 is the report string -## $4 is the check string -## $5+ are any optional arguments -## -check_datatable() { - local checkstr=${4} - local teststr - local prog=${1} - local database=${2} - local report=${3} - shift 4 - echo "Checking ${report} report" - teststr="`${prog} ${database} ${report} $@`" - if [ "${teststr}" != "${checkstr}" ]; then - perr "datatable check:\nExpected: '${checkstr}'\nGot: '${teststr}'" - fi -} - -# cd to the build directory -cd ${build_dir} -res=$? -if [ $res -ne 0 ]; then - perr "Unable to cd to build directory, '${build_dir}'" -fi -# Clean build directory -rm -rf * -res=$? -if [ $res -ne 0 ]; then - perr "Unable to clean build directory, '${build_dir}'" -fi -# Run CMake -opts="" -if [ $verbosity -gt 0 ]; then - opts="${opts} -DVERBOSITY=${verbosity}" -fi -# Run cmake -cmake ${scriptdir} ${opts} -res=$? -if [ $res -ne 0 ]; then - perr "CMake failed with exit code, ${res}" -fi -# Test the datafile user interface -report_prog="${framework}/scripts/ccpp_datafile.py" -datafile="${build_dir}/ccpp/datatable.xml" -echo "Running python interface tests" -python3 ${scriptdir}/test_reports.py ${build_dir} ${datafile} -res=$? -if [ $res -ne 0 ]; then - perr "python interface tests failed" -fi -echo "Running command line tests" -echo "Checking required files from command line:" -check_datatable ${report_prog} ${datafile} "--host-files" ${host_files} -check_datatable ${report_prog} ${datafile} "--suite-files" ${suite_files} -check_datatable ${report_prog} ${datafile} "--utility-files" ${utility_files} -check_datatable ${report_prog} ${datafile} "--ccpp-files" ${ccpp_files} -echo -e "\nChecking lists from command line" -check_datatable ${report_prog} ${datafile} "--process-list" ${process_list} -check_datatable ${report_prog} ${datafile} "--module-list" ${module_list} -check_datatable ${report_prog} ${datafile} "--dependencies" ${dependencies} -check_datatable ${report_prog} ${datafile} "--suite-list" ${suite_list} \ - --sep ";" -echo -e "\nChecking variables for var_compatibility suite from command line" -check_datatable ${report_prog} ${datafile} "--required-variables" \ - ${required_vars_var_compatibility} "var_compatibility_suite" -check_datatable ${report_prog} ${datafile} "--input-variables" \ - ${input_vars_var_compatibility} "var_compatibility_suite" -check_datatable ${report_prog} ${datafile} "--output-variables" \ - ${output_vars_var_compatibility} "var_compatibility_suite" -# Run make -make -res=$? -if [ $res -ne 0 ]; then - perr "make failed with exit code, ${res}" -fi -# Run test -./test_host -res=$? -if [ $res -ne 0 ]; then - perr "test_host failed with exit code, ${res}" -fi - -if [ "${cleanup}" == "ALWAYS" ]; then - docleanup -elif [ $res -eq 0 -a "${cleanup}" == "PASS" ]; then - docleanup -fi - -exit $res diff --git a/test/var_compatibility_test/test_host.F90 b/test/var_compatibility_test/test_host.F90 index bbe7b373..f3a389e8 100644 --- a/test/var_compatibility_test/test_host.F90 +++ b/test/var_compatibility_test/test_host.F90 @@ -24,92 +24,10 @@ module test_prog CONTAINS - logical function check_list(test_list, chk_list, list_desc, suite_name) - ! Check a list () against its expected value () - - ! Dummy arguments - character(len=*), intent(in) :: test_list(:) - character(len=*), intent(in) :: chk_list(:) - character(len=*), intent(in) :: list_desc - character(len=*), optional, intent(in) :: suite_name - - ! Local variables - logical :: found - integer :: num_items - integer :: lindex, tindex - integer, allocatable :: check_unique(:) - character(len=2) :: sep - character(len=256) :: errmsg - - check_list = .true. - errmsg = '' - - ! Check the list size - num_items = size(chk_list) - if (size(test_list) /= num_items) then - write(errmsg, '(a,i0,2a)') 'ERROR: Found ', size(test_list), & - ' ', trim(list_desc) - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' for suite, ', & - trim(suite_name) - end if - write(errmsg(len_trim(errmsg)+1:), '(a,i0)') ', should be ', num_items - write(6, *) trim(errmsg) - errmsg = '' - check_list = .false. - end if - - ! Now, check the list contents for 1-1 correspondence - if (check_list) then - allocate(check_unique(num_items)) - check_unique = -1 - do lindex = 1, num_items - found = .false. - do tindex = 1, num_items - if (trim(test_list(lindex)) == trim(chk_list(tindex))) then - check_unique(tindex) = lindex - found = .true. - exit - end if - end do - if (.not. found) then - check_list = .false. - write(errmsg, '(5a)') 'ERROR: ', trim(list_desc), ' item, ', & - trim(test_list(lindex)), ', was not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - write(6, *) trim(errmsg) - errmsg = '' - end if - end do - if (check_list .and. ANY(check_unique < 0)) then - check_list = .false. - write(errmsg, '(3a)') 'ERROR: The following ', trim(list_desc), & - ' items were not found' - if (present(suite_name)) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') ' in suite, ', & - trim(suite_name) - end if - sep = '; ' - do lindex = 1, num_items - if (check_unique(lindex) < 0) then - write(errmsg(len_trim(errmsg)+1:), '(2a)') sep, & - trim(chk_list(lindex)) - sep = ', ' - end if - end do - write(6, *) trim(errmsg) - errmsg = '' - end if - end if - - end function check_list - logical function check_suite(test_suite) use test_host_ccpp_cap, only: ccpp_physics_suite_part_list use test_host_ccpp_cap, only: ccpp_physics_suite_variables + use test_utils, only: check_list ! Dummy argument type(suite_info), intent(in) :: test_suite @@ -194,6 +112,7 @@ subroutine test_host(retval, test_suites) use test_host_ccpp_cap, only: test_host_ccpp_physics_finalize use test_host_ccpp_cap, only: ccpp_physics_suite_list use test_host_mod, only: init_data, compare_data + use test_utils, only: check_list type(suite_info), intent(in) :: test_suites(:) logical, intent(out) :: retval @@ -343,90 +262,3 @@ subroutine test_host(retval, test_suites) end subroutine test_host end module test_prog - - program test - use test_prog, only: test_host, suite_info, cm, cs - - implicit none - - character(len=cs), target :: test_parts1(1) = (/ 'radiation ' /) - - character(len=cm), target :: test_invars1(18) = (/ & - 'effective_radius_of_stratiform_cloud_rain_particle ', & - 'effective_radius_of_stratiform_cloud_liquid_water_particle', & - 'effective_radius_of_stratiform_cloud_snow_particle ', & - 'effective_radius_of_stratiform_cloud_graupel ', & - 'cloud_graupel_number_concentration ', & - 'scalar_variable_for_testing ', & - 'turbulent_kinetic_energy ', & - 'turbulent_kinetic_energy2 ', & - 'scalar_variable_for_testing_a ', & - 'scalar_variable_for_testing_b ', & - 'scalar_variable_for_testing_c ', & - 'scheme_order_in_suite ', & - 'num_subcycles_for_effr ', & - 'flag_indicating_cloud_microphysics_has_graupel ', & - 'flag_indicating_cloud_microphysics_has_ice ', & - 'surface_downwelling_shortwave_radiation_flux ', & - 'surface_upwelling_shortwave_radiation_flux ', & - 'longwave_radiation_fluxes '/) - - character(len=cm), target :: test_outvars1(14) = (/ & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'effective_radius_of_stratiform_cloud_ice_particle ', & - 'effective_radius_of_stratiform_cloud_liquid_water_particle', & - 'effective_radius_of_stratiform_cloud_rain_particle ', & - 'effective_radius_of_stratiform_cloud_snow_particle ', & - 'cloud_ice_number_concentration ', & - 'scalar_variable_for_testing ', & - 'scheme_order_in_suite ', & - 'surface_downwelling_shortwave_radiation_flux ', & - 'surface_upwelling_shortwave_radiation_flux ', & - 'turbulent_kinetic_energy ', & - 'turbulent_kinetic_energy2 ', & - 'longwave_radiation_fluxes '/) - - character(len=cm), target :: test_reqvars1(22) = (/ & - 'ccpp_error_code ', & - 'ccpp_error_message ', & - 'effective_radius_of_stratiform_cloud_rain_particle ', & - 'effective_radius_of_stratiform_cloud_ice_particle ', & - 'effective_radius_of_stratiform_cloud_liquid_water_particle', & - 'effective_radius_of_stratiform_cloud_snow_particle ', & - 'effective_radius_of_stratiform_cloud_graupel ', & - 'cloud_graupel_number_concentration ', & - 'cloud_ice_number_concentration ', & - 'scalar_variable_for_testing ', & - 'turbulent_kinetic_energy ', & - 'turbulent_kinetic_energy2 ', & - 'scalar_variable_for_testing_a ', & - 'scalar_variable_for_testing_b ', & - 'scalar_variable_for_testing_c ', & - 'scheme_order_in_suite ', & - 'num_subcycles_for_effr ', & - 'flag_indicating_cloud_microphysics_has_graupel ', & - 'flag_indicating_cloud_microphysics_has_ice ', & - 'surface_downwelling_shortwave_radiation_flux ', & - 'surface_upwelling_shortwave_radiation_flux ', & - 'longwave_radiation_fluxes '/) - - type(suite_info) :: test_suites(1) - logical :: run_okay - - ! Setup expected test suite info - test_suites(1)%suite_name = 'var_compatibility_suite' - test_suites(1)%suite_parts => test_parts1 - test_suites(1)%suite_input_vars => test_invars1 - test_suites(1)%suite_output_vars => test_outvars1 - test_suites(1)%suite_required_vars => test_reqvars1 - - call test_host(run_okay, test_suites) - - if (run_okay) then - STOP 0 - else - STOP -1 - end if - -end program test diff --git a/test/var_compatibility_test/test_reports.py b/test/var_compatibility_test/test_reports.py deleted file mode 100755 index 4bf0d6bb..00000000 --- a/test/var_compatibility_test/test_reports.py +++ /dev/null @@ -1,183 +0,0 @@ -#! /usr/bin/env python3 -""" ------------------------------------------------------------------------ - Description: Test capgen database report python interface - - Assumptions: - - Command line arguments: build_dir database_filepath - - Usage: python test_reports ------------------------------------------------------------------------ -""" -import sys -import os - -_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) -_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") -_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") - -if not os.path.exists(_SCRIPTS_DIR): - raise ImportError("Cannot find scripts directory") -# end if - -if ((sys.version_info[0] < 3) or - (sys.version_info[0] == 3) and (sys.version_info[1] < 8)): - raise Exception("Python 3.8 or greater required") -# end if - -sys.path.append(_SCRIPTS_DIR) -# pylint: disable=wrong-import-position -from ccpp_datafile import datatable_report, DatatableReport -# pylint: enable=wrong-import-position - -def usage(errmsg=None): - """Raise an exception with optional error message and usage message""" - emsg = "usage: {} " - if errmsg: - emsg = errmsg + '\n' + emsg - # end if - raise ValueError(emsg.format(sys.argv[0])) - -if len(sys.argv) != 3: - usage() -# end if - -_BUILD_DIR = os.path.abspath(sys.argv[1]) -_DATABASE = os.path.abspath(sys.argv[2]) -if not os.path.isdir(_BUILD_DIR): - _EMSG = " must be an existing build directory" - usage(_EMSG) -# end if -if (not os.path.exists(_DATABASE)) or (not os.path.isfile(_DATABASE)): - _EMSG = " must be an existing CCPP database file" - usage(_EMSG) -# end if - -# Check data -_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] -_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_var_compatibility_suite_cap.F90")] -_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), - os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), - os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), - os.path.join(_SRC_DIR, "ccpp_hashable.F90"), - os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] -_CCPP_FILES = _UTILITY_FILES + \ - [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), - os.path.join(_BUILD_DIR, "ccpp", "ccpp_var_compatibility_suite_cap.F90")] -_MODULE_LIST = ["effr_calc", "effr_diag", "effr_post", "mod_effr_pre", "rad_lw", "rad_sw"] -_SUITE_LIST = ["var_compatibility_suite"] -_DEPENDENCIES = [ os.path.join(_TEST_DIR, "module_rad_ddt.F90")] -_INPUT_VARS_VAR_ACTION = ["horizontal_loop_begin", "horizontal_loop_end", "horizontal_dimension", "vertical_layer_dimension", - "effective_radius_of_stratiform_cloud_liquid_water_particle", - "effective_radius_of_stratiform_cloud_rain_particle", - "effective_radius_of_stratiform_cloud_snow_particle", - "effective_radius_of_stratiform_cloud_graupel", - "cloud_graupel_number_concentration", - "scalar_variable_for_testing", - "turbulent_kinetic_energy", - "turbulent_kinetic_energy2", - "scalar_variable_for_testing_a", - "scalar_variable_for_testing_b", - "scalar_variable_for_testing_c", - "scheme_order_in_suite", - "flag_indicating_cloud_microphysics_has_graupel", - "flag_indicating_cloud_microphysics_has_ice", - "surface_downwelling_shortwave_radiation_flux", - "surface_upwelling_shortwave_radiation_flux", - "longwave_radiation_fluxes", - "num_subcycles_for_effr"] -_OUTPUT_VARS_VAR_ACTION = ["ccpp_error_code", "ccpp_error_message", - "effective_radius_of_stratiform_cloud_ice_particle", - "effective_radius_of_stratiform_cloud_liquid_water_particle", - "effective_radius_of_stratiform_cloud_snow_particle", - "cloud_ice_number_concentration", - "effective_radius_of_stratiform_cloud_rain_particle", - "turbulent_kinetic_energy", - "turbulent_kinetic_energy2", - "scalar_variable_for_testing", - "scalar_variable_for_testing", - "surface_downwelling_shortwave_radiation_flux", - "surface_upwelling_shortwave_radiation_flux", - "longwave_radiation_fluxes", - "scheme_order_in_suite"] -_REQUIRED_VARS_VAR_ACTION = _INPUT_VARS_VAR_ACTION + _OUTPUT_VARS_VAR_ACTION - -def fields_string(field_type, field_list, sep): - """Create an error string for field(s), . - is used to separate items in """ - indent = ' '*11 - if field_list: - if len(field_list) > 1: - field_str = "{} Fields: ".format(field_type) - else: - field_str = "{} Field: ".format(field_type) - # end if - fmsg = "\n{}{}{}".format(indent, field_str, sep.join(field_list)) - else: - fmsg = "" - # end if - return fmsg - -def check_datatable(database, report_type, check_list, - sep=',', exclude_protected=False): - """Run a database report and check the return string. - If an error is found, print an error message. - Return the number of errors""" - if sep is None: - sep = ',' - # end if - test_str = datatable_report(database, report_type, sep, exclude_protected=exclude_protected) - test_list = [x for x in test_str.split(sep) if x] - missing = list() - unexpected = list() - for item in check_list: - if item not in test_list: - missing.append(item) - # end if - # end for - for item in test_list: - if item not in check_list: - unexpected.append(item) - # end if - # end for - if missing or unexpected: - vmsg = "ERROR in {} datafile check:".format(report_type.action) - vmsg += fields_string("Missing", missing, sep) - vmsg += fields_string("Unexpected", unexpected, sep) - print(vmsg) - else: - print("{} report okay".format(report_type.action)) - # end if - return len(missing) + len(unexpected) - -NUM_ERRORS = 0 -print("Checking required files from python:") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("host_files"), - _HOST_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_files"), - _SUITE_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("utility_files"), - _UTILITY_FILES) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("ccpp_files"), - _CCPP_FILES) -print("\nChecking lists from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("module_list"), - _MODULE_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("suite_list"), - _SUITE_LIST) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("dependencies"), - _DEPENDENCIES) -print("\nChecking variables for var_compatibility suite from python") -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("required_variables", - value="var_compatibility_suite"), - _REQUIRED_VARS_VAR_ACTION) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("input_variables", - value="var_compatibility_suite"), - _INPUT_VARS_VAR_ACTION) -NUM_ERRORS += check_datatable(_DATABASE, DatatableReport("output_variables", - value="var_compatibility_suite"), - _OUTPUT_VARS_VAR_ACTION) - -sys.exit(NUM_ERRORS) diff --git a/test/var_compatibility_test/test_var_compatibility_integration.F90 b/test/var_compatibility_test/test_var_compatibility_integration.F90 new file mode 100644 index 00000000..1e081e10 --- /dev/null +++ b/test/var_compatibility_test/test_var_compatibility_integration.F90 @@ -0,0 +1,85 @@ +program test_var_compatibility_integration + use test_prog, only: test_host, suite_info, cm, cs + + implicit none + + character(len=cs), target :: test_parts1(1) = (/ 'radiation ' /) + + character(len=cm), target :: test_invars1(18) = (/ & + 'effective_radius_of_stratiform_cloud_rain_particle ', & + 'effective_radius_of_stratiform_cloud_liquid_water_particle', & + 'effective_radius_of_stratiform_cloud_snow_particle ', & + 'effective_radius_of_stratiform_cloud_graupel ', & + 'cloud_graupel_number_concentration ', & + 'scalar_variable_for_testing ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & + 'scalar_variable_for_testing_a ', & + 'scalar_variable_for_testing_b ', & + 'scalar_variable_for_testing_c ', & + 'scheme_order_in_suite ', & + 'num_subcycles_for_effr ', & + 'flag_indicating_cloud_microphysics_has_graupel ', & + 'flag_indicating_cloud_microphysics_has_ice ', & + 'surface_downwelling_shortwave_radiation_flux ', & + 'surface_upwelling_shortwave_radiation_flux ', & + 'longwave_radiation_fluxes '/) + + character(len=cm), target :: test_outvars1(14) = (/ & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'effective_radius_of_stratiform_cloud_ice_particle ', & + 'effective_radius_of_stratiform_cloud_liquid_water_particle', & + 'effective_radius_of_stratiform_cloud_rain_particle ', & + 'effective_radius_of_stratiform_cloud_snow_particle ', & + 'cloud_ice_number_concentration ', & + 'scalar_variable_for_testing ', & + 'scheme_order_in_suite ', & + 'surface_downwelling_shortwave_radiation_flux ', & + 'surface_upwelling_shortwave_radiation_flux ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & + 'longwave_radiation_fluxes '/) + + character(len=cm), target :: test_reqvars1(22) = (/ & + 'ccpp_error_code ', & + 'ccpp_error_message ', & + 'effective_radius_of_stratiform_cloud_rain_particle ', & + 'effective_radius_of_stratiform_cloud_ice_particle ', & + 'effective_radius_of_stratiform_cloud_liquid_water_particle', & + 'effective_radius_of_stratiform_cloud_snow_particle ', & + 'effective_radius_of_stratiform_cloud_graupel ', & + 'cloud_graupel_number_concentration ', & + 'cloud_ice_number_concentration ', & + 'scalar_variable_for_testing ', & + 'turbulent_kinetic_energy ', & + 'turbulent_kinetic_energy2 ', & + 'scalar_variable_for_testing_a ', & + 'scalar_variable_for_testing_b ', & + 'scalar_variable_for_testing_c ', & + 'scheme_order_in_suite ', & + 'num_subcycles_for_effr ', & + 'flag_indicating_cloud_microphysics_has_graupel ', & + 'flag_indicating_cloud_microphysics_has_ice ', & + 'surface_downwelling_shortwave_radiation_flux ', & + 'surface_upwelling_shortwave_radiation_flux ', & + 'longwave_radiation_fluxes '/) + + type(suite_info) :: test_suites(1) + logical :: run_okay + + ! Setup expected test suite info + test_suites(1)%suite_name = 'var_compatibility_suite' + test_suites(1)%suite_parts => test_parts1 + test_suites(1)%suite_input_vars => test_invars1 + test_suites(1)%suite_output_vars => test_outvars1 + test_suites(1)%suite_required_vars => test_reqvars1 + + call test_host(run_okay, test_suites) + + if (run_okay) then + STOP 0 + else + STOP -1 + end if +end program test_var_compatibility_integration diff --git a/test/var_compatibility_test/var_compatibility_files.txt b/test/var_compatibility_test/var_compatibility_files.txt deleted file mode 100644 index 71df1054..00000000 --- a/test/var_compatibility_test/var_compatibility_files.txt +++ /dev/null @@ -1,7 +0,0 @@ -module_rad_ddt.meta -effr_calc.meta -effr_diag.meta -effr_pre.meta -effr_post.meta -rad_lw.meta -rad_sw.meta diff --git a/test/var_compatibility_test/var_compatibility_test_reports.py b/test/var_compatibility_test/var_compatibility_test_reports.py new file mode 100755 index 00000000..5a8bdb95 --- /dev/null +++ b/test/var_compatibility_test/var_compatibility_test_reports.py @@ -0,0 +1,116 @@ +#! /usr/bin/env python3 +""" +----------------------------------------------------------------------- + Description: Test capgen database report python interface + + Assumptions: + + Command line arguments: build_dir database_filepath + + Usage: python test_reports +----------------------------------------------------------------------- +""" +import os +import unittest + +from test_stub import BaseTests + +_BUILD_DIR = os.path.join(os.path.abspath(os.environ['BUILD_DIR']), "test", "var_compatibility_test") +_DATABASE = os.path.abspath(os.path.join(_BUILD_DIR, "ccpp", "datatable.xml")) + +_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +_FRAMEWORK_DIR = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) +_SCRIPTS_DIR = os.path.join(_FRAMEWORK_DIR, "scripts") +_SRC_DIR = os.path.join(_FRAMEWORK_DIR, "src") + +# Check data +_HOST_FILES = [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90")] +_SUITE_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_var_compatibility_suite_cap.F90")] +_UTILITY_FILES = [os.path.join(_BUILD_DIR, "ccpp", "ccpp_kinds.F90"), + os.path.join(_SRC_DIR, "ccpp_constituent_prop_mod.F90"), + os.path.join(_SRC_DIR, "ccpp_scheme_utils.F90"), + os.path.join(_SRC_DIR, "ccpp_hashable.F90"), + os.path.join(_SRC_DIR, "ccpp_hash_table.F90")] +_CCPP_FILES = _UTILITY_FILES + \ + [os.path.join(_BUILD_DIR, "ccpp", "test_host_ccpp_cap.F90"), + os.path.join(_BUILD_DIR, "ccpp", "ccpp_var_compatibility_suite_cap.F90")] +_PROCESS_LIST = [""] +_MODULE_LIST = ["effr_calc", "effr_diag", "effr_post", "mod_effr_pre", "rad_lw", "rad_sw"] +_SUITE_LIST = ["var_compatibility_suite"] +_DEPENDENCIES = [ os.path.join(_TEST_DIR, "module_rad_ddt.F90")] +_INPUT_VARS_VAR_ACTION = ["horizontal_loop_begin", "horizontal_loop_end", "horizontal_dimension", "vertical_layer_dimension", + "effective_radius_of_stratiform_cloud_liquid_water_particle", + "effective_radius_of_stratiform_cloud_rain_particle", + "effective_radius_of_stratiform_cloud_snow_particle", + "effective_radius_of_stratiform_cloud_graupel", + "cloud_graupel_number_concentration", + "scalar_variable_for_testing", + "turbulent_kinetic_energy", + "turbulent_kinetic_energy2", + "scalar_variable_for_testing_a", + "scalar_variable_for_testing_b", + "scalar_variable_for_testing_c", + "scheme_order_in_suite", + "flag_indicating_cloud_microphysics_has_graupel", + "flag_indicating_cloud_microphysics_has_ice", + "surface_downwelling_shortwave_radiation_flux", + "surface_upwelling_shortwave_radiation_flux", + "longwave_radiation_fluxes", + "num_subcycles_for_effr"] +_OUTPUT_VARS_VAR_ACTION = ["ccpp_error_code", "ccpp_error_message", + "effective_radius_of_stratiform_cloud_ice_particle", + "effective_radius_of_stratiform_cloud_liquid_water_particle", + "effective_radius_of_stratiform_cloud_snow_particle", + "cloud_ice_number_concentration", + "effective_radius_of_stratiform_cloud_rain_particle", + "turbulent_kinetic_energy", + "turbulent_kinetic_energy2", + "scalar_variable_for_testing", + "scalar_variable_for_testing", + "surface_downwelling_shortwave_radiation_flux", + "surface_upwelling_shortwave_radiation_flux", + "longwave_radiation_fluxes", + "scheme_order_in_suite"] +_REQUIRED_VARS_VAR_ACTION = _INPUT_VARS_VAR_ACTION + _OUTPUT_VARS_VAR_ACTION + + +class TestVarCompatibilityHostDataTables(unittest.TestCase, BaseTests.TestHostDataTables): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + + +class CommandLineVarCompatibilityHostDatafileRequiredFiles(unittest.TestCase, BaseTests.TestHostCommandLineDataFiles): + database = _DATABASE + host_files = _HOST_FILES + suite_files = _SUITE_FILES + utility_files = _UTILITY_FILES + ccpp_files = _CCPP_FILES + process_list = _PROCESS_LIST + module_list = _MODULE_LIST + dependencies = _DEPENDENCIES + suite_list = _SUITE_LIST + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py" + + +class TestCapgenDdtSuite(unittest.TestCase, BaseTests.TestSuite): + database = _DATABASE + required_vars = _REQUIRED_VARS_VAR_ACTION + input_vars = _INPUT_VARS_VAR_ACTION + output_vars = _OUTPUT_VARS_VAR_ACTION + suite_name = "var_compatibility_suite" + + +class CommandLineCapgenDdtSuite(unittest.TestCase, BaseTests.TestSuiteCommandLine): + database = _DATABASE + required_vars = _REQUIRED_VARS_VAR_ACTION + input_vars = _INPUT_VARS_VAR_ACTION + output_vars = _OUTPUT_VARS_VAR_ACTION + suite_name = "var_compatibility_suite" + datafile_script = f"{_SCRIPTS_DIR}/ccpp_datafile.py"