diff --git a/.github/workflows/fortran_unit_tests.yml b/.github/workflows/fortran_unit_tests.yml new file mode 100644 index 00000000..8b298ebf --- /dev/null +++ b/.github/workflows/fortran_unit_tests.yml @@ -0,0 +1,73 @@ +name: Fortran Unit Tests + +on: + push: + branches: + - development + - main + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + gcc-toolchain: + runs-on: ubuntu-latest + strategy: + matrix: + version: [12, 13, 14] + env: + CC: gcc-${{ matrix.version }} + CXX: g++-${{ matrix.version }} + FC: gfortran-${{ matrix.version }} + steps: + - name: Checkout cam-sima + uses: actions/checkout@v4 + + - name: Build pFUnit + run: | + git clone --depth 1 --branch v4.10.0 https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git + cd pFUnit + cmake -B./build -S. + cd build + make install + + - name: Build cam-sima + run: | + cmake \ + -DCMAKE_PREFIX_PATH=/home/runner/work/CAM-SIMA/CAM-SIMA/pFUnit/build/installed \ + -DCAM_SIMA_ENABLE_CODE_COVERAGE=ON \ + -B./build \ + -S./test/unit/fortran + cd build + make + + - name: Run fortran unit tests + run: | + cd build && ctest -V --output-on-failure --output-junit test_results.xml + + - name: Upload unit test results + uses: actions/upload-artifact@v4 + with: + name: unit-test-results-${{ env.FC }} + path: build/test_results.xml + + - name: Setup GCov + run: | + python3 -m venv venv + source venv/bin/activate + pip3 install gcovr + + - name: Run Gcov + run: | + source venv/bin/activate + cd build + gcovr --gcov-executable gcov-${{ matrix.version }} -r .. --filter '\.\./src' --html cam_sima_code_coverage.html --txt + + - name: Upload code coverage results + uses: actions/upload-artifact@v4 + with: + name: code-coverage-results-${{ env.FC }} + path: build/cam_sima_code_coverage.html diff --git a/.github/workflows/python_unit_tests.yml b/.github/workflows/python_unit_tests.yml index 395412e6..429ceca7 100644 --- a/.github/workflows/python_unit_tests.yml +++ b/.github/workflows/python_unit_tests.yml @@ -1,6 +1,7 @@ name: Python Unit Tests on: + workflow_dispatch: pull_request: types: [opened, synchronize, reopened] push: @@ -16,12 +17,12 @@ jobs: #a PR is either opened or synced (i.e. additional commits are pushed #to branch involved in PR). python_unit_tests: - if: github.event_name == 'pull_request' || github.repository == 'ESCOMP/CAM-SIMA' + if: github.event_name == 'pull_request' || github.repository == 'ESCOMP/CAM-SIMA' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest strategy: matrix: #All of these python versions will be used to run tests: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] fail-fast: false steps: # Acquire github action routines: @@ -56,4 +57,4 @@ jobs: pytest src/data --doctest-modules # Run all python unit tests: - pytest test/unit + pytest test/unit/python diff --git a/.gitignore b/.gitignore index 7d6f4be2..8ca10c01 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ buildnmlc # Ignore test output test/include/*.mod test/include/*.o -test/unit/tmp +test/unit/python/tmp test/system/*.log test/system/cime-tests.o* test_driver_*.sh diff --git a/cime_config/buildlib b/cime_config/buildlib index 33f1d3ec..c564a4b6 100755 --- a/cime_config/buildlib +++ b/cime_config/buildlib @@ -101,7 +101,8 @@ def _build_cam(): os.path.join(atm_root, "src", "history", "buffers", "src"), os.path.join(atm_root, "src", "history", "buffers", "src", "hash"), os.path.join(atm_root, "src", "history", "buffers", "src", "util"), - os.path.join(atm_root, "src", "utils")] + os.path.join(atm_root, "src", "utils"), + os.path.join(atm_root, "src", "core_utils")] for path in phys_dirs: if path not in paths: paths.append(path) diff --git a/cime_config/cam_autogen.py b/cime_config/cam_autogen.py index e08b1f5b..581c98d1 100644 --- a/cime_config/cam_autogen.py +++ b/cime_config/cam_autogen.py @@ -132,7 +132,7 @@ def _find_scheme_source(source_dirs, metadata_file_name): doctests: 1. Check that the function can correctly find source and namelist files: - >>> _find_scheme_source([os.path.join(_CAM_ROOT_DIR, "test", "unit", "sample_files", \ + >>> _find_scheme_source([os.path.join(_CAM_ROOT_DIR, "test", "unit", "python", "sample_files", \ "autogen_files")], "two_scheme_banana") # doctest: +ELLIPSIS ('...two_scheme_banana.F90', '...two_scheme_banana_namelist.xml') diff --git a/src/core_utils/CMakeLists.txt b/src/core_utils/CMakeLists.txt new file mode 100644 index 00000000..9919672f --- /dev/null +++ b/src/core_utils/CMakeLists.txt @@ -0,0 +1,9 @@ +set(CORE_UTILS_SRC string_core_utils.F90) + +# core_utils is not integrated into the CMake build of any top level +# project yet and this CMake is for testing purposes only. +# Making a change to this project's CMake will not impact the build of +# a parent project at this time. +add_library(core_utils ${CORE_UTILS_SRC}) +target_include_directories(core_utils PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + diff --git a/src/core_utils/string_core_utils.F90 b/src/core_utils/string_core_utils.F90 new file mode 100644 index 00000000..cffd8a0e --- /dev/null +++ b/src/core_utils/string_core_utils.F90 @@ -0,0 +1,162 @@ +module string_core_utils + + implicit none + private + + public :: core_to_str ! Convert integer to left justified string + public :: core_int_date_to_yyyymmdd ! Convert encoded date integer to "yyyy-mm-dd" format + public :: core_int_seconds_to_hhmmss ! Convert integer seconds past midnight to "hh:mm:ss" format + public :: core_stringify ! Convert one or more values of any intrinsic data types to a character string for pretty printing + +CONTAINS + + character(len=10) pure function core_to_str(n) + ! return default integer as a left justified string + + integer, intent(in) :: n + + write(core_to_str,'(i0)') n + + end function core_to_str + + character(len=10) pure function core_int_date_to_yyyymmdd (date) + ! Undefined behavior if date <= 0 + + ! Input arguments + integer, intent(in) :: date + + ! Local variables + integer :: year ! year of yyyy-mm-dd + integer :: month ! month of yyyy-mm-dd + integer :: day ! day of yyyy-mm-dd + + year = date / 10000 + month = (date - year*10000) / 100 + day = date - year*10000 - month*100 + + write(core_int_date_to_yyyymmdd, '(i4.4,A,i2.2,A,i2.2)') & + year,'-',month,'-',day + + end function core_int_date_to_yyyymmdd + + character(len=8) pure function core_int_seconds_to_hhmmss (seconds) + ! Undefined behavior if seconds outside [0, 86400] + + ! Input arguments + integer, intent(in) :: seconds + + ! Local variables + integer :: hours ! hours of hh:mm:ss + integer :: minutes ! minutes of hh:mm:ss + integer :: secs ! seconds of hh:mm:ss + + hours = seconds / 3600 + minutes = (seconds - hours*3600) / 60 + secs = (seconds - hours*3600 - minutes*60) + + write(core_int_seconds_to_hhmmss,'(i2.2,A,i2.2,A,i2.2)') & + hours,':',minutes,':',secs + + end function core_int_seconds_to_hhmmss + + !> Convert one or more values of any intrinsic data types to a character string for pretty printing. + !> If `value` contains more than one element, the elements will be stringified, delimited by `separator`, then concatenated. + !> If `value` contains exactly one element, the element will be stringified without using `separator`. + !> If `value` contains zero element or is of unsupported data types, an empty character string is produced. + !> If `separator` is not supplied, it defaults to `, ` (i.e., a comma and a space). + !> (KCW, 2024-02-04) + pure function core_stringify(value, separator) + use, intrinsic :: iso_fortran_env, only: int32, int64, real32, real64 + + class(*), intent(in) :: value(:) + character(*), optional, intent(in) :: separator + character(:), allocatable :: core_stringify + + integer, parameter :: sizelimit = 1024 + + character(:), allocatable :: buffer, delimiter, format + integer :: i, n, offset + + if (present(separator)) then + delimiter = separator + else + delimiter = ', ' + end if + + n = min(size(value), sizelimit) + + if (n == 0) then + core_stringify = '' + return + end if + + select type (value) + type is (character(*)) + allocate(character(len(value) * n + len(delimiter) * (n - 1)) :: buffer) + + buffer(:) = '' + offset = 0 + + do i = 1, n + if (len(delimiter) > 0 .and. i > 1) then + buffer(offset + 1:offset + len(delimiter)) = delimiter + offset = offset + len(delimiter) + end if + + if (len_trim(adjustl(value(i))) > 0) then + buffer(offset + 1:offset + len_trim(adjustl(value(i)))) = trim(adjustl(value(i))) + offset = offset + len_trim(adjustl(value(i))) + end if + end do + type is (integer(int32)) + allocate(character(11 * n + len(delimiter) * (n - 1)) :: buffer) + allocate(character(17 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + + write(format, '(a, i0, 3a)') '(ss, ', n, '(i0, :, "', delimiter, '"))' + write(buffer, format) value + type is (integer(int64)) + allocate(character(20 * n + len(delimiter) * (n - 1)) :: buffer) + allocate(character(17 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + + write(format, '(a, i0, 3a)') '(ss, ', n, '(i0, :, "', delimiter, '"))' + write(buffer, format) value + type is (logical) + allocate(character(1 * n + len(delimiter) * (n - 1)) :: buffer) + allocate(character(13 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + + write(format, '(a, i0, 3a)') '(', n, '(l1, :, "', delimiter, '"))' + write(buffer, format) value + type is (real(real32)) + allocate(character(13 * n + len(delimiter) * (n - 1)) :: buffer) + + if (maxval(abs(value)) < 1.0e5_real32) then + allocate(character(20 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + write(format, '(a, i0, 3a)') '(ss, ', n, '(f13.6, :, "', delimiter, '"))' + else + allocate(character(23 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + write(format, '(a, i0, 3a)') '(ss, ', n, '(es13.6e2, :, "', delimiter, '"))' + end if + + write(buffer, format) value + type is (real(real64)) + allocate(character(13 * n + len(delimiter) * (n - 1)) :: buffer) + + if (maxval(abs(value)) < 1.0e5_real64) then + allocate(character(20 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + write(format, '(a, i0, 3a)') '(ss, ', n, '(f13.6, :, "', delimiter, '"))' + else + allocate(character(23 + len(delimiter) + floor(log10(real(n))) + 1) :: format) + write(format, '(a, i0, 3a)') '(ss, ', n, '(es13.6e2, :, "', delimiter, '"))' + end if + + write(buffer, format) value + class default + core_stringify = '' + return + end select + + core_stringify = trim(buffer) + + end function core_stringify + +end module string_core_utils diff --git a/src/utils/string_utils.F90 b/src/utils/string_utils.F90 index 223d54da..c3d10b79 100644 --- a/src/utils/string_utils.F90 +++ b/src/utils/string_utils.F90 @@ -4,390 +4,84 @@ module string_utils use shr_string_mod, only: to_lower => shr_string_toLower use cam_logfile, only: iulog use cam_abortutils, only: endrun + use string_core_utils, only: core_int_date_to_yyyymmdd, core_int_seconds_to_hhmmss + use string_core_utils, only: stringify=>core_stringify, to_str=>core_to_str implicit none private ! Public interface methods - public :: strlist_get_ind ! Gets the index of a given string in a list of strings - public :: date2yyyymmdd ! convert encoded date integer to "yyyy-mm-dd" format - public :: sec2hms ! convert integer seconds past midnight to "hh:mm:ss" format - public :: increment_string ! increments a string - public :: last_sig_char ! Position of last significant character in string - public :: to_str ! convert integer to left justified string + public :: date2yyyymmdd ! Convert encoded date integer to "yyyy-mm-dd" format + public :: sec2hms ! Convert integer seconds past midnight to "hh:mm:ss" format + public :: to_str ! Convert integer to left justified string public :: stringify ! Convert one or more values of any intrinsic data types to a character string for pretty printing - ! Private module variables - integer, parameter :: lower_to_upper = iachar("A") - iachar("a") - integer, parameter :: upper_to_lower = iachar("a") - iachar("A") - CONTAINS - !========================================================================================= - subroutine strlist_get_ind(strlist, str, ind, abort) - ! Get the index of a given string in a list of strings. Optional abort argument - ! allows returning control to caller when the string is not found. Default - ! behavior is to call endrun when string is not found. - - ! Arguments - character(len=*), intent(in) :: strlist(:) ! list of strings - character(len=*), intent(in) :: str ! string to search for - integer, intent(out) :: ind ! index of str in strlist - logical, optional, intent(in) :: abort ! flag controlling abort + ! Get the index of a given string in a list of strings. Optional abort argument + ! allows returning control to caller when the string is not found. Default + ! behavior is to call endrun when string is not found. + + ! Arguments + character(len=*), intent(in) :: strlist(:) ! list of strings + character(len=*), intent(in) :: str ! string to search for + integer, intent(out) :: ind ! index of str in strlist + logical, optional, intent(in) :: abort ! flag controlling abort + + ! Local variables + integer :: m + logical :: abort_on_error + character(len=*), parameter :: sub='strlist_get_ind' + + ! Find string in list + do m = 1, size(strlist) + if (str == strlist(m)) then + ind = m + return + end if + end do - ! Local variables - integer :: m - logical :: abort_on_error - character(len=*), parameter :: sub='strlist_get_ind' - !---------------------------------------------------------------------------- + ! String not found + abort_on_error = .true. + if (present(abort)) abort_on_error = abort - ! Find string in list - do m = 1, size(strlist) - if (str == strlist(m)) then - ind = m - return + if (abort_on_error) then + write(iulog, *) sub//': FATAL: string:', trim(str), ' not found in list:', strlist(:) + call endrun(sub//': FATAL: string not found') end if - end do - - ! String not found - abort_on_error = .true. - if (present(abort)) abort_on_error = abort - - if (abort_on_error) then - write(iulog, *) sub//': FATAL: string:', trim(str), ' not found in list:', strlist(:) - call endrun(sub//': FATAL: string not found') - end if - ! error return - ind = -1 + ! error return + ind = -1 end subroutine strlist_get_ind - !========================================================================================= - character(len=10) function date2yyyymmdd (date) - ! Input arguments - integer, intent(in) :: date - - ! Local workspace - - integer :: year ! year of yyyy-mm-dd - integer :: month ! month of yyyy-mm-dd - integer :: day ! day of yyyy-mm-dd - + if (date < 0) then + write(iulog,*)'DATE2YYYYMMDD: negative date not allowed' call endrun ('DATE2YYYYMMDD: negative date not allowed') end if - year = date / 10000 - month = (date - year*10000) / 100 - day = date - year*10000 - month*100 - - write(date2yyyymmdd,80) year, month, day - 80 format(i4.4,'-',i2.2,'-',i2.2) + date2yyyymmdd = core_int_date_to_yyyymmdd(date) end function date2yyyymmdd - !========================================================================================= - character(len=8) function sec2hms (seconds) - ! Input arguments - integer, intent(in) :: seconds - ! Local workspace - - integer :: hours ! hours of hh:mm:ss - integer :: minutes ! minutes of hh:mm:ss - integer :: secs ! seconds of hh:mm:ss - if (seconds < 0 .or. seconds > 86400) then write(iulog,*)'SEC2HMS: bad input seconds:', seconds call endrun ('SEC2HMS: bad input seconds: '//stringify((/seconds/))) end if - hours = seconds / 3600 - minutes = (seconds - hours*3600) / 60 - secs = (seconds - hours*3600 - minutes*60) - - write(sec2hms,80) hours, minutes, secs - 80 format(i2.2,':',i2.2,':',i2.2) + sec2hms = core_int_seconds_to_hhmmss(seconds) end function sec2hms - !========================================================================================= - - integer function increment_string(str, increment) - !----------------------------------------------------------------------- - ! ... Increment a string whose ending characters are digits. - ! The incremented integer must be in the range [0 - (10**n)-1] - ! where n is the number of trailing digits. - ! Return values: - ! - ! 0 success - ! -1 error: no trailing digits in string - ! -2 error: incremented integer is out of range - !----------------------------------------------------------------------- - - !----------------------------------------------------------------------- - ! ... Dummy variables - !----------------------------------------------------------------------- - character(len=*), intent(inout) :: str ! string with trailing digits - ! increment: value to increment string (may be negative) - integer, intent(in) :: increment - - !----------------------------------------------------------------------- - ! ... Local variables - !----------------------------------------------------------------------- - integer :: ind ! index - integer :: lstr ! number of significant characters in string - integer :: lnd ! position of last non-digit - integer :: ndigit ! number of trailing digits - integer :: ival ! integer value of trailing digits - integer :: exp ! power of ten related to most sig digit processed - integer :: digit ! integer value of a single digit - - lstr = last_sig_char(str) - lnd = last_index(str) - ndigit = lstr - lnd - - if (ndigit == 0) then - increment_string = -1 - return - end if - - !----------------------------------------------------------------------- - ! ... Calculate integer corresponding to trailing digits. - !----------------------------------------------------------------------- - ival = 0 - exp = 1 - do ind = lstr, lnd+1, -1 - digit = ICHAR(str(ind:ind)) - ICHAR('0') - ival = ival + (digit * exp) - exp = exp * 10 - end do - - !----------------------------------------------------------------------- - ! ... Increment the integer and test for range - !----------------------------------------------------------------------- - exp = exp - 1 - ival = ival + increment - if (ival < 0 .or. ival > exp) then - increment_string = -2 - return - end if - - !----------------------------------------------------------------------- - ! ... Record new values - !----------------------------------------------------------------------- - do ind = lstr, lnd+1, -1 - str(ind:ind) = ACHAR(MOD(ival, 10) + ICHAR('0')) - ival = ival / 10 - end do - - increment_string = 0 - - end function increment_string - - !=========================================================================== - - integer function last_index(cstr) - !----------------------------------------------------------------------- - ! ... Position of last non-digit in the first input token. - ! Return values: - ! > 0 => position of last non-digit - ! = 0 => token is all digits (or empty) - !----------------------------------------------------------------------- - !----------------------------------------------------------------------- - ! ... Dummy arguments - !----------------------------------------------------------------------- - character(len=*), intent(in) :: cstr ! Input character string - - !----------------------------------------------------------------------- - ! ... Local variables - !----------------------------------------------------------------------- - integer :: lsc ! last sig char - integer :: index - integer :: digit - - lsc = last_sig_char(cstr) - if (lsc == 0) then ! empty string - last_index = 0 - return - end if - - do index = lsc, 1, -1 - digit = ICHAR(cstr(index:index)) - ICHAR('0') - if ((digit < 0) .or. (digit > 9)) then - last_index = index - return - end if - end do - - last_index = 0 ! all characters are digits - - end function last_index - - !=========================================================================== - - integer function last_sig_char(cstr) - !----------------------------------------------------------------------- - ! ... Position of last significant character in string. - ! Here significant means non-blank or non-null. - ! Return values: - ! > 0 => position of last significant character - ! = 0 => no significant characters in string - !----------------------------------------------------------------------- - !----------------------------------------------------------------------- - ! ... Dummy arguments - !----------------------------------------------------------------------- - character(len=*), intent(in) :: cstr ! Input character string - - !----------------------------------------------------------------------- - ! ... Local variables - !----------------------------------------------------------------------- - integer :: slen - integer :: index - - slen = len_trim(cstr) - if (slen == 0) then - last_sig_char = 0 - return - end if - - do index = slen, 1, -1 - if ( (cstr(index:index) /= ' ') .and. & - (cstr(index:index) /= ACHAR(0))) then - exit - end if - end do - last_sig_char = index - - end function last_sig_char - - !=========================================================================== - - character(len=10) function to_str(n) - - ! return default integer as a left justified string - - ! arguments - integer, intent(in) :: n - !---------------------------------------------------------------------------- - - write(to_str,'(i0)') n - - end function to_str - - !=========================================================================== - - !> Convert one or more values of any intrinsic data types to a character string for pretty printing. - !> If `value` contains more than one element, the elements will be stringified, delimited by `separator`, then concatenated. - !> If `value` contains exactly one element, the element will be stringified without using `separator`. - !> If `value` contains zero element or is of unsupported data types, an empty character string is produced. - !> If `separator` is not supplied, it defaults to `, ` (i.e., a comma and a space). - !> (KCW, 2024-02-04) - pure function stringify(value, separator) - use, intrinsic :: iso_fortran_env, only: int32, int64, real32, real64 - - class(*), intent(in) :: value(:) - character(*), optional, intent(in) :: separator - character(:), allocatable :: stringify - - integer, parameter :: sizelimit = 1024 - - character(:), allocatable :: buffer, delimiter, format - integer :: i, n, offset - - if (present(separator)) then - delimiter = separator - else - delimiter = ', ' - end if - - n = min(size(value), sizelimit) - - if (n == 0) then - stringify = '' - - return - end if - - select type (value) - type is (character(*)) - allocate(character(len(value) * n + len(delimiter) * (n - 1)) :: buffer) - - buffer(:) = '' - offset = 0 - - do i = 1, n - if (len(delimiter) > 0 .and. i > 1) then - buffer(offset + 1:offset + len(delimiter)) = delimiter - offset = offset + len(delimiter) - end if - - if (len_trim(adjustl(value(i))) > 0) then - buffer(offset + 1:offset + len_trim(adjustl(value(i)))) = trim(adjustl(value(i))) - offset = offset + len_trim(adjustl(value(i))) - end if - end do - type is (integer(int32)) - allocate(character(11 * n + len(delimiter) * (n - 1)) :: buffer) - allocate(character(17 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - - write(format, '(a, i0, 3a)') '(ss, ', n, '(i0, :, "', delimiter, '"))' - write(buffer, format) value - type is (integer(int64)) - allocate(character(20 * n + len(delimiter) * (n - 1)) :: buffer) - allocate(character(17 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - - write(format, '(a, i0, 3a)') '(ss, ', n, '(i0, :, "', delimiter, '"))' - write(buffer, format) value - type is (logical) - allocate(character(1 * n + len(delimiter) * (n - 1)) :: buffer) - allocate(character(13 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - - write(format, '(a, i0, 3a)') '(', n, '(l1, :, "', delimiter, '"))' - write(buffer, format) value - type is (real(real32)) - allocate(character(13 * n + len(delimiter) * (n - 1)) :: buffer) - - if (maxval(abs(value)) < 1.0e5_real32) then - allocate(character(20 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - write(format, '(a, i0, 3a)') '(ss, ', n, '(f13.6, :, "', delimiter, '"))' - else - allocate(character(23 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - write(format, '(a, i0, 3a)') '(ss, ', n, '(es13.6e2, :, "', delimiter, '"))' - end if - - write(buffer, format) value - type is (real(real64)) - allocate(character(13 * n + len(delimiter) * (n - 1)) :: buffer) - - if (maxval(abs(value)) < 1.0e5_real64) then - allocate(character(20 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - write(format, '(a, i0, 3a)') '(ss, ', n, '(f13.6, :, "', delimiter, '"))' - else - allocate(character(23 + len(delimiter) + floor(log10(real(n))) + 1) :: format) - write(format, '(a, i0, 3a)') '(ss, ', n, '(es13.6e2, :, "', delimiter, '"))' - end if - - write(buffer, format) value - class default - stringify = '' - - return - end select - - stringify = trim(buffer) - end function stringify - -!========================================================================================= - end module string_utils diff --git a/test/run_unit_tests.sh b/test/run_python_unit_tests.sh similarity index 85% rename from test/run_unit_tests.sh rename to test/run_python_unit_tests.sh index a68cbf08..ec4a9d78 100755 --- a/test/run_unit_tests.sh +++ b/test/run_python_unit_tests.sh @@ -69,21 +69,21 @@ run_doctest cime_config/atm_in_paramgen.py # CAM history config doctests: run_doctest cime_config/hist_config.py # CAM config unit tests: -run_unittest test/unit/test_cam_config.py +run_unittest test/unit/python/test_cam_config.py # CAM autogen unit tests: -run_unittest test/unit/test_cam_autogen.py +run_unittest test/unit/python/test_cam_autogen.py # CAM build cache unit tests: -run_unittest test/unit/test_build_cache.py +run_unittest test/unit/python/test_build_cache.py # Registry generator unit tests: -run_unittest test/unit/test_registry.py +run_unittest test/unit/python/test_registry.py # Namelist reader autogeneration unit tests -run_unittest test/unit/test_create_readnl_files.py +run_unittest test/unit/python/test_create_readnl_files.py # Physics variable init (phys_init) generator unit tests: -run_unittest test/unit/test_write_init_files.py +run_unittest test/unit/python/test_write_init_files.py # ParamGen atm_in namelist writer unit tests: -run_unittest test/unit/test_atm_in_paramgen.py +run_unittest test/unit/python/test_atm_in_paramgen.py # CAM history config unit tests -run_unittest test/unit/test_hist_config.py +run_unittest test/unit/python/test_hist_config.py # Report if [ ${NUMERRORS} -gt 0 ]; then diff --git a/test/unit/fortran/CMakeLists.txt b/test/unit/fortran/CMakeLists.txt new file mode 100644 index 00000000..965f1581 --- /dev/null +++ b/test/unit/fortran/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.18) + +project(CAM-SIMA-core-utils LANGUAGES Fortran) + +option(CAM_SIMA_ENABLE_TESTS "Run pFUnit unit tests" OFF) +option(CAM_SIMA_ENABLE_CODE_COVERAGE "Run code coverage tool" OFF) + +if(NOT CAM-SIMA-core-utils_IS_TOP_LEVEL) + message(WARNING "CAM-SIMA-core-utils is not integrated into the CMake build of any top level " + "project yet and this CMake is for testing purposes only. " + "Making a change to this project's CMake will not impact the build of " + "a parent project at this time.") +endif() + +# Overwrite the init flags chosen by CMake +if(${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") + add_compile_options("-ffree-line-length-none") +endif() + +if(CAM_SIMA_ENABLE_CODE_COVERAGE) + add_compile_options(-O0 --coverage) + add_link_options(--coverage) +endif() + +add_subdirectory(${CMAKE_SOURCE_DIR}/../../../src/core_utils ${CMAKE_BINARY_DIR}/src) + +if(CAM_SIMA_ENABLE_TESTS OR CAM_SIMA_ENABLE_CODE_COVERAGE) +set(CMAKE_BUILD_TYPE Debug) + find_package(PFUNIT REQUIRED) + enable_testing() + add_subdirectory(src/core_utils) +endif() diff --git a/test/unit/fortran/src/core_utils/CMakeLists.txt b/test/unit/fortran/src/core_utils/CMakeLists.txt new file mode 100644 index 00000000..b288a189 --- /dev/null +++ b/test/unit/fortran/src/core_utils/CMakeLists.txt @@ -0,0 +1,3 @@ +add_pfunit_ctest(core_utils_tests + TEST_SOURCES test_string_core_utils.pf + LINK_LIBRARIES core_utils) diff --git a/test/unit/fortran/src/core_utils/test_string_core_utils.pf b/test/unit/fortran/src/core_utils/test_string_core_utils.pf new file mode 100644 index 00000000..a87c670d --- /dev/null +++ b/test/unit/fortran/src/core_utils/test_string_core_utils.pf @@ -0,0 +1,287 @@ +@test +subroutine test_integer_one_to_str() + use funit + use string_core_utils, only : core_to_str + + integer :: to_convert + + to_convert = 1 + + @assertEqual("1", core_to_str(to_convert)) +end subroutine test_integer_one_to_str + +@test +subroutine test_integer_with_leading_zeros_to_str() + use funit + use string_core_utils, only : core_to_str + + integer :: to_convert + character(len=10) :: actual + + to_convert = 001 + actual = core_to_str(to_convert) + + @assertEqual("1", actual) +end subroutine test_integer_with_leading_zeros_to_str + +@test +subroutine test_integer_with_trailing_zeros_to_str() + use funit + use string_core_utils, only : core_to_str + + integer :: to_convert + character(len=10) :: actual + + to_convert = 100 + actual = core_to_str(to_convert) + + @assertEqual("100", actual) +end subroutine test_integer_with_trailing_zeros_to_str + +@test +subroutine test_one_int_date_to_yyyymmdd() + use funit + use string_core_utils, only : core_int_date_to_yyyymmdd + + integer :: to_convert + character(len=10) :: actual + + to_convert = 1 + actual = core_int_date_to_yyyymmdd(to_convert) + + @assertEqual("0000-00-01", actual) +end subroutine test_one_int_date_to_yyyymmdd + +@test +subroutine test_zero_int_seconds_to_hhmmss() + use funit + use string_core_utils, only : core_int_seconds_to_hhmmss + + integer :: to_convert + character(len=8) :: actual + + to_convert = 0 + actual = core_int_seconds_to_hhmmss(to_convert) + + @assertEqual("00:00:00", actual) +end subroutine test_zero_int_seconds_to_hhmmss + +@test +subroutine test_stringify_empty_arrays() + use, intrinsic :: iso_fortran_env, only: int32, int64, real32, real64 + use funit + use string_core_utils, only: core_stringify + + character(128), allocatable :: carr(:) + integer(int32), allocatable :: i32arr(:) + integer(int64), allocatable :: i64arr(:) + logical, allocatable :: larr(:) + real(real32), allocatable :: r32arr(:) + real(real64), allocatable :: r64arr(:) + + + allocate(carr(0), i32arr(0), i64arr(0), larr(0), r32arr(0), r64arr(0)) + + @assertEqual("", core_stringify(carr)) + @assertEqual("", core_stringify(i32arr)) + @assertEqual("", core_stringify(i64arr)) + @assertEqual("", core_stringify(larr)) + @assertEqual("", core_stringify(r32arr)) + @assertEqual("", core_stringify(r64arr)) + +end subroutine test_stringify_empty_arrays + +! Ignoring this test due to a bug in the GNU compiler >= v12. +! https://github.com/ESCOMP/CAM-SIMA/pull/326#discussion_r1909520600 +@test(#ifndef= __GNUC__) +subroutine test_stringify_character_array + use funit + use string_core_utils, only: core_stringify + + character(128), allocatable :: carr(:) + allocate(carr(10)) + + carr(:) = [ character(len(carr)) :: & + ' Talc', 'Gypsum', 'Calcite', 'Fluorite', 'Apatite', & + 'Orthoclase', 'Quartz', ' Topaz', 'Corundum', 'Diamond' ] + + @assertEqual('Talc, Gypsum, Calcite, Fluorite, Apatite, Orthoclase, Quartz, Topaz, Corundum, Diamond', core_stringify(carr)) + @assertEqual('TalcPhysics state variables updated by dynamical core - $SRCROOT/test/unit/sample_files/ref_pres.meta + $SRCROOT/test/unit/python/sample_files/ref_pres.meta diff --git a/test/unit/sample_files/reg_good_complete.xml b/test/unit/python/sample_files/reg_good_complete.xml similarity index 98% rename from test/unit/sample_files/reg_good_complete.xml rename to test/unit/python/sample_files/reg_good_complete.xml index a18660ba..9ef13746 100644 --- a/test/unit/sample_files/reg_good_complete.xml +++ b/test/unit/python/sample_files/reg_good_complete.xml @@ -106,5 +106,5 @@ Physics state variables updated by dynamical core - $SRCROOT/test/unit/sample_files/ref_pres.meta + $SRCROOT/test/unit/python/sample_files/ref_pres.meta diff --git a/test/unit/sample_files/reg_good_ddt.xml b/test/unit/python/sample_files/reg_good_ddt.xml similarity index 100% rename from test/unit/sample_files/reg_good_ddt.xml rename to test/unit/python/sample_files/reg_good_ddt.xml diff --git a/test/unit/sample_files/reg_good_ddt2.xml b/test/unit/python/sample_files/reg_good_ddt2.xml similarity index 100% rename from test/unit/sample_files/reg_good_ddt2.xml rename to test/unit/python/sample_files/reg_good_ddt2.xml diff --git a/test/unit/sample_files/reg_good_ddt_array.xml b/test/unit/python/sample_files/reg_good_ddt_array.xml similarity index 100% rename from test/unit/sample_files/reg_good_ddt_array.xml rename to test/unit/python/sample_files/reg_good_ddt_array.xml diff --git a/test/unit/sample_files/reg_good_mf.xml b/test/unit/python/sample_files/reg_good_mf.xml similarity index 96% rename from test/unit/sample_files/reg_good_mf.xml rename to test/unit/python/sample_files/reg_good_mf.xml index 618cb8c2..d39f356e 100644 --- a/test/unit/sample_files/reg_good_mf.xml +++ b/test/unit/python/sample_files/reg_good_mf.xml @@ -41,5 +41,5 @@ Physics state variables updated by dynamical core - $SRCROOT/test/unit/sample_files/ref_pres.meta + $SRCROOT/test/unit/python/sample_files/ref_pres.meta diff --git a/test/unit/sample_files/reg_good_simple.xml b/test/unit/python/sample_files/reg_good_simple.xml similarity index 100% rename from test/unit/sample_files/reg_good_simple.xml rename to test/unit/python/sample_files/reg_good_simple.xml diff --git a/test/unit/sample_files/rotten_namelist.xml b/test/unit/python/sample_files/rotten_namelist.xml similarity index 100% rename from test/unit/sample_files/rotten_namelist.xml rename to test/unit/python/sample_files/rotten_namelist.xml diff --git a/test/unit/sample_files/write_init_files/ddt2_reg.xml b/test/unit/python/sample_files/write_init_files/ddt2_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/ddt2_reg.xml rename to test/unit/python/sample_files/write_init_files/ddt2_reg.xml diff --git a/test/unit/sample_files/write_init_files/ddt_array_reg.xml b/test/unit/python/sample_files/write_init_files/ddt_array_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/ddt_array_reg.xml rename to test/unit/python/sample_files/write_init_files/ddt_array_reg.xml diff --git a/test/unit/sample_files/write_init_files/ddt_reg.xml b/test/unit/python/sample_files/write_init_files/ddt_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/ddt_reg.xml rename to test/unit/python/sample_files/write_init_files/ddt_reg.xml diff --git a/test/unit/sample_files/write_init_files/host_var_host.F90 b/test/unit/python/sample_files/write_init_files/host_var_host.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/host_var_host.F90 rename to test/unit/python/sample_files/write_init_files/host_var_host.F90 diff --git a/test/unit/sample_files/write_init_files/host_var_host.meta b/test/unit/python/sample_files/write_init_files/host_var_host.meta similarity index 100% rename from test/unit/sample_files/write_init_files/host_var_host.meta rename to test/unit/python/sample_files/write_init_files/host_var_host.meta diff --git a/test/unit/sample_files/write_init_files/host_var_reg.xml b/test/unit/python/sample_files/write_init_files/host_var_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/host_var_reg.xml rename to test/unit/python/sample_files/write_init_files/host_var_reg.xml diff --git a/test/unit/sample_files/write_init_files/mf_reg.xml b/test/unit/python/sample_files/write_init_files/mf_reg.xml similarity index 88% rename from test/unit/sample_files/write_init_files/mf_reg.xml rename to test/unit/python/sample_files/write_init_files/mf_reg.xml index 0183dd50..e39c9eaf 100644 --- a/test/unit/sample_files/write_init_files/mf_reg.xml +++ b/test/unit/python/sample_files/write_init_files/mf_reg.xml @@ -14,5 +14,5 @@ eddy_len - $SRCROOT/test/unit/sample_files/write_init_files/ref_theta.meta + $SRCROOT/test/unit/python/sample_files/write_init_files/ref_theta.meta diff --git a/test/unit/sample_files/write_init_files/missing_ICs_reg.xml b/test/unit/python/sample_files/write_init_files/missing_ICs_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/missing_ICs_reg.xml rename to test/unit/python/sample_files/write_init_files/missing_ICs_reg.xml diff --git a/test/unit/sample_files/write_init_files/no_horiz_dim_reg.xml b/test/unit/python/sample_files/write_init_files/no_horiz_dim_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/no_horiz_dim_reg.xml rename to test/unit/python/sample_files/write_init_files/no_horiz_dim_reg.xml diff --git a/test/unit/sample_files/write_init_files/no_req_var_reg.xml b/test/unit/python/sample_files/write_init_files/no_req_var_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/no_req_var_reg.xml rename to test/unit/python/sample_files/write_init_files/no_req_var_reg.xml diff --git a/test/unit/sample_files/write_init_files/param_reg.xml b/test/unit/python/sample_files/write_init_files/param_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/param_reg.xml rename to test/unit/python/sample_files/write_init_files/param_reg.xml diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_4D.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_4D.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_4D.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_4D.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_bvd.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_bvd.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_bvd.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_bvd.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_cnst.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_cnst.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_cnst.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_cnst.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_ddt.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_ddt.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_ddt2.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt2.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_ddt2.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt2.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_host_var.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_host_var.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_host_var.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_host_var.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_mf.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_mf.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_mf.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_mf.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_noreq.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_noreq.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_noreq.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_noreq.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_param.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_param.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_param.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_param.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_parameter.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_parameter.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_parameter.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_parameter.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_protect.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_protect.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_protect.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_protect.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_scalar.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_scalar.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_scalar.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_scalar.F90 diff --git a/test/unit/sample_files/write_init_files/phys_vars_init_check_simple.F90 b/test/unit/python/sample_files/write_init_files/phys_vars_init_check_simple.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/phys_vars_init_check_simple.F90 rename to test/unit/python/sample_files/write_init_files/phys_vars_init_check_simple.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_4D.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_4D.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_bvd.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_bvd.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_cnst.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_cnst.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_ddt.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_ddt.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_ddt2.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_ddt2.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_ddt_array.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_ddt_array.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_host_var.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_host_var.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_mf.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_mf.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_no_horiz.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_no_horiz.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_noreq.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_noreq.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_param.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_param.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_param.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_parameter.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_parameter.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_parameter.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_parameter.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_protect.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_protect.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_scalar.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_scalar.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90 diff --git a/test/unit/sample_files/write_init_files/physics_inputs_simple.F90 b/test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/physics_inputs_simple.F90 rename to test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90 diff --git a/test/unit/sample_files/write_init_files/protected_reg.xml b/test/unit/python/sample_files/write_init_files/protected_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/protected_reg.xml rename to test/unit/python/sample_files/write_init_files/protected_reg.xml diff --git a/test/unit/sample_files/write_init_files/ref_theta.F90 b/test/unit/python/sample_files/write_init_files/ref_theta.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/ref_theta.F90 rename to test/unit/python/sample_files/write_init_files/ref_theta.F90 diff --git a/test/unit/sample_files/write_init_files/ref_theta.meta b/test/unit/python/sample_files/write_init_files/ref_theta.meta similarity index 100% rename from test/unit/sample_files/write_init_files/ref_theta.meta rename to test/unit/python/sample_files/write_init_files/ref_theta.meta diff --git a/test/unit/sample_files/write_init_files/ref_two.F90 b/test/unit/python/sample_files/write_init_files/ref_two.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/ref_two.F90 rename to test/unit/python/sample_files/write_init_files/ref_two.F90 diff --git a/test/unit/sample_files/write_init_files/ref_two.meta b/test/unit/python/sample_files/write_init_files/ref_two.meta similarity index 100% rename from test/unit/sample_files/write_init_files/ref_two.meta rename to test/unit/python/sample_files/write_init_files/ref_two.meta diff --git a/test/unit/sample_files/write_init_files/scalar_var_reg.xml b/test/unit/python/sample_files/write_init_files/scalar_var_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/scalar_var_reg.xml rename to test/unit/python/sample_files/write_init_files/scalar_var_reg.xml diff --git a/test/unit/sample_files/write_init_files/simple_build_cache_template.xml b/test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml similarity index 100% rename from test/unit/sample_files/write_init_files/simple_build_cache_template.xml rename to test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml diff --git a/test/unit/sample_files/write_init_files/simple_host.F90 b/test/unit/python/sample_files/write_init_files/simple_host.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/simple_host.F90 rename to test/unit/python/sample_files/write_init_files/simple_host.F90 diff --git a/test/unit/sample_files/write_init_files/simple_host.meta b/test/unit/python/sample_files/write_init_files/simple_host.meta similarity index 100% rename from test/unit/sample_files/write_init_files/simple_host.meta rename to test/unit/python/sample_files/write_init_files/simple_host.meta diff --git a/test/unit/sample_files/write_init_files/simple_reg.xml b/test/unit/python/sample_files/write_init_files/simple_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/simple_reg.xml rename to test/unit/python/sample_files/write_init_files/simple_reg.xml diff --git a/test/unit/sample_files/write_init_files/suite_simple.xml b/test/unit/python/sample_files/write_init_files/suite_simple.xml similarity index 100% rename from test/unit/sample_files/write_init_files/suite_simple.xml rename to test/unit/python/sample_files/write_init_files/suite_simple.xml diff --git a/test/unit/sample_files/write_init_files/temp_adjust.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust.meta b/test/unit/python/sample_files/write_init_files/temp_adjust.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_4D.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_4D.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_4D.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_4D.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_4D.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_4D.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_4D.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_4D.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_bvd.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_bvd.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_bvd.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_bvd.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_bvd.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_bvd.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_bvd.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_bvd.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_cnst.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_cnst.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_cnst.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_cnst.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_cnst.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_cnst.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_cnst.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_cnst.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_no_horiz.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_no_horiz.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_no_horiz.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_no_horiz.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_noreq.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_noreq.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_noreq.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_noreq.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_noreq.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_noreq.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_noreq.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_noreq.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_param.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_param.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_param.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_param.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_param.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_param.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_param.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_param.meta diff --git a/test/unit/sample_files/write_init_files/temp_adjust_scalar.F90 b/test/unit/python/sample_files/write_init_files/temp_adjust_scalar.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_scalar.F90 rename to test/unit/python/sample_files/write_init_files/temp_adjust_scalar.F90 diff --git a/test/unit/sample_files/write_init_files/temp_adjust_scalar.meta b/test/unit/python/sample_files/write_init_files/temp_adjust_scalar.meta similarity index 100% rename from test/unit/sample_files/write_init_files/temp_adjust_scalar.meta rename to test/unit/python/sample_files/write_init_files/temp_adjust_scalar.meta diff --git a/test/unit/sample_files/write_init_files/theta_ddt.F90 b/test/unit/python/sample_files/write_init_files/theta_ddt.F90 similarity index 100% rename from test/unit/sample_files/write_init_files/theta_ddt.F90 rename to test/unit/python/sample_files/write_init_files/theta_ddt.F90 diff --git a/test/unit/sample_files/write_init_files/theta_ddt.meta b/test/unit/python/sample_files/write_init_files/theta_ddt.meta similarity index 100% rename from test/unit/sample_files/write_init_files/theta_ddt.meta rename to test/unit/python/sample_files/write_init_files/theta_ddt.meta diff --git a/test/unit/sample_files/write_init_files/var_4D_reg.xml b/test/unit/python/sample_files/write_init_files/var_4D_reg.xml similarity index 100% rename from test/unit/sample_files/write_init_files/var_4D_reg.xml rename to test/unit/python/sample_files/write_init_files/var_4D_reg.xml diff --git a/test/unit/sample_files/write_init_files/var_bad_vertdim.xml b/test/unit/python/sample_files/write_init_files/var_bad_vertdim.xml similarity index 100% rename from test/unit/sample_files/write_init_files/var_bad_vertdim.xml rename to test/unit/python/sample_files/write_init_files/var_bad_vertdim.xml diff --git a/test/unit/test_atm_in_paramgen.py b/test/unit/python/test_atm_in_paramgen.py similarity index 99% rename from test/unit/test_atm_in_paramgen.py rename to test/unit/python/test_atm_in_paramgen.py index 69d0d353..5ea26d6c 100644 --- a/test/unit/test_atm_in_paramgen.py +++ b/test/unit/python/test_atm_in_paramgen.py @@ -30,7 +30,7 @@ #Add directory to python path: _TEST_DIR = os.path.abspath(os.path.dirname(__file__)) -_CAM_ROOT_DIR = os.path.join(_TEST_DIR, os.pardir, os.pardir) +_CAM_ROOT_DIR = os.path.join(_TEST_DIR, os.pardir, os.pardir, os.pardir) _CIME_CONF_DIR = os.path.abspath(os.path.join(_CAM_ROOT_DIR, "cime_config")) _SAMPLES_DIR = os.path.join(os.path.join(_TEST_DIR, "sample_files"), "atm_in_files") diff --git a/test/unit/test_build_cache.py b/test/unit/python/test_build_cache.py similarity index 99% rename from test/unit/test_build_cache.py rename to test/unit/python/test_build_cache.py index 39dc59d2..e211635e 100644 --- a/test/unit/test_build_cache.py +++ b/test/unit/python/test_build_cache.py @@ -32,7 +32,7 @@ #Add directory to python path: _CWD = os.getcwd() _CURRDIR = os.path.abspath(os.path.dirname(__file__)) -_CAM_ROOT_DIR = os.path.join(_CURRDIR, os.pardir, os.pardir) +_CAM_ROOT_DIR = os.path.join(_CURRDIR, os.pardir, os.pardir, os.pardir) _CAM_CONF_DIR = os.path.abspath(os.path.join(_CAM_ROOT_DIR, "cime_config")) _PRE_TMP_DIR = os.path.join(_CURRDIR, "tmp") _TMP_DIR = os.path.join(_PRE_TMP_DIR, "cam_build_cache") diff --git a/test/unit/test_cam_autogen.py b/test/unit/python/test_cam_autogen.py similarity index 99% rename from test/unit/test_cam_autogen.py rename to test/unit/python/test_cam_autogen.py index 4a786aab..c5317a65 100644 --- a/test/unit/test_cam_autogen.py +++ b/test/unit/python/test_cam_autogen.py @@ -32,7 +32,7 @@ #Add directory to python path: _CURRDIR = os.path.abspath(os.path.dirname(__file__)) -_CAM_ROOT_DIR = os.path.join(_CURRDIR, os.pardir, os.pardir) +_CAM_ROOT_DIR = os.path.join(_CURRDIR, os.pardir, os.pardir, os.pardir) _CAM_CONF_DIR = os.path.abspath(os.path.join(_CAM_ROOT_DIR, "cime_config")) _CCPP_DIR = os.path.join(_CAM_ROOT_DIR, "ccpp_framework", "scripts") diff --git a/test/unit/test_cam_config.py b/test/unit/python/test_cam_config.py similarity index 99% rename from test/unit/test_cam_config.py rename to test/unit/python/test_cam_config.py index bad32f5f..91fd9724 100644 --- a/test/unit/test_cam_config.py +++ b/test/unit/python/test_cam_config.py @@ -30,7 +30,7 @@ #Add directory to python path: CURRDIR = os.path.abspath(os.path.dirname(__file__)) -CAM_ROOT_DIR = os.path.join(CURRDIR, os.pardir, os.pardir) +CAM_ROOT_DIR = os.path.join(CURRDIR, os.pardir, os.pardir, os.pardir) CAM_CONF_DIR = os.path.abspath(os.path.join(CAM_ROOT_DIR, "cime_config")) #Add "cime_config" directory to python path: diff --git a/test/unit/test_create_readnl_files.py b/test/unit/python/test_create_readnl_files.py similarity index 98% rename from test/unit/test_create_readnl_files.py rename to test/unit/python/test_create_readnl_files.py index 5e92caf9..a6681743 100644 --- a/test/unit/test_create_readnl_files.py +++ b/test/unit/python/test_create_readnl_files.py @@ -22,7 +22,7 @@ import xml.etree.ElementTree as ET _TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_CAM_ROOT = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir)) +_CAM_ROOT = os.path.abspath(os.path.join(_TEST_DIR, os.pardir, os.pardir, os.pardir)) _CCPP_DIR = os.path.join(_CAM_ROOT, "ccpp_framework", "scripts") _CIME_CONFIG_DIR = os.path.join(_CAM_ROOT, "cime_config") _XML_SAMPLES_DIR = os.path.join(_TEST_DIR, "sample_files") @@ -409,18 +409,18 @@ def test_double_namelist_def(self): # Check logger lmsgs = [("INFO:test_double_namelist_def:Reading CAM physics " \ "namelist definition file, ", - "test/unit/sample_files/banana_namelist.xml'"), + "test/unit/python/sample_files/banana_namelist.xml'"), ("INFO:test_double_namelist_def:Writing metadata file, ", - "unit/tmp/namelist_files/banana_namelist.meta"), + "unit/python/tmp/namelist_files/banana_namelist.meta"), ("INFO:test_double_namelist_def:Writing Fortran module, ", - "unit/tmp/namelist_files/banana_namelist.F90"), + "unit/python/tmp/namelist_files/banana_namelist.F90"), ("INFO:test_double_namelist_def:Reading CAM physics " \ "namelist definition file, ", - "test/unit/sample_files/kumquat_namelist.xml'"), + "test/unit/python/sample_files/kumquat_namelist.xml'"), ("INFO:test_double_namelist_def:Writing metadata file, ", - "test/unit/tmp/namelist_files/kumquat_namelist.meta"), + "test/unit/python/tmp/namelist_files/kumquat_namelist.meta"), ("INFO:test_double_namelist_def:Writing Fortran module, ", - "test/unit/tmp/namelist_files/kumquat_namelist.F90")] + "test/unit/python/tmp/namelist_files/kumquat_namelist.F90")] comp_lmsgs = cmp_log.output amsg = "Test failure: Number of log output messages, " \ f"{len(comp_lmsgs)} does not match what is expected, " \ diff --git a/test/unit/test_hist_config.py b/test/unit/python/test_hist_config.py similarity index 99% rename from test/unit/test_hist_config.py rename to test/unit/python/test_hist_config.py index fa5435f6..57fad85f 100644 --- a/test/unit/test_hist_config.py +++ b/test/unit/python/test_hist_config.py @@ -20,7 +20,7 @@ import unittest __TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir)) +_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir, os.pardir)) __CIME_CONFIG_DIR = os.path.join(_CAM_ROOT, "cime_config") _SAMPLE_FILES_DIR = os.path.join(__TEST_DIR, "sample_files", "hist_config_files") diff --git a/test/unit/test_registry.py b/test/unit/python/test_registry.py similarity index 99% rename from test/unit/test_registry.py rename to test/unit/python/test_registry.py index 6fff5360..adde7797 100644 --- a/test/unit/test_registry.py +++ b/test/unit/python/test_registry.py @@ -22,7 +22,7 @@ import xml.etree.ElementTree as ET __TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir)) +_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir, os.pardir)) __REGISTRY_DIR = os.path.join(_CAM_ROOT, "src", "data") _SAMPLE_FILES_DIR = os.path.join(__TEST_DIR, "sample_files") _TMP_DIR = os.path.join(__TEST_DIR, "tmp") @@ -325,7 +325,7 @@ def test_diff_src_root_metadata_file_registry(self): remove_files([out_source, out_meta]) # Create new directory: - tmp_src_dir = os.path.join(_TMP_DIR, "test", "unit", \ + tmp_src_dir = os.path.join(_TMP_DIR, "test", "unit", "python", \ "sample_files") if not os.path.exists(tmp_src_dir): os.makedirs(tmp_src_dir) diff --git a/test/unit/test_write_init_files.py b/test/unit/python/test_write_init_files.py similarity index 99% rename from test/unit/test_write_init_files.py rename to test/unit/python/test_write_init_files.py index 903df86d..dd3154b1 100644 --- a/test/unit/test_write_init_files.py +++ b/test/unit/python/test_write_init_files.py @@ -20,7 +20,7 @@ import logging __TEST_DIR = os.path.dirname(os.path.abspath(__file__)) -_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir)) +_CAM_ROOT = os.path.abspath(os.path.join(__TEST_DIR, os.pardir, os.pardir, os.pardir)) __CCPP_DIR = os.path.join(_CAM_ROOT, "ccpp_framework", "scripts") __REGISTRY_DIR = os.path.join(_CAM_ROOT, "src", "data") _REG_SAMPLES_DIR = os.path.join(__TEST_DIR, "sample_files")