Skip to content

Commit

Permalink
Initial pFUnit Integration (#326)
Browse files Browse the repository at this point in the history
Tag name (required for release branches):
Originator(s): @mwaxmonsky 

Description (include the issue title, and the keyword ['closes',
'fixes', 'resolves'] followed by the issue number):

Describe any changes made to build system: None

Describe any changes made to the namelist: None

List any changes to the defaults for the input datasets (e.g. boundary
datasets): None

List all files eliminated and why: None

List all files added and what they do:
A       .github/workflows/fortran_unit_tests.yml
- Initial workflow file to support unit testing in CI

A       src/core_utils/CMakeLists.txt
A       test/unit-fortran/CMakeLists.txt
A       test/unit-fortran/src/core_utils/CMakeLists.txt
- Initial unit test build infrastructure.

A       src/core_utils/string_core_utils.F90
A       test/unit-fortran/src/core_utils/test_string_core_utils.pf
- Refactored string utils and unit tests without dependencies

List all existing files that have been modified, and describe the
changes:
(Helpful git command: `git diff --name-status
development...<your_branch_name>`)
R085	test/run_unit_tests.sh	test/run_python_unit_tests.sh
R100	test/unit/.coveragerc	test/unit/python/.coveragerc
R100 test/unit/sample_files/atm_in_files/test_attr_in
test/unit/python/sample_files/atm_in_files/test_attr_in
R100 test/unit/sample_files/atm_in_files/test_cmplx_array_atm_in
test/unit/python/sample_files/atm_in_files/test_cmplx_array_atm_in
R100 test/unit/sample_files/atm_in_files/test_extra_nml_def.xml
test/unit/python/sample_files/atm_in_files/test_extra_nml_def.xml
R100 test/unit/sample_files/atm_in_files/test_extra_nml_same_group.xml
test/unit/python/sample_files/atm_in_files/test_extra_nml_same_group.xml
R100 test/unit/sample_files/atm_in_files/test_extra_nml_same_var.xml
test/unit/python/sample_files/atm_in_files/test_extra_nml_same_var.xml
R100 test/unit/sample_files/atm_in_files/test_missing_elems.xml
test/unit/python/sample_files/atm_in_files/test_missing_elems.xml
R100 test/unit/sample_files/atm_in_files/test_multi_attr_in
test/unit/python/sample_files/atm_in_files/test_multi_attr_in
R100 test/unit/sample_files/atm_in_files/test_multi_xml_in
test/unit/python/sample_files/atm_in_files/test_multi_xml_in
R100 test/unit/sample_files/atm_in_files/test_nl_duplicate_atm_in
test/unit/python/sample_files/atm_in_files/test_nl_duplicate_atm_in
R100 test/unit/sample_files/atm_in_files/test_simple_atm_in
test/unit/python/sample_files/atm_in_files/test_simple_atm_in
R100 test/unit/sample_files/atm_in_files/test_simple_nml_def.xml
test/unit/python/sample_files/atm_in_files/test_simple_nml_def.xml
R100 test/unit/sample_files/atm_in_files/test_third_nml_def.xml
test/unit/python/sample_files/atm_in_files/test_third_nml_def.xml
R100 test/unit/sample_files/atm_in_files/test_user_in
test/unit/python/sample_files/atm_in_files/test_user_in
R100 test/unit/sample_files/atm_in_files/test_user_nl_allow_dupl_var
test/unit/python/sample_files/atm_in_files/test_user_nl_allow_dupl_var
R100 test/unit/sample_files/atm_in_files/test_user_nl_bad_equals
test/unit/python/sample_files/atm_in_files/test_user_nl_bad_equals
R100 test/unit/sample_files/atm_in_files/test_user_nl_dupl_var
test/unit/python/sample_files/atm_in_files/test_user_nl_dupl_var
R100 test/unit/sample_files/atm_in_files/test_user_nl_no_equals
test/unit/python/sample_files/atm_in_files/test_user_nl_no_equals
R100 test/unit/sample_files/atm_in_files/test_user_nl_simple
test/unit/python/sample_files/atm_in_files/test_user_nl_simple
R100 test/unit/sample_files/atm_in_files/test_user_nl_undefined_var
test/unit/python/sample_files/atm_in_files/test_user_nl_undefined_var
R100 test/unit/sample_files/autogen_files/two_scheme_banana.F90
test/unit/python/sample_files/autogen_files/two_scheme_banana.F90
R100 test/unit/sample_files/autogen_files/two_scheme_banana.meta
test/unit/python/sample_files/autogen_files/two_scheme_banana.meta
R100 test/unit/sample_files/autogen_files/two_scheme_banana_namelist.xml
test/unit/python/sample_files/autogen_files/two_scheme_banana_namelist.xml
R100 test/unit/sample_files/banana_namelist.xml
test/unit/python/sample_files/banana_namelist.xml
R100
test/unit/sample_files/build_cache_files/bad_ccpp_tag_build_cache.xml
test/unit/python/sample_files/build_cache_files/bad_ccpp_tag_build_cache.xml
R100
test/unit/sample_files/build_cache_files/bad_reg_tag_build_cache.xml
test/unit/python/sample_files/build_cache_files/bad_reg_tag_build_cache.xml
R100
test/unit/sample_files/build_cache_files/bad_section_tag_build_cache.xml
test/unit/python/sample_files/build_cache_files/bad_section_tag_build_cache.xml
R100 test/unit/sample_files/build_cache_files/example_build_cache.xml
test/unit/python/sample_files/build_cache_files/example_build_cache.xml
R100
test/unit/sample_files/build_cache_files/update_ccpp_build_cache.xml
test/unit/python/sample_files/build_cache_files/update_ccpp_build_cache.xml
R100
test/unit/sample_files/build_cache_files/update_init_gen_build_cache.xml
test/unit/python/sample_files/build_cache_files/update_init_gen_build_cache.xml
R100 test/unit/sample_files/build_cache_files/update_reg_build_cache.xml
test/unit/python/sample_files/build_cache_files/update_reg_build_cache.xml
R100 test/unit/sample_files/hist_config_files/amwg_hist_config
test/unit/python/sample_files/hist_config_files/amwg_hist_config
R100 test/unit/sample_files/hist_config_files/atm_in_flat
test/unit/python/sample_files/hist_config_files/atm_in_flat
R100 test/unit/sample_files/hist_config_files/atm_in_multi
test/unit/python/sample_files/hist_config_files/atm_in_multi
R100 test/unit/sample_files/hist_config_files/rad_config
test/unit/python/sample_files/hist_config_files/rad_config
R100 test/unit/sample_files/hist_config_files/user_nl_cam_flat
test/unit/python/sample_files/hist_config_files/user_nl_cam_flat
R100 test/unit/sample_files/hist_config_files/user_nl_cam_multi
test/unit/python/sample_files/hist_config_files/user_nl_cam_multi
R100 test/unit/sample_files/kumquat_namelist.xml
test/unit/python/sample_files/kumquat_namelist.xml
R100 test/unit/sample_files/namelist_files/banana_namelist.F90
test/unit/python/sample_files/namelist_files/banana_namelist.F90
R100 test/unit/sample_files/namelist_files/banana_namelist.meta
test/unit/python/sample_files/namelist_files/banana_namelist.meta
R100
test/unit/sample_files/namelist_files/cam_ccpp_scheme_namelists_double_def.F90
test/unit/python/sample_files/namelist_files/cam_ccpp_scheme_namelists_double_def.F90
R100
test/unit/sample_files/namelist_files/cam_ccpp_scheme_namelists_single_def.F90
test/unit/python/sample_files/namelist_files/cam_ccpp_scheme_namelists_single_def.F90
R100 test/unit/sample_files/namelist_files/kumquat_namelist.F90
test/unit/python/sample_files/namelist_files/kumquat_namelist.F90
R100 test/unit/sample_files/namelist_files/kumquat_namelist.meta
test/unit/python/sample_files/namelist_files/kumquat_namelist.meta
R100 test/unit/sample_files/phys_types_dup_section.meta
test/unit/python/sample_files/phys_types_dup_section.meta
R100 test/unit/sample_files/phys_types_no_table.meta
test/unit/python/sample_files/phys_types_no_table.meta
R100 test/unit/sample_files/physics_types_complete.F90
test/unit/python/sample_files/physics_types_complete.F90
R100 test/unit/sample_files/physics_types_complete.meta
test/unit/python/sample_files/physics_types_complete.meta
R100 test/unit/sample_files/physics_types_ddt2.F90
test/unit/python/sample_files/physics_types_ddt2.F90
R100 test/unit/sample_files/physics_types_ddt2.meta
test/unit/python/sample_files/physics_types_ddt2.meta
R100 test/unit/sample_files/physics_types_ddt_array.F90
test/unit/python/sample_files/physics_types_ddt_array.F90
R100 test/unit/sample_files/physics_types_ddt_array.meta
test/unit/python/sample_files/physics_types_ddt_array.meta
R100 test/unit/sample_files/physics_types_ddt_eul.F90
test/unit/python/sample_files/physics_types_ddt_eul.F90
R100 test/unit/sample_files/physics_types_ddt_eul.meta
test/unit/python/sample_files/physics_types_ddt_eul.meta
R100 test/unit/sample_files/physics_types_ddt_fv.F90
test/unit/python/sample_files/physics_types_ddt_fv.F90
R100 test/unit/sample_files/physics_types_ddt_fv.meta
test/unit/python/sample_files/physics_types_ddt_fv.meta
R100 test/unit/sample_files/physics_types_ddt_se.F90
test/unit/python/sample_files/physics_types_ddt_se.F90
R100 test/unit/sample_files/physics_types_ddt_se.meta
test/unit/python/sample_files/physics_types_ddt_se.meta
R100 test/unit/sample_files/physics_types_parameter.F90
test/unit/python/sample_files/physics_types_parameter.F90
R100 test/unit/sample_files/physics_types_parameter.meta
test/unit/python/sample_files/physics_types_parameter.meta
R100 test/unit/sample_files/physics_types_simple.F90
test/unit/python/sample_files/physics_types_simple.F90
R100 test/unit/sample_files/physics_types_simple.meta
test/unit/python/sample_files/physics_types_simple.meta
R100 test/unit/sample_files/ref_pres.meta
test/unit/python/sample_files/ref_pres.meta
R100 test/unit/sample_files/ref_pres_SourceMods.meta
test/unit/python/sample_files/ref_pres_SourceMods.meta
R098 test/unit/sample_files/reg_bad_xml.xml
test/unit/python/sample_files/reg_bad_xml.xml
R098 test/unit/sample_files/reg_good_complete.xml
test/unit/python/sample_files/reg_good_complete.xml
R100 test/unit/sample_files/reg_good_ddt.xml
test/unit/python/sample_files/reg_good_ddt.xml
R100 test/unit/sample_files/reg_good_ddt2.xml
test/unit/python/sample_files/reg_good_ddt2.xml
R100 test/unit/sample_files/reg_good_ddt_array.xml
test/unit/python/sample_files/reg_good_ddt_array.xml
R096 test/unit/sample_files/reg_good_mf.xml
test/unit/python/sample_files/reg_good_mf.xml
R100 test/unit/sample_files/reg_good_simple.xml
test/unit/python/sample_files/reg_good_simple.xml
R100 test/unit/sample_files/rotten_namelist.xml
test/unit/python/sample_files/rotten_namelist.xml
R100 test/unit/sample_files/write_init_files/ddt2_reg.xml
test/unit/python/sample_files/write_init_files/ddt2_reg.xml
R100 test/unit/sample_files/write_init_files/ddt_array_reg.xml
test/unit/python/sample_files/write_init_files/ddt_array_reg.xml
R100 test/unit/sample_files/write_init_files/ddt_reg.xml
test/unit/python/sample_files/write_init_files/ddt_reg.xml
R100 test/unit/sample_files/write_init_files/host_var_host.F90
test/unit/python/sample_files/write_init_files/host_var_host.F90
R100 test/unit/sample_files/write_init_files/host_var_host.meta
test/unit/python/sample_files/write_init_files/host_var_host.meta
R100 test/unit/sample_files/write_init_files/host_var_reg.xml
test/unit/python/sample_files/write_init_files/host_var_reg.xml
R088 test/unit/sample_files/write_init_files/mf_reg.xml
test/unit/python/sample_files/write_init_files/mf_reg.xml
R100 test/unit/sample_files/write_init_files/missing_ICs_reg.xml
test/unit/python/sample_files/write_init_files/missing_ICs_reg.xml
R100 test/unit/sample_files/write_init_files/no_horiz_dim_reg.xml
test/unit/python/sample_files/write_init_files/no_horiz_dim_reg.xml
R100 test/unit/sample_files/write_init_files/no_req_var_reg.xml
test/unit/python/sample_files/write_init_files/no_req_var_reg.xml
R100 test/unit/sample_files/write_init_files/param_reg.xml
test/unit/python/sample_files/write_init_files/param_reg.xml
R100 test/unit/sample_files/write_init_files/phys_vars_init_check_4D.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_4D.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_bvd.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_bvd.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_cnst.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_cnst.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_ddt.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_ddt2.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt2.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_ddt_array.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_host_var.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_host_var.F90
R100 test/unit/sample_files/write_init_files/phys_vars_init_check_mf.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_mf.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_no_horiz.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_noreq.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_noreq.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_param.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_param.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_parameter.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_parameter.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_protect.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_protect.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_scalar.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_scalar.F90
R100
test/unit/sample_files/write_init_files/phys_vars_init_check_simple.F90
test/unit/python/sample_files/write_init_files/phys_vars_init_check_simple.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_4D.F90
test/unit/python/sample_files/write_init_files/physics_inputs_4D.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_bvd.F90
test/unit/python/sample_files/write_init_files/physics_inputs_bvd.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_cnst.F90
test/unit/python/sample_files/write_init_files/physics_inputs_cnst.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_ddt.F90
test/unit/python/sample_files/write_init_files/physics_inputs_ddt.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_ddt2.F90
test/unit/python/sample_files/write_init_files/physics_inputs_ddt2.F90
R100
test/unit/sample_files/write_init_files/physics_inputs_ddt_array.F90
test/unit/python/sample_files/write_init_files/physics_inputs_ddt_array.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_host_var.F90
test/unit/python/sample_files/write_init_files/physics_inputs_host_var.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_mf.F90
test/unit/python/sample_files/write_init_files/physics_inputs_mf.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_no_horiz.F90
test/unit/python/sample_files/write_init_files/physics_inputs_no_horiz.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_noreq.F90
test/unit/python/sample_files/write_init_files/physics_inputs_noreq.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_param.F90
test/unit/python/sample_files/write_init_files/physics_inputs_param.F90
R100
test/unit/sample_files/write_init_files/physics_inputs_parameter.F90
test/unit/python/sample_files/write_init_files/physics_inputs_parameter.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_protect.F90
test/unit/python/sample_files/write_init_files/physics_inputs_protect.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_scalar.F90
test/unit/python/sample_files/write_init_files/physics_inputs_scalar.F90
R100 test/unit/sample_files/write_init_files/physics_inputs_simple.F90
test/unit/python/sample_files/write_init_files/physics_inputs_simple.F90
R100 test/unit/sample_files/write_init_files/protected_reg.xml
test/unit/python/sample_files/write_init_files/protected_reg.xml
R100 test/unit/sample_files/write_init_files/ref_theta.F90
test/unit/python/sample_files/write_init_files/ref_theta.F90
R100 test/unit/sample_files/write_init_files/ref_theta.meta
test/unit/python/sample_files/write_init_files/ref_theta.meta
R100 test/unit/sample_files/write_init_files/ref_two.F90
test/unit/python/sample_files/write_init_files/ref_two.F90
R100 test/unit/sample_files/write_init_files/ref_two.meta
test/unit/python/sample_files/write_init_files/ref_two.meta
R100 test/unit/sample_files/write_init_files/scalar_var_reg.xml
test/unit/python/sample_files/write_init_files/scalar_var_reg.xml
R100
test/unit/sample_files/write_init_files/simple_build_cache_template.xml
test/unit/python/sample_files/write_init_files/simple_build_cache_template.xml
R100 test/unit/sample_files/write_init_files/simple_host.F90
test/unit/python/sample_files/write_init_files/simple_host.F90
R100 test/unit/sample_files/write_init_files/simple_host.meta
test/unit/python/sample_files/write_init_files/simple_host.meta
R100 test/unit/sample_files/write_init_files/simple_reg.xml
test/unit/python/sample_files/write_init_files/simple_reg.xml
R100 test/unit/sample_files/write_init_files/suite_simple.xml
test/unit/python/sample_files/write_init_files/suite_simple.xml
R100 test/unit/sample_files/write_init_files/temp_adjust.F90
test/unit/python/sample_files/write_init_files/temp_adjust.F90
R100 test/unit/sample_files/write_init_files/temp_adjust.meta
test/unit/python/sample_files/write_init_files/temp_adjust.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_4D.F90
test/unit/python/sample_files/write_init_files/temp_adjust_4D.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_4D.meta
test/unit/python/sample_files/write_init_files/temp_adjust_4D.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_bvd.F90
test/unit/python/sample_files/write_init_files/temp_adjust_bvd.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_bvd.meta
test/unit/python/sample_files/write_init_files/temp_adjust_bvd.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_cnst.F90
test/unit/python/sample_files/write_init_files/temp_adjust_cnst.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_cnst.meta
test/unit/python/sample_files/write_init_files/temp_adjust_cnst.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_no_horiz.F90
test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_no_horiz.meta
test/unit/python/sample_files/write_init_files/temp_adjust_no_horiz.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_noreq.F90
test/unit/python/sample_files/write_init_files/temp_adjust_noreq.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_noreq.meta
test/unit/python/sample_files/write_init_files/temp_adjust_noreq.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_param.F90
test/unit/python/sample_files/write_init_files/temp_adjust_param.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_param.meta
test/unit/python/sample_files/write_init_files/temp_adjust_param.meta
R100 test/unit/sample_files/write_init_files/temp_adjust_scalar.F90
test/unit/python/sample_files/write_init_files/temp_adjust_scalar.F90
R100 test/unit/sample_files/write_init_files/temp_adjust_scalar.meta
test/unit/python/sample_files/write_init_files/temp_adjust_scalar.meta
R100 test/unit/sample_files/write_init_files/theta_ddt.F90
test/unit/python/sample_files/write_init_files/theta_ddt.F90
R100 test/unit/sample_files/write_init_files/theta_ddt.meta
test/unit/python/sample_files/write_init_files/theta_ddt.meta
R100 test/unit/sample_files/write_init_files/var_4D_reg.xml
test/unit/python/sample_files/write_init_files/var_4D_reg.xml
R100 test/unit/sample_files/write_init_files/var_bad_vertdim.xml
test/unit/python/sample_files/write_init_files/var_bad_vertdim.xml
R099 test/unit/test_atm_in_paramgen.py
test/unit/python/test_atm_in_paramgen.py
R099	test/unit/test_build_cache.py	test/unit/python/test_build_cache.py
R099	test/unit/test_cam_autogen.py	test/unit/python/test_cam_autogen.py
R099	test/unit/test_cam_config.py	test/unit/python/test_cam_config.py
R098 test/unit/test_create_readnl_files.py
test/unit/python/test_create_readnl_files.py
R099	test/unit/test_hist_config.py	test/unit/python/test_hist_config.py
R099	test/unit/test_registry.py	test/unit/python/test_registry.py
R099 test/unit/test_write_init_files.py
test/unit/python/test_write_init_files.py
- Moved python tests and associated files from top level test directory
to python specific directory to allow other language based tests.

M       src/utils/string_utils.F90
- Removed unused code (`last_sig_char`, `lower_to_upper`,
`upper_to_lower`, `last_index`, and `increment_string`), made formatting
consistent, and refactored portable string util code to core equivalent
so this layer handles non-portable code and then calls core util code.

M	.github/workflows/python_unit_tests.yml
M	.gitignore
M	cime_config/cam_autogen.py
-  Updated python test paths to new directory.

M	cime_config/buildlib
- Adding `core_utils` directory to list of source directories.

If there are new failures (compared to the
`test/existing-test-failures.txt` file),
have them OK'd by the gatekeeper, note them here, and add them to the
file.
If there are baseline differences, include the test and the reason for
the
diff. What is the nature of the change? Roundoff?

derecho/intel/aux_sima:

derecho/gnu/aux_sima:

If this changes climate describe any run(s) done to evaluate the new
climate in enough detail that it(they) could be reproduced:

CAM-SIMA date used for the baseline comparison tests if different than
latest:
  • Loading branch information
mwaxmonsky authored Feb 11, 2025
1 parent 2b81de7 commit 74b935d
Show file tree
Hide file tree
Showing 164 changed files with 640 additions and 378 deletions.
73 changes: 73 additions & 0 deletions .github/workflows/fortran_unit_tests.yml
Original file line number Diff line number Diff line change
@@ -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
7 changes: 4 additions & 3 deletions .github/workflows/python_unit_tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Python Unit Tests

on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened]
push:
Expand All @@ -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:
Expand Down Expand Up @@ -56,4 +57,4 @@ jobs:
pytest src/data --doctest-modules
# Run all python unit tests:
pytest test/unit
pytest test/unit/python
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion cime_config/buildlib
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion cime_config/cam_autogen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
9 changes: 9 additions & 0 deletions src/core_utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})

162 changes: 162 additions & 0 deletions src/core_utils/string_core_utils.F90
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 74b935d

Please sign in to comment.