From 4e2c4bc03d8c4187a676da439f2c04689ad8917a Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Wed, 6 Jul 2022 18:25:40 -0600 Subject: [PATCH 01/35] per #685, added custom pytest markers for many tests so we can run groups of tests in automation to speed things up. removed unused imports from tests and removed parenthesis from assert statements when they are not needed --- .../pytests/ascii2nc/test_ascii2nc_wrapper.py | 15 +- .../command_builder/test_command_builder.py | 52 +++++- .../compare_gridded/test_compare_gridded.py | 60 ++----- .../test_ensemble_stat_wrapper.py | 21 ++- .../extract_tiles/test_extract_tiles.py | 29 ++- .../gen_ens_prod/test_gen_ens_prod_wrapper.py | 20 +-- .../pytests/gen_vx_mask/test_gen_vx_mask.py | 44 +---- .../pytests/grid_diag/test_grid_diag.py | 25 +-- .../grid_stat/test_grid_stat_wrapper.py | 25 ++- .../pytests/ioda2nc/test_ioda2nc_wrapper.py | 12 +- .../pytests/met_db_load/test_met_db_load.py | 4 + .../pytests/mode/test_mode_wrapper.py | 18 +- .../pytests/mtd/test_mtd_wrapper.py | 16 +- .../pytests/pb2nc/test_pb2nc_wrapper.py | 54 ++---- .../pcp_combine/test_pcp_combine_wrapper.py | 42 ++++- .../make_plots/test_make_plots_wrapper.py | 70 +------- .../plotting/plot_util/test_plot_util.py | 57 +++--- .../pytests/point2grid/test_point2grid.py | 37 +--- .../point_stat/test_point_stat_wrapper.py | 20 ++- internal_tests/pytests/produtil/README | 23 --- .../pytests/produtil/produtil_test.conf | 31 ---- .../pytests/produtil/test_produtil.py | 144 --------------- ...rk_in_progress_test_produtil_regression.py | 159 ----------------- internal_tests/pytests/pytest.ini | 4 + .../test_regrid_data_plane.py | 57 ++---- .../pytests/runtime_freq/test_runtime_freq.py | 15 +- .../series_analysis/test_series_analysis.py | 72 +++++--- .../stat_analysis/test_stat_analysis.py | 165 +++++++----------- .../test_stat_analysis_plotting.py | 140 ++------------- .../pytests/tc_gen/test_tc_gen_wrapper.py | 25 +-- .../pytests/tc_pairs/test_tc_pairs_wrapper.py | 42 +++-- .../pytests/tc_stat/test_tc_stat_wrapper.py | 20 ++- .../tcmpr_plotter/test_tcmpr_plotter.py | 14 +- .../pytests/user_script/test_user_script.py | 14 +- .../pytests/{ => util}/config/config_1.conf | 0 .../pytests/{ => util}/config/config_2.conf | 0 .../pytests/{ => util}/config/config_3.conf | 0 .../pytests/{ => util}/config/test_config.py | 3 +- .../config_metplus/test_config_metplus.py | 1 + .../{ => util}/logging/test_logging.py | 5 +- .../{ => util}/met_config/test_met_config.py | 0 .../{ => util}/met_util/test_met_util.py | 0 .../metplus_check/test_metplus_check.py | 0 .../test_string_template_substitution.py | 0 .../{ => util}/time_util/test_time_util.py | 0 45 files changed, 521 insertions(+), 1034 deletions(-) delete mode 100644 internal_tests/pytests/produtil/README delete mode 100644 internal_tests/pytests/produtil/produtil_test.conf delete mode 100644 internal_tests/pytests/produtil/test_produtil.py delete mode 100644 internal_tests/pytests/produtil/work_in_progress_test_produtil_regression.py create mode 100644 internal_tests/pytests/pytest.ini rename internal_tests/pytests/{ => util}/config/config_1.conf (100%) rename internal_tests/pytests/{ => util}/config/config_2.conf (100%) rename internal_tests/pytests/{ => util}/config/config_3.conf (100%) rename internal_tests/pytests/{ => util}/config/test_config.py (99%) rename internal_tests/pytests/{ => util}/config_metplus/test_config_metplus.py (99%) rename internal_tests/pytests/{ => util}/logging/test_logging.py (98%) rename internal_tests/pytests/{ => util}/met_config/test_met_config.py (100%) rename internal_tests/pytests/{ => util}/met_util/test_met_util.py (100%) rename internal_tests/pytests/{ => util}/metplus_check/test_metplus_check.py (100%) rename internal_tests/pytests/{StringTemplateSubstitution => util/string_template_substitution}/test_string_template_substitution.py (100%) rename internal_tests/pytests/{ => util}/time_util/test_time_util.py (100%) diff --git a/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py b/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py index e746de6be9..f492f1f349 100644 --- a/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py +++ b/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py @@ -1,18 +1,11 @@ #!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -import datetime -import produtil +import os from metplus.wrappers.ascii2nc_wrapper import ASCII2NCWrapper -from metplus.util import met_util as util -from metplus.util import time_util + def ascii2nc_wrapper(metplus_config, config_path=None, config_overrides=None): config = metplus_config() @@ -37,6 +30,7 @@ def ascii2nc_wrapper(metplus_config, config_path=None, config_overrides=None): return ASCII2NCWrapper(config, instance=instance) + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ ({}, @@ -145,6 +139,7 @@ def ascii2nc_wrapper(metplus_config, config_path=None, config_overrides=None): ] ) +@pytest.mark.wrapper def test_ascii2nc_wrapper(metplus_config, config_overrides, env_var_values): wrapper = ( @@ -192,6 +187,8 @@ def test_ascii2nc_wrapper(metplus_config, config_overrides, assert (env_var_values.get(env_var_key, '') == value) + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' config = metplus_config() diff --git a/internal_tests/pytests/command_builder/test_command_builder.py b/internal_tests/pytests/command_builder/test_command_builder.py index ab2c622726..024c53db73 100644 --- a/internal_tests/pytests/command_builder/test_command_builder.py +++ b/internal_tests/pytests/command_builder/test_command_builder.py @@ -8,9 +8,7 @@ from metplus.wrappers.command_builder import CommandBuilder from metplus.util import ti_calculate -# ------------------------ -# test_find_data_no_dated -# ------------------------ + @pytest.mark.parametrize( 'data_type', [ ("FCST_"), @@ -19,6 +17,7 @@ ("MASK_"), ] ) +@pytest.mark.wrapper def test_find_data_no_dated(metplus_config, data_type): config = metplus_config() @@ -49,6 +48,7 @@ def test_find_data_no_dated(metplus_config, data_type): ("MASK_"), ] ) +@pytest.mark.wrapper def test_find_data_not_a_path(metplus_config, data_type): config = metplus_config() @@ -65,6 +65,8 @@ def test_find_data_not_a_path(metplus_config, data_type): obs_file = pcw.find_data(time_info, var_info=None, data_type=data_type) assert obs_file == 'G003' + +@pytest.mark.wrapper def test_find_obs_no_dated(metplus_config): config = metplus_config() @@ -83,6 +85,8 @@ def test_find_obs_no_dated(metplus_config): obs_file = pcw.find_obs(time_info, v) assert obs_file == pcw.c_dict['OBS_INPUT_DIR'] + '/20180201_0045' + +@pytest.mark.wrapper def test_find_obs_dated(metplus_config): config = metplus_config() @@ -101,6 +105,7 @@ def test_find_obs_dated(metplus_config): obs_file = pcw.find_obs(time_info, v) assert obs_file == pcw.c_dict['OBS_INPUT_DIR']+'/20180201/20180201_0013' + @pytest.mark.parametrize( 'offsets, expected_file, offset_seconds', [ ([2], '14z.prepbufr.tm02.20200201', 7200), @@ -111,6 +116,7 @@ def test_find_obs_dated(metplus_config): ([], None, None), ] ) +@pytest.mark.wrapper def test_find_obs_offset(metplus_config, offsets, expected_file, offset_seconds): config = metplus_config() @@ -136,6 +142,8 @@ def test_find_obs_offset(metplus_config, offsets, expected_file, offset_seconds) assert (os.path.basename(obs_file) == expected_file and time_info['offset'] == offset_seconds) + +@pytest.mark.wrapper def test_find_obs_dated_previous_day(metplus_config): config = metplus_config() @@ -154,6 +162,8 @@ def test_find_obs_dated_previous_day(metplus_config): obs_file = pcw.find_obs(time_info, v) assert obs_file == pcw.c_dict['OBS_INPUT_DIR']+'/20180131/20180131_2345' + +@pytest.mark.wrapper def test_find_obs_dated_next_day(metplus_config): config = metplus_config() @@ -172,6 +182,7 @@ def test_find_obs_dated_next_day(metplus_config): obs_file = pcw.find_obs(time_info, v) assert obs_file == pcw.c_dict['OBS_INPUT_DIR']+'/20180202/20180202_0013' + # dictionary items with values will be set in [test_section] # items with value None will not be set, so it should use # the value in [config], which is always 'default' @@ -191,6 +202,7 @@ def test_find_obs_dated_next_day(metplus_config): ({'FAKE_TEMPLATE': None}), ] ) +@pytest.mark.wrapper def test_override_by_instance(metplus_config, section_items): config = metplus_config() @@ -209,6 +221,7 @@ def test_override_by_instance(metplus_config, section_items): expected_value = 'default' if value is None else value assert pcw.config.getraw('config', key) == expected_value + @pytest.mark.parametrize( 'filename, file_list, output_dir', [ # write lists to staging dir @@ -220,6 +233,7 @@ def test_override_by_instance(metplus_config, section_items): ('my_ascii_file3', [], 'write_list_test'), ] ) +@pytest.mark.wrapper def test_write_list_file(metplus_config, filename, file_list, output_dir): config = metplus_config() cbw = CommandBuilder(config) @@ -254,6 +268,7 @@ def test_write_list_file(metplus_config, filename, file_list, output_dir): for actual_line, expected_line in zip(lines[1:], file_list): assert actual_line.strip() == expected_line + @pytest.mark.parametrize( 'config_overrides, expected_value', [ ({}, ''), @@ -268,6 +283,7 @@ def test_write_list_file(metplus_config, filename, file_list, output_dir): 'GRID_STAT_DESC': '"gs_desc"'}, 'desc = "gs_desc";'), ] ) +@pytest.mark.wrapper def test_handle_description(metplus_config, config_overrides, expected_value): config = metplus_config() @@ -283,6 +299,7 @@ def test_handle_description(metplus_config, config_overrides, expected_value): cbw.handle_description() assert cbw.env_var_dict.get('METPLUS_DESC', '') == expected_value + @pytest.mark.parametrize( 'config_overrides, set_to_grid, expected_dict', [ ({}, True, {'REGRID_TO_GRID': 'NONE'}), @@ -307,6 +324,7 @@ def test_handle_description(metplus_config, config_overrides, expected_value): {'REGRID_TO_GRID': 'NONE'}), ] ) +@pytest.mark.wrapper def test_handle_regrid_old(metplus_config, config_overrides, set_to_grid, expected_dict): config = metplus_config() @@ -329,6 +347,7 @@ def test_handle_regrid_old(metplus_config, config_overrides, set_to_grid, for key, value in expected_dict.items(): assert c_dict.get(key, '') == value + @pytest.mark.parametrize( 'config_overrides, expected_output', [ ({}, ''), @@ -355,6 +374,7 @@ def test_handle_regrid_old(metplus_config, config_overrides, set_to_grid, 'vld_thresh = 0.8;shape = CIRCLE;}')), ] ) +@pytest.mark.wrapper def test_handle_regrid_new(metplus_config, config_overrides, expected_output): config = metplus_config() @@ -370,6 +390,7 @@ def test_handle_regrid_new(metplus_config, config_overrides, expected_output): cbw.handle_regrid(cbw.c_dict) assert cbw.env_var_dict['METPLUS_REGRID_DICT'] == expected_output + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,remove_quotes,expected_output', [ # var is set, use quotes @@ -389,6 +410,7 @@ def test_handle_regrid_new(metplus_config, config_overrides, expected_output): True, 'test_string_1 = value_1;'), ] ) +@pytest.mark.wrapper def test_add_met_config_string(metplus_config, mp_config_name, met_config_name, c_dict_key, remove_quotes, expected_output): cbw = CommandBuilder(metplus_config()) @@ -413,6 +435,7 @@ def test_add_met_config_string(metplus_config, mp_config_name, met_config_name, assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,uppercase,expected_output, is_ok', [ # var is set to True, not uppercase @@ -441,6 +464,7 @@ def test_add_met_config_string(metplus_config, mp_config_name, met_config_name, True, '', False), ] ) +@pytest.mark.wrapper def test_add_met_config_bool(metplus_config, mp_config_name, met_config_name, c_dict_key, uppercase, expected_output, is_ok): cbw = CommandBuilder(metplus_config()) @@ -468,7 +492,7 @@ def test_add_met_config_bool(metplus_config, mp_config_name, met_config_name, assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output assert cbw.isOK == is_ok -# int + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,expected_output,is_ok', [ # var is set to positive int @@ -488,6 +512,7 @@ def test_add_met_config_bool(metplus_config, mp_config_name, met_config_name, '', False), ] ) +@pytest.mark.wrapper def test_add_met_config_int(metplus_config, mp_config_name, met_config_name, c_dict_key, expected_output, is_ok): cbw = CommandBuilder(metplus_config()) @@ -510,6 +535,7 @@ def test_add_met_config_int(metplus_config, mp_config_name, met_config_name, assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output assert cbw.isOK == is_ok + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,expected_output,is_ok', [ # var is set to float @@ -529,6 +555,7 @@ def test_add_met_config_int(metplus_config, mp_config_name, met_config_name, '', False), ] ) +@pytest.mark.wrapper def test_add_met_config_float(metplus_config, mp_config_name, met_config_name, c_dict_key, expected_output, is_ok): cbw = CommandBuilder(metplus_config()) @@ -551,6 +578,7 @@ def test_add_met_config_float(metplus_config, mp_config_name, met_config_name, assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output assert cbw.isOK == is_ok + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,expected_output,is_ok', [ # var is set to alphabet threshold @@ -576,6 +604,7 @@ def test_add_met_config_float(metplus_config, mp_config_name, met_config_name, 'test_thresh_6 = NA;', True), ] ) +@pytest.mark.wrapper def test_add_met_config_thresh(metplus_config, mp_config_name, met_config_name, c_dict_key, expected_output, is_ok): cbw = CommandBuilder(metplus_config()) @@ -601,6 +630,7 @@ def test_add_met_config_thresh(metplus_config, mp_config_name, met_config_name, assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output assert cbw.isOK == is_ok + @pytest.mark.parametrize( 'mp_config_name,met_config_name,c_dict_key,remove_quotes,expected_output', [ # var is set, use quotes @@ -626,6 +656,7 @@ def test_add_met_config_thresh(metplus_config, mp_config_name, met_config_name, True, 'test_list_4 = [value_1, value2];'), ] ) +@pytest.mark.wrapper def test_add_met_config_list(metplus_config, mp_config_name, met_config_name, c_dict_key, remove_quotes, expected_output): cbw = CommandBuilder(metplus_config()) @@ -653,6 +684,7 @@ def test_add_met_config_list(metplus_config, mp_config_name, met_config_name, print(f"KEY: {key}, ENV VARS: {cbw.env_var_dict}") assert cbw.env_var_dict.get(f'METPLUS_{key}', '') == expected_output + @pytest.mark.parametrize( 'mp_config_name,allow_empty,expected_output', [ # var is set to empty, don't set @@ -665,6 +697,7 @@ def test_add_met_config_list(metplus_config, mp_config_name, met_config_name, ('TEST_LIST_2', True, ''), ] ) +@pytest.mark.wrapper def test_add_met_config_list_allow_empty(metplus_config, mp_config_name, allow_empty, expected_output): cbw = CommandBuilder(metplus_config()) @@ -684,8 +717,9 @@ def test_add_met_config_list_allow_empty(metplus_config, mp_config_name, extra_args=extra_args) assert cbw.env_var_dict.get(f'METPLUS_{mp_config_name}', '') == expected_output - #assert c_dict.get(mp_config_name, '') == expected_output + +@pytest.mark.wrapper def test_add_met_config_dict(metplus_config): dict_name = 'fcst_hr_window' beg = -3 @@ -708,6 +742,8 @@ def test_add_met_config_dict(metplus_config): actual_value = cbw.env_var_dict.get('METPLUS_FCST_HR_WINDOW_DICT') assert actual_value == expected_value + +@pytest.mark.wrapper def test_add_met_config_window(metplus_config): dict_name = 'fcst_hr_window' beg = -3 @@ -725,6 +761,8 @@ def test_add_met_config_window(metplus_config): actual_value = cbw.env_var_dict.get('METPLUS_FCST_HR_WINDOW_DICT') assert actual_value == expected_value + +@pytest.mark.wrapper def test_add_met_config(metplus_config): config = metplus_config() value = 5 @@ -738,6 +776,8 @@ def test_add_met_config(metplus_config): expected_value = f'valid_freq = {value};' assert cbw.env_var_dict['METPLUS_VALID_FREQ'] == expected_value + +@pytest.mark.wrapper def test_add_met_config_dict_nested(metplus_config): dict_name = 'outer' beg = -3 @@ -770,6 +810,7 @@ def test_add_met_config_dict_nested(metplus_config): print(f"env_var_dict: {cbw.env_var_dict}") assert cbw.env_var_dict.get('METPLUS_OUTER_DICT') == expected_value + @pytest.mark.parametrize( 'extra, expected_value', [ # trailing semi-colon should be added at end automatically @@ -781,6 +822,7 @@ def test_add_met_config_dict_nested(metplus_config): "'name=\"name\"; level=\"(*,*)\"; file_type = NETCDF_NCCF; other_opt = \"opt\";'"), ] ) +@pytest.mark.wrapper def test_get_field_info_extra(metplus_config, extra, expected_value): d_type = 'FCST' name = 'name' diff --git a/internal_tests/pytests/compare_gridded/test_compare_gridded.py b/internal_tests/pytests/compare_gridded/test_compare_gridded.py index 0bc93453fe..82b4fa2059 100644 --- a/internal_tests/pytests/compare_gridded/test_compare_gridded.py +++ b/internal_tests/pytests/compare_gridded/test_compare_gridded.py @@ -1,40 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -import datetime -import produtil +import os +import datetime from metplus.wrappers.compare_gridded_wrapper import CompareGriddedWrapper -from metplus.util import met_util as util -from metplus.util import time_util - -# --------------------TEST CONFIGURATION and FIXTURE SUPPORT ------------- -# -# The test configuration and fixture support the additional configuration -# files used in METplus -# !!!!!!!!!!!!!!! -# !!!IMPORTANT!!! -# !!!!!!!!!!!!!!! -# The following two methods should be included in ALL pytest tests for METplus. -# -# -#def pytest_addoption(parser): -# parser.addoption("-c", action="store", help=" -c ") - - -# @pytest.fixture -#def cmdopt(request): -# return request.config.getoption("-c") - - -# -----------------FIXTURES THAT CAN BE USED BY ALL TESTS---------------- -#@pytest.fixture + def compare_gridded_wrapper(metplus_config): """! Returns a default GridStatWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -44,13 +16,7 @@ def compare_gridded_wrapper(metplus_config): config = metplus_config() return CompareGriddedWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- -# ------------------------ -# test_get_field_info_no_prob -# ------------------------ -# key is a list of inputs to get_field_info: name, level, thresh_list, extras, and data type (FCST/OBS) -# value is a list of field info lists that are generated from the keys @pytest.mark.parametrize( 'key, value', [ # forecast name and level @@ -95,7 +61,7 @@ def compare_gridded_wrapper(metplus_config): ] ) - +@pytest.mark.wrapper def test_get_field_info_no_prob(metplus_config, key, value): w = compare_gridded_wrapper(metplus_config) w.c_dict['FCST_IS_PROB'] = False @@ -111,12 +77,7 @@ def test_get_field_info_no_prob(metplus_config, key, value): fields = w.get_field_info(**field_dict) assert(fields == value) -# ------------------------ -# test_get_field_info_fcst_prob_grib_pds -# - forecast is grib probabalistic but observation is not -# ------------------------ -# key is a list of inputs to get_field_info: name, level, thresh_list, extras, and data type (FCST/OBS) -# value is a list of field info lists that are generated from the keys + @pytest.mark.parametrize( 'key, value', [ # forecast grib name level thresh @@ -142,6 +103,7 @@ def test_get_field_info_no_prob(metplus_config, key, value): ] ) +@pytest.mark.wrapper def test_get_field_info_fcst_prob_grib_pds(metplus_config, key, value): w = compare_gridded_wrapper(metplus_config) w.c_dict['FCST_IS_PROB'] = True @@ -160,6 +122,7 @@ def test_get_field_info_fcst_prob_grib_pds(metplus_config, key, value): fields = w.get_field_info(**field_dict) assert(fields == value) + # ------------------------ # test_get_field_info_fcst_prob_grib_non_pds # - forecast is grib probabalistic but observation is not @@ -191,6 +154,7 @@ def test_get_field_info_fcst_prob_grib_pds(metplus_config, key, value): ] ) +@pytest.mark.wrapper def test_get_field_info_fcst_prob_grib_non_pds(metplus_config, key, value): w = compare_gridded_wrapper(metplus_config) w.c_dict['FCST_IS_PROB'] = True @@ -209,7 +173,8 @@ def test_get_field_info_fcst_prob_grib_non_pds(metplus_config, key, value): fields = w.get_field_info(**field_dict) assert(fields == value) - # ------------------------ + +# ------------------------ # test_get_field_info_fcst_prob # - forecast is probabalistic but observation is not # ------------------------ @@ -227,6 +192,7 @@ def test_get_field_info_fcst_prob_grib_non_pds(metplus_config, key, value): ['{ name=\"NAME\"; level=\"L0\"; cat_thresh=[ gt3 ]; }']), ] ) +@pytest.mark.wrapper def test_get_field_info_fcst_prob_netcdf(metplus_config, key, value): w = compare_gridded_wrapper(metplus_config) w.c_dict['FCST_IS_PROB'] = True @@ -243,6 +209,7 @@ def test_get_field_info_fcst_prob_netcdf(metplus_config, key, value): fields = w.get_field_info(**field_dict) assert(fields == value) + @pytest.mark.parametrize( 'win, app_win, file_win, app_file_win, win_value, file_win_value', [ ([1, 2, 3, 4, 2, 4]), @@ -254,6 +221,7 @@ def test_get_field_info_fcst_prob_netcdf(metplus_config, key, value): ([1, None, 3, 4, 1, 4]), ] ) +@pytest.mark.wrapper def test_handle_window_once(metplus_config, win, app_win, file_win, app_file_win, win_value, file_win_value): cgw = compare_gridded_wrapper(metplus_config) config = cgw.config diff --git a/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py index c42f7a86d8..5dab906682 100644 --- a/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py @@ -1,18 +1,13 @@ #!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest + +import os + from datetime import datetime -import produtil from metplus.wrappers.ensemble_stat_wrapper import EnsembleStatWrapper -from metplus.util import met_util as util -from metplus.util import time_util fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' @@ -31,6 +26,7 @@ time_fmt = '%Y%m%d%H' run_times = ['2005080700', '2005080712'] + def set_minimum_config_settings(config, set_fields=True): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -67,6 +63,7 @@ def set_minimum_config_settings(config, set_fields=True): config.set('config', 'ENS_VAR1_NAME', ens_name) config.set('config', 'ENS_VAR1_LEVELS', ens_level) + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ # 0 : 3 ens, 1 fcst, 1 obs @@ -109,6 +106,7 @@ def set_minimum_config_settings(config, set_fields=True): }), ] ) +@pytest.mark.wrapper def test_ensemble_stat_field_info(metplus_config, config_overrides, env_var_values): @@ -160,6 +158,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) +@pytest.mark.wrapper def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -193,6 +192,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, expected_value = expected_value.replace('YMDH', ymdh) assert(expected_value == actual_value) + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ ({'MODEL': 'my_model'}, @@ -666,6 +666,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) +@pytest.mark.wrapper def test_ensemble_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -721,6 +722,8 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, else: assert(env_var_values.get(env_var_key, '') == actual_value) + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' @@ -736,12 +739,14 @@ def test_get_config_file(metplus_config): wrapper = EnsembleStatWrapper(config) assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name + @pytest.mark.parametrize( 'config_overrides, expected_num_files', [ ({}, 4), ({'ENSEMBLE_STAT_ENS_MEMBER_IDS': '1'}, 1), ] ) +@pytest.mark.wrapper def test_ensemble_stat_fill_missing(metplus_config, config_overrides, expected_num_files): config = metplus_config() diff --git a/internal_tests/pytests/extract_tiles/test_extract_tiles.py b/internal_tests/pytests/extract_tiles/test_extract_tiles.py index bfc78ae128..f4b1f1ed6f 100644 --- a/internal_tests/pytests/extract_tiles/test_extract_tiles.py +++ b/internal_tests/pytests/extract_tiles/test_extract_tiles.py @@ -1,22 +1,20 @@ # !/usr/bin/env python3 -import sys -import os -import datetime -import logging -import re import pytest -import produtil +import os +import datetime from metplus.wrappers.extract_tiles_wrapper import ExtractTilesWrapper + def get_config(metplus_config): extra_configs = [] extra_configs.append(os.path.join(os.path.dirname(__file__), 'extract_tiles_test.conf')) return metplus_config(extra_configs) + def extract_tiles_wrapper(metplus_config): config = get_config(metplus_config) @@ -24,6 +22,7 @@ def extract_tiles_wrapper(metplus_config): wrapper = ExtractTilesWrapper(config) return wrapper + def get_storm_lines(wrapper): filter_file = os.path.join(wrapper.config.getdir('METPLUS_BASE'), 'internal_tests', @@ -32,6 +31,7 @@ def get_storm_lines(wrapper): 'fake_filter_20141214_00.tcst') return get_input_lines(filter_file) + def get_mtd_lines(wrapper): input_file = os.path.join(wrapper.config.getdir('METPLUS_BASE'), 'internal_tests', @@ -40,11 +40,13 @@ def get_mtd_lines(wrapper): 'fake_mtd_2d.txt') return get_input_lines(input_file) + def get_input_lines(filepath): with open(filepath, 'r') as file_handle: lines = file_handle.readlines() return lines + @pytest.mark.parametrize( 'object_cats, expected_indices', [ # 0: No object cats provided @@ -57,10 +59,12 @@ def get_input_lines(filepath): (['CF002', 'CF001', 'CO002', 'CO001'], ['001', '002']), ] ) +@pytest.mark.wrapper def test_get_object_indices(metplus_config, object_cats, expected_indices): wrapper = extract_tiles_wrapper(metplus_config) assert wrapper.get_object_indices(object_cats) == expected_indices + @pytest.mark.parametrize( 'header_name, index', [ ('VALID', 10), @@ -74,12 +78,14 @@ def test_get_object_indices(metplus_config, object_cats, expected_indices): ] ) +@pytest.mark.wrapper def test_get_header_indices(metplus_config,header_name, index): wrapper = extract_tiles_wrapper(metplus_config) header = get_storm_lines(wrapper)[0] idx_dict = wrapper.get_header_indices(header) assert(idx_dict[header_name] == index) + @pytest.mark.parametrize( 'header_name, index', [ ('CENTROID_LAT', 28), @@ -91,12 +97,14 @@ def test_get_header_indices(metplus_config,header_name, index): ('OBJECT_ID', 22), ] ) +@pytest.mark.wrapper def test_get_header_indices_mtd(metplus_config, header_name, index): wrapper = extract_tiles_wrapper(metplus_config) header = get_mtd_lines(wrapper)[0] idx_dict = wrapper.get_header_indices(header, 'MTD') assert(idx_dict[header_name] == index) + @pytest.mark.parametrize( 'header_name, value', [ ('VALID', '20141214_060000'), @@ -110,6 +118,7 @@ def test_get_header_indices_mtd(metplus_config, header_name, index): ] ) +@pytest.mark.wrapper def test_get_data_from_track_line(metplus_config, header_name, value): wrapper = extract_tiles_wrapper(metplus_config) storm_lines = get_storm_lines(wrapper) @@ -118,6 +127,7 @@ def test_get_data_from_track_line(metplus_config, header_name, value): storm_data = wrapper.get_data_from_track_line(idx_dict, storm_lines[2]) assert(storm_data[header_name] == value) + @pytest.mark.parametrize( 'header_name, value', [ ('CENTROID_LAT', '37.26'), @@ -129,6 +139,7 @@ def test_get_data_from_track_line(metplus_config, header_name, value): ('OBJECT_ID', 'F001'), ] ) +@pytest.mark.wrapper def test_get_data_from_track_line_mtd(metplus_config, header_name, value): wrapper = extract_tiles_wrapper(metplus_config) storm_lines = get_mtd_lines(wrapper) @@ -137,6 +148,8 @@ def test_get_data_from_track_line_mtd(metplus_config, header_name, value): storm_data = wrapper.get_data_from_track_line(idx_dict, storm_lines[2]) assert(storm_data[header_name] == value) + +@pytest.mark.wrapper def test_set_time_info_from_track_data(metplus_config): storm_id = 'ML1221072014' wrapper = extract_tiles_wrapper(metplus_config) @@ -153,22 +166,26 @@ def test_set_time_info_from_track_data(metplus_config): for key, value in expected_time_info.items(): assert(time_info[key] == value) + @pytest.mark.parametrize( 'lat, lon, expected_result', [ (-54.9, -168.6, 'latlon 60 60 -70.0 -183.5 0.5 0.5'), ] ) +@pytest.mark.wrapper def test_get_grid_info(metplus_config, lat, lon, expected_result): wrapper = extract_tiles_wrapper(metplus_config) assert(wrapper.get_grid_info(lat, lon, 'FCST') == expected_result) + @pytest.mark.parametrize( 'lat, lon, expected_result', [ (-54.9, -168.6, 'latlon 60 60 -70.0 -183.5 0.5 0.5'), ] ) +@pytest.mark.wrapper def test_get_grid(metplus_config, lat, lon, expected_result): wrapper = extract_tiles_wrapper(metplus_config) storm_data = {'ALAT': lat, 'ALON': lon} diff --git a/internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py b/internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py index d9c2648343..87b7bad762 100644 --- a/internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py +++ b/internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py @@ -1,18 +1,10 @@ #!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -from datetime import datetime -import produtil +import os from metplus.wrappers.gen_ens_prod_wrapper import GenEnsProdWrapper -from metplus.util import met_util as util -from metplus.util import time_util ens_name = 'REFC' ens_level = 'L0' @@ -21,6 +13,7 @@ time_fmt = '%Y%m%d%H' run_times = ['2009123112', '2009123118'] + def set_minimum_config_settings(config): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -51,6 +44,7 @@ def set_minimum_config_settings(config): config.set('config', 'ENS_VAR1_NAME', ens_name) config.set('config', 'ENS_VAR1_LEVELS', ens_level) + def handle_input_dir(config): test_data_dir = os.path.join(config.getdir('METPLUS_BASE'), 'internal_tests', @@ -60,6 +54,7 @@ def handle_input_dir(config): config.set('config', 'GEN_ENS_PROD_CTRL_INPUT_DIR', input_dir) return input_dir + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ # 0 @@ -363,8 +358,9 @@ def handle_input_dir(config): ] ) +@pytest.mark.wrapper def test_gen_ens_prod_single_field(metplus_config, config_overrides, - env_var_values): + env_var_values): config = metplus_config() @@ -435,12 +431,14 @@ def test_gen_ens_prod_single_field(metplus_config, config_overrides, else: assert(env_var_values.get(env_var_key, '') == actual_value) + @pytest.mark.parametrize( 'use_default_config_file', [ True, False, ] ) +@pytest.mark.wrapper def test_get_config_file(metplus_config, use_default_config_file): config = metplus_config() @@ -455,12 +453,14 @@ def test_get_config_file(metplus_config, use_default_config_file): wrapper = GenEnsProdWrapper(config) assert wrapper.c_dict['CONFIG_FILE'] == config_file + @pytest.mark.parametrize( 'config_overrides, expected_num_files', [ ({}, 10), ({'GEN_ENS_PROD_ENS_MEMBER_IDS': '1'}, 5), ] ) +@pytest.mark.wrapper def test_gen_ens_prod_fill_missing(metplus_config, config_overrides, expected_num_files): config = metplus_config() diff --git a/internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py b/internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py index 41c3883d19..b584f75499 100644 --- a/internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py +++ b/internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py @@ -1,39 +1,15 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest + +import os import datetime -import produtil from metplus.wrappers.gen_vx_mask_wrapper import GenVxMaskWrapper from metplus.util import time_util -# --------------------TEST CONFIGURATION and FIXTURE SUPPORT ------------- -# -# The test configuration and fixture support the additional configuration -# files used in METplus -# !!!!!!!!!!!!!!! -# !!!IMPORTANT!!! -# !!!!!!!!!!!!!!! -# The following two methods should be included in ALL pytest tests for METplus. -# -# -#def pytest_addoption(parser): -# parser.addoption("-c", action="store", help=" -c ") - -# @pytest.fixture -#def cmdopt(request): -# return request.config.getoption("-c") - - -# -----------------FIXTURES THAT CAN BE USED BY ALL TESTS---------------- -#@pytest.fixture def gen_vx_mask_wrapper(metplus_config): """! Returns a default GenVxMaskWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -44,12 +20,8 @@ def gen_vx_mask_wrapper(metplus_config): config.set('config', 'DO_NOT_RUN_EXE', True) return GenVxMaskWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- - -# ------------------------ -# test_ -# ------------------------ +@pytest.mark.wrapper def test_run_gen_vx_mask_once(metplus_config): input_dict = {'valid': datetime.datetime.strptime("201802010000",'%Y%m%d%H%M'), 'lead': 0} @@ -72,8 +44,10 @@ def test_run_gen_vx_mask_once(metplus_config): for cmd, _ in wrap.all_commands: print(f"COMMAND:{cmd}") print("EXPECTED:{expected_cmd}") - assert(cmd == expected_cmd) + assert cmd == expected_cmd + +@pytest.mark.wrapper def test_run_gen_vx_mask_twice(metplus_config): input_dict = {'valid': datetime.datetime.strptime("201802010000",'%Y%m%d%H%M'), 'lead': 0} @@ -97,7 +71,7 @@ def test_run_gen_vx_mask_twice(metplus_config): if len(wrap.all_commands) != len(expected_cmds): print("Number of commands run is not the same as expected") - assert(False) + assert False for (cmd, _), expected_cmd in zip(wrap.all_commands, expected_cmds): print(f" ACTUAL:{cmd}") @@ -105,5 +79,5 @@ def test_run_gen_vx_mask_twice(metplus_config): if cmd != expected_cmd: test_passed = False - assert(test_passed) + assert test_passed diff --git a/internal_tests/pytests/grid_diag/test_grid_diag.py b/internal_tests/pytests/grid_diag/test_grid_diag.py index 21e48d27f4..3c9178229c 100644 --- a/internal_tests/pytests/grid_diag/test_grid_diag.py +++ b/internal_tests/pytests/grid_diag/test_grid_diag.py @@ -1,18 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest + +import os + from datetime import datetime from dateutil.relativedelta import relativedelta -import produtil - from metplus.wrappers.grid_diag_wrapper import GridDiagWrapper -from metplus.util import time_util + @pytest.mark.parametrize( 'time_info, expected_subset', [ @@ -65,7 +61,9 @@ 'lead': 86400}, ['init_20141031213015_valid_20141101213015_lead_024.nc', ]), - ]) + ] +) +@pytest.mark.wrapper def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): """! Test to ensure that get_all_files only gets the files that are relevant to the runtime settings and not every file in the directory @@ -119,6 +117,7 @@ def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): actual_file = actual_file.strip() assert(os.path.basename(actual_file) == expected_file) + @pytest.mark.parametrize( 'time_info, expected_filename', [ # all wildcard @@ -166,11 +165,15 @@ def test_get_all_files_and_subset(metplus_config, time_info, expected_subset): 'valid': datetime(2014, 11, 1, 9, 30, 15), 'lead': relativedelta(hours=24)}, 'grid_diag_files_input0_init_20141031093015_valid_20141101093015_lead_86400.txt'), -]) + ] +) +@pytest.mark.wrapper def test_get_list_file_name(metplus_config, time_info, expected_filename): wrapper = GridDiagWrapper(metplus_config()) assert(wrapper.get_list_file_name(time_info, 'input0') == expected_filename) + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py b/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py index e29aeaae5a..598692b831 100644 --- a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py +++ b/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import pytest + import os from datetime import datetime @@ -19,6 +20,7 @@ time_fmt = '%Y%m%d%H' run_times = ['2005080700', '2005080712'] + def set_minimum_config_settings(config): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -51,6 +53,7 @@ def set_minimum_config_settings(config): config.set('config', 'OBS_VAR1_NAME', obs_name) config.set('config', 'OBS_VAR1_LEVELS', obs_level) + @pytest.mark.parametrize( 'config_overrides, expected_values', [ # 0 generic FCST is prob @@ -82,6 +85,7 @@ def set_minimum_config_settings(config): {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), ] ) +@pytest.mark.wrapper def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): config = metplus_config() @@ -97,6 +101,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): for key, expected_value in expected_values.items(): assert expected_value == wrapper.c_dict[key] + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ # 0 no climo settings @@ -119,6 +124,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) +@pytest.mark.wrapper def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -145,11 +151,12 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, for old_env in old_env_vars: match = next((item for item in actual_env_vars if item.startswith(old_env)), None) - assert(match is not None) + assert match is not None actual_value = match.split('=', 1)[1] expected_value = env_var_values.get(old_env, '') expected_value = expected_value.replace('YMDH', ymdh) - assert(expected_value == actual_value) + assert expected_value == actual_value + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ @@ -681,6 +688,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) +@pytest.mark.wrapper def test_grid_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -709,13 +717,12 @@ def test_grid_stat_single_field(metplus_config, config_overrides, f"{config_file} -outdir {out_dir}/2005080800"), ] - all_cmds = wrapper.run_all_times() print(f"ALL COMMANDS: {all_cmds}") for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly # including deprecated env vars (not in wrapper env var keys) @@ -725,16 +732,18 @@ def test_grid_stat_single_field(metplus_config, config_overrides, for env_var_key in env_var_keys: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None actual_value = match.split('=', 1)[1] print(f"ENV VAR: {env_var_key}") if env_var_key == 'METPLUS_FCST_FIELD': - assert(actual_value == fcst_fmt) + assert actual_value == fcst_fmt elif env_var_key == 'METPLUS_OBS_FIELD': - assert (actual_value == obs_fmt) + assert actual_value == obs_fmt else: - assert(env_var_values.get(env_var_key, '') == actual_value) + assert env_var_values.get(env_var_key, '') == actual_value + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py b/internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py index 7effa04e77..1e06c77152 100644 --- a/internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py +++ b/internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py @@ -1,13 +1,13 @@ -import os - import pytest -from metplus.wrappers.ioda2nc_wrapper import IODA2NCWrapper +import os +from metplus.wrappers.ioda2nc_wrapper import IODA2NCWrapper time_fmt = '%Y%m%d%H' run_times = ['2020031012', '2020031100'] + def set_minimum_config_settings(config): config.set('config', 'DO_NOT_RUN_EXE', True) config.set('config', 'INPUT_MUST_EXIST', False) @@ -29,6 +29,7 @@ def set_minimum_config_settings(config): config.set('config', 'IODA2NC_OUTPUT_TEMPLATE', 'ioda.NC001007.{valid?fmt=%Y%m%d%H}.summary.nc') + @pytest.mark.parametrize( 'config_overrides, env_var_values, extra_args', [ # 0 @@ -182,6 +183,7 @@ def set_minimum_config_settings(config): {}, ' -iodafile *INPUT_DIR*/other/file.nc -valid_beg 20200309_12 -valid_end 20200310_12 -nmsg 10'), ] ) +@pytest.mark.wrapper def test_ioda2nc_wrapper(metplus_config, config_overrides, env_var_values, extra_args): config = metplus_config() @@ -230,8 +232,10 @@ def test_ioda2nc_wrapper(metplus_config, config_overrides, item.startswith(env_var_key)), None) assert match is not None actual_value = match.split('=', 1)[1] - assert(env_var_values.get(env_var_key, '') == actual_value) + assert env_var_values.get(env_var_key, '') == actual_value + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' config = metplus_config() diff --git a/internal_tests/pytests/met_db_load/test_met_db_load.py b/internal_tests/pytests/met_db_load/test_met_db_load.py index 7e881d6563..6003be50e5 100644 --- a/internal_tests/pytests/met_db_load/test_met_db_load.py +++ b/internal_tests/pytests/met_db_load/test_met_db_load.py @@ -4,6 +4,7 @@ from metplus.wrappers.met_db_load_wrapper import METDbLoadWrapper + @pytest.mark.parametrize( 'filename, expected_result', [ ('myfile.png', False), @@ -15,9 +16,11 @@ ('monster_badfile.txt', False), ] ) +@pytest.mark.wrapper def test_is_loadable_file(filename, expected_result): assert METDbLoadWrapper._is_loadable_file(filename) == expected_result + @pytest.mark.parametrize( 'filenames, expected_result', [ (['myfile.png', @@ -36,5 +39,6 @@ def test_is_loadable_file(filename, expected_result): ([], False), ] ) +@pytest.mark.wrapper def test_has_loadable_file(filenames, expected_result): assert METDbLoadWrapper._has_loadable_file(filenames) == expected_result diff --git a/internal_tests/pytests/mode/test_mode_wrapper.py b/internal_tests/pytests/mode/test_mode_wrapper.py index 4dd1158c02..3d945a022b 100644 --- a/internal_tests/pytests/mode/test_mode_wrapper.py +++ b/internal_tests/pytests/mode/test_mode_wrapper.py @@ -1,18 +1,11 @@ #!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -import datetime -import produtil +import os + from metplus.wrappers.mode_wrapper import MODEWrapper -from metplus.util import met_util as util -from metplus.util import time_util fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' @@ -64,6 +57,7 @@ def set_minimum_config_settings(config): config.set('config', 'OBS_VAR1_NAME', obs_name) config.set('config', 'OBS_VAR1_LEVELS', obs_level) + @pytest.mark.parametrize( 'config_overrides, expected_output', [ ({'MODEL': 'my_model'}, @@ -321,6 +315,7 @@ def set_minimum_config_settings(config): ] ) +@pytest.mark.wrapper def test_mode_single_field(metplus_config, config_overrides, expected_output): config = metplus_config() @@ -396,12 +391,14 @@ def test_mode_single_field(metplus_config, config_overrides, else: assert expected_output.get(env_var_key, '') == value + @pytest.mark.parametrize( 'config_overrides, expected_output', [ ({'MODE_MULTIVAR_LOGIC': '#1 && #2 && #3', }, {'METPLUS_MULTIVAR_LOGIC': 'multivar_logic = "#1 && #2 && #3";'}), ] ) +@pytest.mark.wrapper def test_mode_multi_variate(metplus_config, config_overrides, expected_output): config = metplus_config() @@ -506,6 +503,7 @@ def test_mode_multi_variate(metplus_config, config_overrides, ] ) +@pytest.mark.wrapper def test_config_synonyms(metplus_config, config_name, env_var_name, met_name, var_type): """! Ensure that different METplus config variable names set the correct @@ -530,6 +528,8 @@ def test_config_synonyms(metplus_config, config_name, env_var_name, expected_output = f'{met_name} = {out_value};' assert wrapper.env_var_dict[env_var_name] == expected_output + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/mtd/test_mtd_wrapper.py b/internal_tests/pytests/mtd/test_mtd_wrapper.py index aff12a1bb7..524741b0d6 100644 --- a/internal_tests/pytests/mtd/test_mtd_wrapper.py +++ b/internal_tests/pytests/mtd/test_mtd_wrapper.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 +import pytest + import os import datetime -import pytest from metplus.wrappers.mtd_wrapper import MTDWrapper @@ -25,8 +26,8 @@ def mtd_wrapper(metplus_config, lead_seq=None): return MTDWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- +@pytest.mark.wrapper def test_mtd_by_init_all_found(metplus_config): mw = mtd_wrapper(metplus_config, '1,2,3') obs_dir = mw.config.getdir('METPLUS_BASE')+"/internal_tests/data/obs" @@ -59,6 +60,8 @@ def test_mtd_by_init_all_found(metplus_config): obs_list[2] == os.path.join(obs_dir,'20170510', 'qpe_2017051006_A06.nc') ) + +@pytest.mark.wrapper def test_mtd_by_valid_all_found(metplus_config): mw = mtd_wrapper(metplus_config, '1, 2, 3') obs_dir = mw.config.getdir('METPLUS_BASE')+"/internal_tests/data/obs" @@ -90,7 +93,9 @@ def test_mtd_by_valid_all_found(metplus_config): obs_list[1] == os.path.join(obs_dir,'20170510', 'qpe_2017051003_A06.nc') and obs_list[2] == os.path.join(obs_dir,'20170510', 'qpe_2017051003_A06.nc') ) - + + +@pytest.mark.wrapper def test_mtd_by_init_miss_fcst(metplus_config): mw = mtd_wrapper(metplus_config, '3, 6, 9, 12') obs_dir = mw.config.getdir('METPLUS_BASE')+"/internal_tests/data/obs" @@ -123,6 +128,8 @@ def test_mtd_by_init_miss_fcst(metplus_config): obs_list[2] == os.path.join(obs_dir,'20170510', 'qpe_2017051015_A06.nc') ) + +@pytest.mark.wrapper def test_mtd_by_init_miss_both(metplus_config): mw = mtd_wrapper(metplus_config, '6, 12, 18') obs_dir = mw.config.getdir('METPLUS_BASE')+"/internal_tests/data/obs" @@ -154,6 +161,7 @@ def test_mtd_by_init_miss_both(metplus_config): ) +@pytest.mark.wrapper def test_mtd_single(metplus_config): mw = mtd_wrapper(metplus_config, '1, 2, 3') fcst_dir = mw.config.getdir('METPLUS_BASE')+"/internal_tests/data/fcst" @@ -177,6 +185,8 @@ def test_mtd_single(metplus_config): single_list[2] == os.path.join(fcst_dir,'20170510', '20170510_i03_f003_HRRRTLE_PHPT.grb2') ) + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py b/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py index 909baa88d4..9f98f0d80e 100644 --- a/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py +++ b/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py @@ -1,39 +1,16 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -import datetime -import produtil +import os +import datetime from metplus.wrappers.pb2nc_wrapper import PB2NCWrapper -from metplus.util import met_util as util + from metplus.util import time_util from metplus.util import do_string_sub -# --------------------TEST CONFIGURATION and FIXTURE SUPPORT ------------- -# -# The test configuration and fixture support the additional configuration -# files used in METplus -# !!!!!!!!!!!!!!! -# !!!IMPORTANT!!! -# !!!!!!!!!!!!!!! -# The following two methods should be included in ALL pytest tests for METplus. -# -# -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") - - -def cmdopt(request): - return request.config.getoption("-c") - -# -----------------FIXTURES THAT CAN BE USED BY ALL TESTS---------------- def pb2nc_wrapper(metplus_config): """! Returns a default PB2NCWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -47,13 +24,7 @@ def pb2nc_wrapper(metplus_config): config = metplus_config(extra_configs) return PB2NCWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- -# --------------------- -# test_find_and_check_output_file_skip -# test that find_and_check_output_file returns correctly based on -# if file exists and if 'skip if exists' is turned on -# --------------------- @pytest.mark.parametrize( # key = grid_id, value = expected reformatted grid id 'exists, skip, run', [ @@ -63,6 +34,7 @@ def pb2nc_wrapper(metplus_config): (False, False, True), ] ) +@pytest.mark.wrapper def test_find_and_check_output_file_skip(metplus_config, exists, skip, run): pb = pb2nc_wrapper(metplus_config) exist_file = 'wackyfilenametocreate' @@ -88,6 +60,7 @@ def test_find_and_check_output_file_skip(metplus_config, exists, skip, run): # cast result to bool because None isn't equal to False assert bool(result) == run + # --------------------- # test_get_command # test that command is generated correctly @@ -101,6 +74,7 @@ def test_find_and_check_output_file_skip(metplus_config, exists, skip, run): ['file1', 'file2', 'file3'], ] ) +@pytest.mark.wrapper def test_get_command(metplus_config, infiles): pb = pb2nc_wrapper(metplus_config) pb.outfile = 'outfilename.txt' @@ -119,6 +93,7 @@ def test_get_command(metplus_config, infiles): assert cmd == expected_cmd + # --------------------- # test_find_input_files # test files can be found with find_input_files with varying offset lists @@ -133,6 +108,7 @@ def test_get_command(metplus_config, infiles): ([2, 4, 6], None), ] ) +@pytest.mark.wrapper def test_find_input_files(metplus_config, offsets, offset_to_find): pb = pb2nc_wrapper(metplus_config) # for valid 20190201_12, offsets 3 and 5, create files to find @@ -170,6 +146,7 @@ def test_find_input_files(metplus_config, offsets, offset_to_find): else: assert result['offset_hours'] == offset_to_find + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ ({'PB2NC_MESSAGE_TYPE': 'ADPUPA, ADPSFC'}, @@ -290,6 +267,7 @@ def test_find_input_files(metplus_config, offsets, offset_to_find): ] ) +@pytest.mark.wrapper def test_pb2nc_all_fields(metplus_config, config_overrides, env_var_values): input_dir = '/some/input/dir' @@ -346,7 +324,7 @@ def test_pb2nc_all_fields(metplus_config, config_overrides, for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly # including deprecated env vars (not in wrapper env var keys) @@ -356,10 +334,12 @@ def test_pb2nc_all_fields(metplus_config, config_overrides, for env_var_key in env_var_keys: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None value = match.split('=', 1)[1] - assert(env_var_values.get(env_var_key, '') == value) + assert env_var_values.get(env_var_key, '') == value + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' @@ -375,6 +355,8 @@ def test_get_config_file(metplus_config): wrapper = PB2NCWrapper(config) assert wrapper.c_dict['CONFIG_FILE'] == fake_config_name + +@pytest.mark.wrapper def test_pb2nc_file_window(metplus_config): begin_value = -3600 end_value = 3600 diff --git a/internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py b/internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py index f95b757911..603d4c0943 100644 --- a/internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py +++ b/internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py @@ -1,12 +1,14 @@ #!/usr/bin/env python3 +import pytest + import os from datetime import datetime -import pytest from metplus.wrappers.pcp_combine_wrapper import PCPCombineWrapper from metplus.util import ti_calculate + def pcp_combine_wrapper(metplus_config, d_type): """! Returns a default PCPCombineWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -25,6 +27,8 @@ def pcp_combine_wrapper(metplus_config, d_type): return PCPCombineWrapper(config) + +@pytest.mark.wrapper def test_get_accumulation_1_to_6(metplus_config): data_src = "OBS" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -48,6 +52,8 @@ def test_get_accumulation_1_to_6(metplus_config): input_dir+"/20160904/file.2016090414.01h" in in_files and input_dir+"/20160904/file.2016090413.01h" in in_files) + +@pytest.mark.wrapper def test_get_accumulation_6_to_6(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -68,6 +74,8 @@ def test_get_accumulation_6_to_6(metplus_config): assert (len(in_files) == 1 and input_dir+"/20160904/file.2016090418.06h" in in_files) + +@pytest.mark.wrapper def test_get_lowest_forecast_file_dated_subdir(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -79,6 +87,8 @@ def test_get_lowest_forecast_file_dated_subdir(metplus_config): assert(out_file == input_dir+"/20180201/file.2018020118f003.nc" and fcst == 10800) + +@pytest.mark.wrapper def test_forecast_constant_init(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -91,6 +101,8 @@ def test_forecast_constant_init(metplus_config): assert(out_file == input_dir+"/20180201/file.2018020112f009.nc" and fcst == 32400) + +@pytest.mark.wrapper def test_forecast_not_constant_init(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -105,6 +117,7 @@ def test_forecast_not_constant_init(metplus_config): fcst == 10800) +@pytest.mark.wrapper def test_get_lowest_forecast_file_no_subdir(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -117,6 +130,8 @@ def test_get_lowest_forecast_file_no_subdir(metplus_config): out_file, fcst = pcw.get_lowest_fcst_file(valid_time, data_src) assert(out_file == input_dir+"/file.2018020118f003.nc" and fcst == 10800) + +@pytest.mark.wrapper def test_get_lowest_forecast_file_yesterday(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -129,6 +144,8 @@ def test_get_lowest_forecast_file_yesterday(metplus_config): out_file, fcst = pcw.get_lowest_fcst_file(valid_time, data_src) assert(out_file == input_dir+"/file.2018013118f012.nc" and fcst == 43200) + +@pytest.mark.wrapper def test_setup_add_method(metplus_config): data_src = "OBS" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -151,7 +168,9 @@ def test_setup_add_method(metplus_config): input_dir+"/20160904/file.2016090414.01h" in in_files and input_dir+"/20160904/file.2016090413.01h" in in_files) + # how to test? check output? +@pytest.mark.wrapper def test_setup_sum_method(metplus_config): data_src = "OBS" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -162,6 +181,8 @@ def test_setup_sum_method(metplus_config): lookback = 6 * 3600 assert pcw.setup_sum_method(time_info, lookback, data_src) + +@pytest.mark.wrapper def test_setup_subtract_method(metplus_config): data_src = "FCST" pcw = pcp_combine_wrapper(metplus_config, data_src) @@ -175,6 +196,8 @@ def test_setup_subtract_method(metplus_config): assert len(in_files) == 2 + +@pytest.mark.wrapper def test_pcp_combine_add_subhourly(metplus_config): fcst_name = 'A000500' fcst_level = 'Surface' @@ -243,6 +266,8 @@ def test_pcp_combine_add_subhourly(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + +@pytest.mark.wrapper def test_pcp_combine_bucket(metplus_config): fcst_output_name = 'APCP' config = metplus_config() @@ -307,6 +332,7 @@ def test_pcp_combine_bucket(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + @pytest.mark.parametrize( 'config_overrides, extra_fields', [ ({}, @@ -320,6 +346,7 @@ def test_pcp_combine_bucket(metplus_config): "-field 'name=\"NAME2\"; level=\"LEVEL2\";' ")), ] ) +@pytest.mark.wrapper def test_pcp_combine_derive(metplus_config, config_overrides, extra_fields): stat_list = 'sum,min,max,range,mean,stdev,vld_count' fcst_name = 'APCP' @@ -395,6 +422,8 @@ def test_pcp_combine_derive(metplus_config, config_overrides, extra_fields): # ensure commands are generated as expected assert cmd == expected_cmd + +@pytest.mark.wrapper def test_pcp_combine_loop_custom(metplus_config): fcst_name = 'APCP' ens_list = ['ens1', 'ens2', 'ens3', 'ens4', 'ens5', 'ens6'] @@ -459,6 +488,8 @@ def test_pcp_combine_loop_custom(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + +@pytest.mark.wrapper def test_pcp_combine_subtract(metplus_config): config = metplus_config() @@ -518,6 +549,8 @@ def test_pcp_combine_subtract(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + +@pytest.mark.wrapper def test_pcp_combine_sum_subhourly(metplus_config): fcst_name = 'A000500' fcst_level = 'Surface' @@ -585,6 +618,7 @@ def test_pcp_combine_sum_subhourly(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + @pytest.mark.parametrize( 'output_name,extra_output,expected_results', [ # 0 @@ -598,6 +632,7 @@ def test_pcp_combine_sum_subhourly(metplus_config): ['-name "out_name1","out_name2","out_name3"']), ] ) +@pytest.mark.wrapper def test_handle_name_argument(metplus_config, output_name, extra_output, expected_results): data_src = 'FCST' @@ -637,6 +672,7 @@ def test_handle_name_argument(metplus_config, output_name, extra_output, "-field 'name=\"input2\";'"]), ] ) +@pytest.mark.wrapper def test_get_extra_fields(metplus_config, names, levels, expected_args): data_src = 'FCST' config = metplus_config() @@ -652,6 +688,8 @@ def test_get_extra_fields(metplus_config, names, levels, expected_args): for index, expected_arg in enumerate(expected_args): assert wrapper.args[index] == expected_arg + +@pytest.mark.wrapper def test_add_method_single_file(metplus_config): data_src = 'FCST' config = metplus_config() @@ -717,6 +755,8 @@ def test_add_method_single_file(metplus_config): # ensure commands are generated as expected assert cmd == expected_cmd + +@pytest.mark.wrapper def test_subtract_method_zero_accum(metplus_config): input_name = 'stratiform_rainfall_amount' input_level = '"(*,*)"' diff --git a/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py b/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py index f1a2e54d32..40d9bb8396 100644 --- a/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py +++ b/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py @@ -1,47 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import datetime -import sys -import logging import pytest -import datetime -import produtil.setup +import os from metplus.wrappers.make_plots_wrapper import MakePlotsWrapper -from metplus.util import met_util as util - -# -# These are tests (not necessarily unit tests) for the -# wrapper to make plots, make_plots_wrapper.py -# NOTE: This test requires pytest, which is NOT part of the standard Python -# library. -# These tests require one configuration file in addition to the three -# required METplus configuration files: test_make_plots.conf. This contains -# the information necessary for running all the tests. Each test can be -# customized to replace various settings if needed. -# - -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# +METPLUS_BASE = os.getcwd().split('/internal_tests')[0] -# Add a test configuration -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") -# @pytest.fixture -def cmdopt(request): - return request.config.getoption("-c") - -# -# ------------Pytest fixtures that can be used for all tests --------------- -# -#@pytest.fixture def make_plots_wrapper(metplus_config): """! Returns a default MakePlotsWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -55,35 +22,8 @@ def make_plots_wrapper(metplus_config): config = metplus_config(extra_configs) return MakePlotsWrapper(config) -# ------------------TESTS GO BELOW --------------------------- -# - -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# To test numerous files for filesize, use parametrization: -# @pytest.mark.parametrize( -# 'key, value', [ -# ('/usr/local/met-6.1/bin/point_stat', 382180), -# ('/usr/local/met-6.1/bin/stat_analysis', 3438944), -# ('/usr/local/met-6.1/bin/pb2nc', 3009056) -# -# ] -# ) -# def test_file_sizes(key, value): -# st = stat_analysis_wrapper() -# # Retrieve the value of the class attribute that corresponds -# # to the key in the parametrization -# files_in_dir = [] -# for dirpath, dirnames, files in os.walk("/usr/local/met-6.1/bin"): -# for name in files: -# files_in_dir.append(os.path.join(dirpath, name)) -# if actual_key in files_in_dir: -# # The actual_key is one of the files of interest we retrieved from -# # the output directory. Verify that it's file size is what we -# # expected. -# assert actual_key == key -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -METPLUS_BASE = os.getcwd().split('/internal_tests')[0] +@pytest.mark.wrapper def test_get_command(metplus_config): # Independently test that the make_plots python # command is being put together correctly with @@ -98,6 +38,8 @@ def test_get_command(metplus_config): test_command = mp.get_command() assert(expected_command == test_command) + +@pytest.mark.wrapper def test_create_c_dict(metplus_config): # Independently test that c_dict is being created # and that the wrapper and config reader diff --git a/internal_tests/pytests/plotting/plot_util/test_plot_util.py b/internal_tests/pytests/plotting/plot_util/test_plot_util.py index 386c584bb4..a4d6ea3ad8 100644 --- a/internal_tests/pytests/plotting/plot_util/test_plot_util.py +++ b/internal_tests/pytests/plotting/plot_util/test_plot_util.py @@ -1,48 +1,23 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 + +import pytest import os -import datetime import sys -import logging -import pytest import datetime +import logging + import numpy as np import pandas as pd -import produtil.setup -# ------------------TESTS GO BELOW --------------------------- -# - -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# To test numerous files for filesize, use parametrization: -# @pytest.mark.parametrize( -# 'key, value', [ -# ('/usr/local/met-6.1/bin/point_stat', 382180), -# ('/usr/local/met-6.1/bin/stat_analysis', 3438944), -# ('/usr/local/met-6.1/bin/pb2nc', 3009056) -# -# ] -# ) -# def test_file_sizes(key, value): -# st = stat_analysis_wrapper() -# # Retrieve the value of the class attribute that corresponds -# # to the key in the parametrization -# files_in_dir = [] -# for dirpath, dirnames, files in os.walk("/usr/local/met-6.1/bin"): -# for name in files: -# files_in_dir.append(os.path.join(dirpath, name)) -# if actual_key in files_in_dir: -# # The actual_key is one of the files of interest we retrieved from -# # the output directory. Verify that it's file size is what we -# # expected. -# assert actual_key == key -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! METPLUS_BASE = os.getcwd().split('/internal_tests')[0] sys.path.append(METPLUS_BASE+'/ush/plotting_scripts') import plot_util logger = logging.getLogger('~/metplus_pytest_plot_util.log') + +@pytest.mark.wrapper def test_get_date_arrays(): # Independently test the creation of # the date arrays, one used for plotting @@ -209,6 +184,8 @@ def test_get_date_arrays(): assert(test_expected_stat_file_dates[l] == expected_expected_stat_file_dates[l]) + +@pytest.mark.wrapper def test_format_thresh(): # Independently test the formatting # of thresholds @@ -297,6 +274,8 @@ def test_format_thresh(): assert(test_thresh_symbol == expected_thresh_symbol) assert(test_thresh_letter == expected_thresh_letter) + +@pytest.mark.wrapper def test_get_stat_file_base_columns(): # Independently test getting list # of the base MET version .stat file columns @@ -332,6 +311,8 @@ def test_get_stat_file_base_columns(): ) assert(test_stat_file_base_columns == expected_stat_file_base_columns) + +@pytest.mark.wrapper def test_get_stat_file_line_type_columns(): # Independently test getting list # of the line type MET version .stat file columns @@ -441,6 +422,8 @@ def test_get_stat_file_line_type_columns(): assert(test_stat_file_line_type_columns == expected_stat_file_line_type_columns) + +@pytest.mark.wrapper def get_clevels(): # Independently test creating an array # of levels centered about 0 to plot @@ -453,6 +436,8 @@ def get_clevels(): test_clevels = plot_util.get_clevels(data) assert(test_clevels == expected_clevels) + +@pytest.mark.wrapper def test_calculate_average(): # Independently test getting the average # of a data array based on method @@ -558,7 +543,9 @@ def test_calculate_average(): assert(len(test_average_array) == len(expected_average_array)) for l in range(len(test_average_array)): assert(round(test_average_array[l],6) == expected_average_array[l]) - + + +@pytest.mark.long def test_calculate_ci(): pytest.skip("Takes far too long to run") # Independently test getting the @@ -691,6 +678,8 @@ def test_calculate_ci(): stat, average_method, randx) assert(test_intvl == expected_intvl) + +@pytest.mark.wrapper def test_get_stat_plot_name(): # Independently test getting the # a more formalized statistic name @@ -730,6 +719,8 @@ def test_get_stat_plot_name(): test_stat_plot_name = plot_util.get_stat_plot_name(logger, stat) assert(test_stat_plot_name == expected_stat_plot_name) + +@pytest.mark.wrapper def test_calculate_stat(): # Independently test calculating # statistic values diff --git a/internal_tests/pytests/point2grid/test_point2grid.py b/internal_tests/pytests/point2grid/test_point2grid.py index d6b2137222..e4b27f5d60 100644 --- a/internal_tests/pytests/point2grid/test_point2grid.py +++ b/internal_tests/pytests/point2grid/test_point2grid.py @@ -1,39 +1,12 @@ -#!/usr/bin/env python - -import os -import sys -import re -import logging -from collections import namedtuple -import produtil +#!/usr/bin/env python3 + import pytest import datetime from metplus.wrappers.point2grid_wrapper import Point2GridWrapper -from metplus.util import met_util as util from metplus.util import time_util -# --------------------TEST CONFIGURATION and FIXTURE SUPPORT ------------- -# -# The test configuration and fixture support the additional configuration -# files used in METplus -# !!!!!!!!!!!!!!! -# !!!IMPORTANT!!! -# !!!!!!!!!!!!!!! -# The following two methods should be included in ALL pytest tests for METplus. -# -# -#def pytest_addoption(parser): -# parser.addoption("-c", action="store", help=" -c ") - - -# @pytest.fixture -#def cmdopt(request): -# return request.config.getoption("-c") - -# -----------------FIXTURES THAT CAN BE USED BY ALL TESTS---------------- -#@pytest.fixture def p2g_wrapper(metplus_config): """! Returns a default Point2Grid with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -44,9 +17,8 @@ def p2g_wrapper(metplus_config): config.set('config', 'DO_NOT_RUN_EXE', True) return Point2GridWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- - +@pytest.mark.wrapper def test_set_command_line_arguments(metplus_config): test_passed = True wrap = p2g_wrapper(metplus_config) @@ -174,5 +146,4 @@ def test_set_command_line_arguments(metplus_config): wrap.args.clear() - - assert(test_passed) + assert test_passed diff --git a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py b/internal_tests/pytests/point_stat/test_point_stat_wrapper.py index d7d4ff8167..62b08f736b 100755 --- a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py +++ b/internal_tests/pytests/point_stat/test_point_stat_wrapper.py @@ -1,13 +1,15 @@ #!/usr/bin/env python3 -import os import pytest +import os + from metplus.wrappers.point_stat_wrapper import PointStatWrapper fcst_dir = '/some/path/fcst' obs_dir = '/some/path/obs' + def set_minimum_config_settings(config): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -36,6 +38,8 @@ def set_minimum_config_settings(config): '{OUTPUT_BASE}/GridStat/output') config.set('config', 'POINT_STAT_OUTPUT_TEMPLATE', '{valid?fmt=%Y%m%d%H}') + +@pytest.mark.wrapper def test_met_dictionary_in_var_options(metplus_config): config = metplus_config() set_minimum_config_settings(config) @@ -50,6 +54,7 @@ def test_met_dictionary_in_var_options(metplus_config): all_cmds = wrapper.run_all_times() + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ ({'MODEL': 'my_model'}, @@ -465,6 +470,7 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_FCST_FILE_TYPE': 'file_type = NETCDF_PINT;'}), ] ) +@pytest.mark.wrapper def test_point_stat_all_fields(metplus_config, config_overrides, env_var_values): level_no_quotes = '(*,*)' @@ -553,7 +559,7 @@ def test_point_stat_all_fields(metplus_config, config_overrides, for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly # including deprecated env vars (not in wrapper env var keys) @@ -563,15 +569,17 @@ def test_point_stat_all_fields(metplus_config, config_overrides, for env_var_key in env_var_keys: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None value = match.split('=', 1)[1] if env_var_key == 'METPLUS_FCST_FIELD': - assert(value == fcst_fmt) + assert value == fcst_fmt elif env_var_key == 'METPLUS_OBS_FIELD': - assert (value == obs_fmt) + assert value == obs_fmt else: - assert(env_var_values.get(env_var_key, '') == value) + assert env_var_values.get(env_var_key, '') == value + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/produtil/README b/internal_tests/pytests/produtil/README deleted file mode 100644 index 540ba31e6d..0000000000 --- a/internal_tests/pytests/produtil/README +++ /dev/null @@ -1,23 +0,0 @@ -The test_produtil.py provides some simple tests for the produtil module. - -To run the test: - -1) cd to the directory METplus/internal_tests/pytest/produtil - -2) open the test_produtil.py file and replace the '/path/to' with the full path to the directory where your produtil_test.conf file -resides (this will be in METplus/internal_tests/pytest/produtil). - -NOTE: This is necessary, as we are NOT using run_metplus.py to begin the process of reading in the config -file, test_produtil.conf - - -2) Then, from the command line, enter the following command: - - pytest -c ./produtil_test.conf - -There are currently 9 tests and they should pass. - - - - - diff --git a/internal_tests/pytests/produtil/produtil_test.conf b/internal_tests/pytests/produtil/produtil_test.conf deleted file mode 100644 index 8d3b0b84b7..0000000000 --- a/internal_tests/pytests/produtil/produtil_test.conf +++ /dev/null @@ -1,31 +0,0 @@ - -# Test configuration for METplus produtil -[config] -STRING_VALUE = someStringValue!#@$% -INT_VALUE = 2908887 -RAW_VALUE = GRIB_lvl_type = 100 -BOOL_VALUE = True -NEW_LINES = very long line requiring newline character to be tested 12345 - 67890 end of the line. -UNASSIGNED_VALUE = -JOB_LIST = -job filter -dump_row {PROJ_DIR}/dump_file.out -job summary by AMAX_WIND -job summary 'ABS(AMAX_WIND-BMAX_WIND)' -out {OUTPUT_BASE}/max_wind_delta.tcst -JOBS = -job summary -by AMODEL,LEAD -column AMSLP -column BMSLP -column 'ABS(AMSLP-BMSLP)' -out {OUTPUT_BASE}/tc_stat_summary.out - -[dir] -# set in the metplus_data.conf to /path/to, override here for testing -PROJ_DIR = /tmp/produtil_testing - -# set in the metplus_system.conf to /path/to, override here for testing, set to -# appropriate version of MET -MET_INSTALL_DIR = /usr/local/met-8.1 -METPLUS_BASE = /usr/local/met-8.1 -OUTPUT_BASE = /tmp/produtil_testing/out -TMP_DIR = /tmp/produtil_testing/tmp - -# Used for testing -DIR_VALUE = /tmp/some_dir -BASE_DIR = /tmp -SPECIFIC_DIR = {BASE_DIR}/specific_place - -[exe] -WGRIB2 = wgrib2 diff --git a/internal_tests/pytests/produtil/test_produtil.py b/internal_tests/pytests/produtil/test_produtil.py deleted file mode 100644 index c5e816e742..0000000000 --- a/internal_tests/pytests/produtil/test_produtil.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import produtil.setup -import sys -import logging -import pytest -from shutil import which - -from metplus.util import met_util as util - -# -# These are tests (not necessarily unit tests) for the -# MET Point-Stat Wrapper, PointStatWrapper.py -# NOTE: This test requires pytest, which is NOT part of the standard Python -# library. -# These tests require one configuration file in addition to the three -# required METplus configuration files: point_stat_test.conf. This contains -# the information necessary for running all the tests. Each test can be -# customized to replace various settings if needed. -# - -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# - - -# Add a test configuration -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") - - -# @pytest.fixture -def cmdopt(request): - return request.config.getoption("-c") - - -# ------------------------ -def dummy(): - assert(True) - -def get_config_obj(metplus_config): - """! Create the configuration object that is used by all tests""" - file_list = ["/path/to/METplus/internal_tests/pytests/produtil"] - extra_configs = [] - extra_configs.append(os.path.join(os.path.dirname(__file__), 'produtil_test.conf')) - config = metplus_config(extra_configs) - - return config - - -def test_getstr_ok(metplus_config): - """! Test that the expected string is retrieved via produtil's getstr - method - """ - conf_obj = get_config_obj(metplus_config) - str_value = conf_obj.getstr('config', 'STRING_VALUE') - expected_str_value = "someStringValue!#@$%" - assert str_value == expected_str_value - - -def test_getint_ok(metplus_config): - """! Test that the expected int in the produtil_test.conf file has been - retrieved correctly. - """ - conf_obj = get_config_obj(metplus_config) - expected_int_value = int(2908887) - int_value = conf_obj.getint('config', 'INT_VALUE') - assert int_value == expected_int_value - - - -def test_getdir_ok(metplus_config): - """! Test that the directory in the produtil_test.conf file has been - correctly retrieved. - """ - conf_obj = get_config_obj(metplus_config) - expected_dir = "/tmp/some_dir" - dir_retrieved = conf_obj.getdir('DIR_VALUE') - assert dir_retrieved == expected_dir - - -def test_getdir_compound_ok(metplus_config): - """! Test that directories created from other directories, ie. - BASE_DIR = /base/dir - SPECIFIC_DIR = {BASE_DIR}/specific/dir - - correctly returns the directory path for SPECIFIC_DIR - """ - expected_specific_dir = "/tmp/specific_place" - conf_obj = get_config_obj(metplus_config) - specific_dir = conf_obj.getdir('SPECIFIC_DIR') - assert specific_dir == expected_specific_dir - - -def test_no_value_as_string(metplus_config): - """! Tests that a key with no value returns an empty string.""" - - conf_obj = get_config_obj(metplus_config) - expected_unassigned = '' - unassigned = conf_obj.getstr('config', 'UNASSIGNED_VALUE') - print("unassigned: ", unassigned) - print("expected: ", expected_unassigned) - assert unassigned == expected_unassigned - - -def test_no_value_as_list(metplus_config): - """! Tests that a key with no list of strings returns an empty list.""" - - conf_obj = get_config_obj(metplus_config) - expected_unassigned = [] - unassigned = util.getlist(conf_obj.getstr('config', 'UNASSIGNED_VALUE')) - assert unassigned == expected_unassigned - - -def test_new_lines_in_conf(metplus_config): - """! Test that any newlines in the configuration file are handled - properly - """ - - conf_obj = get_config_obj(metplus_config) - expected_string = \ - "very long line requiring newline character to be tested 12345\n67890 end of the line." - long_line = conf_obj.getstr('config', 'NEW_LINES') - assert long_line == expected_string - - -def test_get_exe_ok(metplus_config): - """! Test that executables are correctly retrieved.""" - conf_obj = get_config_obj(metplus_config) - expected_exe = which('wgrib2') - executable = conf_obj.getexe('WGRIB2') - assert executable == expected_exe - - -def test_get_bool(metplus_config): - """! Test that boolean values are correctly retrieved.""" - conf_obj = get_config_obj(metplus_config) - bool_val = conf_obj.getbool('config', 'BOOL_VALUE') - assert bool_val is True - diff --git a/internal_tests/pytests/produtil/work_in_progress_test_produtil_regression.py b/internal_tests/pytests/produtil/work_in_progress_test_produtil_regression.py deleted file mode 100644 index 580e376cc7..0000000000 --- a/internal_tests/pytests/produtil/work_in_progress_test_produtil_regression.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python3 - -import os -import subprocess -import produtil -import sys -import logging -import pytest -import config_metplus -import config_launcher as launcher -import met_util as util - - -# -# These are tests (not necessarily unit tests) for the -# MET Point-Stat Wrapper, PointStatWrapper.py -# NOTE: This test requires pytest, which is NOT part of the standard Python -# library. -# These tests require one configuration file in addition to the three -# required METplus configuration files: point_stat_test.conf. This contains -# the information necessary for running all the tests. Each test can be -# customized to replace various settings if needed. -# - -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# - - -# Add a test configuration -# def pytest_addoption(parser): -# parser.addoption("-c", action="store", help=" -c ") -# -# -# # @pytest.fixture -# def cmdopt(request): -# return request.config.getoption("-c") - - -# ------------------------ - -def dummy(): - assert(True) - - -def get_config_obj(): - """! Create the configuration object that is used by all tests""" - file_list = ["METplus/internal_tests/pytests/produtil"] - config_obj = config_metplus.setup(file_list[0]) - - return config_obj - - -def test_getstr_ok(regtest): - """! Test that the expected string is retrieved via produtil's getstr - method - """ - conf_obj = get_config_obj() - str_value = conf_obj.getstr('config', 'STRING_VALUE') - expected_str_value = "someStringValue!#@$%" - # print(str_value, file=regtest) - regtest.write("done") -# -# -# def test_getint_ok(regtest): -# """! Test that the expected int in the produtil_test.conf file has been -# retrieved correctly. -# """ -# conf_obj = get_config_obj() -# expected_int_value = int(2908887) -# int_value = conf_obj.getint('config', 'INT_VALUE') -# # print(int_value, file=regtest) -# regtest.write("done") -# -# -# def test_getraw_ok(regtest): -# """! Test that the raw value in the produtil_test.conf file has been -# retrieved correctly. -# """ -# conf_obj = get_config_obj() -# expected_raw = 'GRIB_lvl_type = 100' -# raw_value = conf_obj.getraw('config', 'RAW_VALUE') -# # print(raw_value, file=regtest) -# # regtest.write("done") -# -# -# def test_getdir_ok(regtest): -# """! Test that the directory in the produtil_test.conf file has been -# correctly retrieved. -# """ -# conf_obj = get_config_obj() -# expected_dir = "/tmp/some_dir" -# dir_retrieved = conf_obj.getdir('DIR_VALUE') -# # print(dir_retrieved, file=regtest) -# -# -# def test_getdir_compound_ok(regtest): -# """! Test that directories created from other directories, ie. -# BASE_DIR = /base/dir -# SPECIFIC_DIR = {BASE_DIR}/specific/dir -# -# correctly returns the directory path for SPECIFIC_DIR -# """ -# expected_specific_dir = "/tmp/specific_place" -# conf_obj = get_config_obj() -# specific_dir = conf_obj.getdir('SPECIFIC_DIR') -# print(specific_dir, file=regtest) -# -# -# def test_no_value_as_string(regtest): -# """! Tests that a key with no value returns an empty string.""" -# -# conf_obj = get_config_obj() -# expected_unassigned = '' -# unassigned = conf_obj.getstr('config', 'UNASSIGNED_VALUE') -# # print(unassigned, file=regtest) -# -# -# def test_no_value_as_list(regtest): -# """! Tests that a key with no list of strings returns an empty list.""" -# -# conf_obj = get_config_obj() -# expected_unassigned = [] -# unassigned = util.getlist(conf_obj.getstr('config', 'UNASSIGNED_VALUE')) -# assert unassigned == expected_unassigned -# # print(unassigned, file=regtest) -# -# -# def test_new_lines_in_conf(regtest): -# """! Test that any newlines in the configuration file are handled -# properly -# """ -# -# conf_obj = get_config_obj() -# expected_string = \ -# "very long line requiring newline character to be tested 12345\n67890 end of the line." -# long_line = conf_obj.getstr('config', 'NEW_LINES') -# assert long_line == expected_string -# # print(long_line, file=regtest) -# -# -# def test_get_exe_ok(regtest): -# """! Test that executables are correctly retrieved.""" -# conf_obj = get_config_obj() -# expected_exe = '/usr/local/bin/wgrib2' -# executable = conf_obj.getexe('WGRIB2') -# assert executable == expected_exe -# # print(executable, file=regtest) -# -# -# def test_get_bool(regtest): -# """! Test that boolean values are correctly retrieved.""" -# conf_obj = get_config_obj() -# bool_val = conf_obj.getbool('config', 'BOOL_VALUE') -# assert bool_val is True -# # print(bool_val, file=regtest) -# diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini new file mode 100644 index 0000000000..98138a6620 --- /dev/null +++ b/internal_tests/pytests/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +markers = + util + wrapper diff --git a/internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py b/internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py index 0d752ee14d..9e6e436bf0 100644 --- a/internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py +++ b/internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py @@ -1,40 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest -import datetime -import produtil +import os +import datetime from metplus.wrappers.regrid_data_plane_wrapper import RegridDataPlaneWrapper -from metplus.util import met_util as util from metplus.util import time_util -# --------------------TEST CONFIGURATION and FIXTURE SUPPORT ------------- -# -# The test configuration and fixture support the additional configuration -# files used in METplus -# !!!!!!!!!!!!!!! -# !!!IMPORTANT!!! -# !!!!!!!!!!!!!!! -# The following two methods should be included in ALL pytest tests for METplus. -# -# -#def pytest_addoption(parser): -# parser.addoption("-c", action="store", help=" -c ") - - -# @pytest.fixture -#def cmdopt(request): -# return request.config.getoption("-c") - -# -----------------FIXTURES THAT CAN BE USED BY ALL TESTS---------------- -#@pytest.fixture def rdp_wrapper(metplus_config): """! Returns a default RegridDataPlane with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -45,7 +19,6 @@ def rdp_wrapper(metplus_config): config.set('config', 'DO_NOT_RUN_EXE', True) return RegridDataPlaneWrapper(config) -# ------------------------ TESTS GO HERE -------------------------- # field info is the input dictionary with name and level info to parse # expected_arg is the argument that should be set by the function @@ -84,7 +57,7 @@ def rdp_wrapper(metplus_config): ), ] ) - +@pytest.mark.wrapper def test_set_field_command_line_arguments(metplus_config, field_info, expected_arg): data_type = 'FCST' @@ -93,7 +66,8 @@ def test_set_field_command_line_arguments(metplus_config, field_info, expected_a rdp = RegridDataPlaneWrapper(config) rdp.set_field_command_line_arguments(field_info, data_type) - assert(rdp.args[0] == expected_arg) + assert rdp.args[0] == expected_arg + @pytest.mark.parametrize( 'var_list, expected_names', [ @@ -150,13 +124,16 @@ def test_set_field_command_line_arguments(metplus_config, field_info, expected_a ] ) +@pytest.mark.wrapper def test_get_output_names(metplus_config, var_list, expected_names): data_type = 'FCST' rdp = RegridDataPlaneWrapper(metplus_config()) - assert(rdp.get_output_names(var_list, data_type) == expected_names) + assert rdp.get_output_names(var_list, data_type) == expected_names + +@pytest.mark.wrapper def test_run_rdp_once_per_field(metplus_config): data_type = 'FCST' @@ -195,7 +172,7 @@ def test_run_rdp_once_per_field(metplus_config): print("Number of commands run is not the same as expected") print(f"Actual commands: {wrap.all_commands}\n") print(f"Expected commands: {expected_cmds}\n") - assert(False) + assert False for (cmd, _), expected_cmd in zip(wrap.all_commands, expected_cmds): print(f" ACTUAL:{cmd}") @@ -203,8 +180,10 @@ def test_run_rdp_once_per_field(metplus_config): if cmd != expected_cmd: test_passed = False - assert(test_passed) + assert test_passed + +@pytest.mark.wrapper def test_run_rdp_all_fields(metplus_config): data_type = 'FCST' @@ -239,7 +218,7 @@ def test_run_rdp_all_fields(metplus_config): if len(wrap.all_commands) != len(expected_cmds): print("Number of commands run is not the same as expected") - assert(False) + assert False for (cmd, _), expected_cmd in zip(wrap.all_commands, expected_cmds): print(f" ACTUAL:{cmd}") @@ -247,8 +226,10 @@ def test_run_rdp_all_fields(metplus_config): if cmd != expected_cmd: test_passed = False - assert(test_passed) + assert test_passed + +@pytest.mark.wrapper def test_set_command_line_arguments(metplus_config): test_passed = True wrap = rdp_wrapper(metplus_config) @@ -329,4 +310,4 @@ def test_set_command_line_arguments(metplus_config): wrap.args.clear() - assert(test_passed) + assert test_passed diff --git a/internal_tests/pytests/runtime_freq/test_runtime_freq.py b/internal_tests/pytests/runtime_freq/test_runtime_freq.py index 890d814668..0c589349c3 100644 --- a/internal_tests/pytests/runtime_freq/test_runtime_freq.py +++ b/internal_tests/pytests/runtime_freq/test_runtime_freq.py @@ -1,18 +1,12 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest + from datetime import datetime from dateutil.relativedelta import relativedelta -import produtil - from metplus.wrappers.runtime_freq_wrapper import RuntimeFreqWrapper -from metplus.util import time_util + @pytest.mark.parametrize( 'runtime, filetime, expected_result', [ @@ -106,9 +100,10 @@ True), ] ) +@pytest.mark.wrapper def test_compare_time_info(metplus_config, runtime, filetime, expected_result): config = metplus_config() wrapper = RuntimeFreqWrapper(config) actual_result = wrapper.compare_time_info(runtime, filetime) - assert(actual_result == expected_result) + assert actual_result == expected_result diff --git a/internal_tests/pytests/series_analysis/test_series_analysis.py b/internal_tests/pytests/series_analysis/test_series_analysis.py index 68fb19c578..bb067af134 100644 --- a/internal_tests/pytests/series_analysis/test_series_analysis.py +++ b/internal_tests/pytests/series_analysis/test_series_analysis.py @@ -1,12 +1,9 @@ import pytest + import os -import sys -import logging from datetime import datetime from dateutil.relativedelta import relativedelta -import produtil - from metplus.util import ti_get_seconds_from_lead, sub_var_list from metplus.wrappers.series_analysis_wrapper import SeriesAnalysisWrapper @@ -27,6 +24,7 @@ stat_list_quotes = '", "'.join(stat_list.split(',')) stat_list_fmt = f'output_stats = {{cnt = ["{stat_list_quotes}"];}}' + def get_input_dirs(config): fake_data_dir = os.path.join(config.getdir('METPLUS_BASE'), 'internal_tests', @@ -37,6 +35,7 @@ def get_input_dirs(config): 'tiles') return stat_input_dir, tile_input_dir + def series_analysis_wrapper(metplus_config, config_overrides=None): extra_configs = [] extra_configs.append(os.path.join(os.path.dirname(__file__), @@ -49,6 +48,7 @@ def series_analysis_wrapper(metplus_config, config_overrides=None): return SeriesAnalysisWrapper(config) + def set_minimum_config_settings(config): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -293,6 +293,7 @@ def set_minimum_config_settings(config): ] ) +@pytest.mark.wrapper def test_series_analysis_single_field(metplus_config, config_overrides, env_var_values): @@ -325,24 +326,26 @@ def test_series_analysis_single_field(metplus_config, config_overrides, for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly for env_var_key in wrapper.WRAPPER_ENV_VAR_KEYS: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None actual_value = match.split('=', 1)[1] print(f"ENV VAR: {env_var_key}") if env_var_key == 'METPLUS_FCST_FIELD': - assert(actual_value == fcst_fmt) + assert actual_value == fcst_fmt elif env_var_key == 'METPLUS_OBS_FIELD': - assert (actual_value == obs_fmt) + assert actual_value == obs_fmt elif env_var_key == 'METPLUS_OUTPUT_STATS_DICT' and 'METPLUS_OUTPUT_STATS_DICT' not in env_var_values: - assert (actual_value == stat_list_fmt) + assert actual_value == stat_list_fmt else: - assert(env_var_values.get(env_var_key, '') == actual_value) + assert env_var_values.get(env_var_key, '') == actual_value + +@pytest.mark.wrapper def test_get_fcst_file_info(metplus_config): """ Verify that the tuple created by get_fcst_file_info is not an empty tuple, and that the number, beginning @@ -376,12 +379,12 @@ def test_get_fcst_file_info(metplus_config): assert beg == expected_beg assert end == expected_end + +@pytest.mark.wrapper def test_get_storms_list(metplus_config): """Verify that the expected number of storms are found for the init time 20141214_00 """ - config = metplus_config() - expected_storm_list = ['ML1201072014', 'ML1221072014', 'ML1241072014', @@ -400,6 +403,7 @@ def test_get_storms_list(metplus_config): storm_list = wrapper.get_storm_list(time_info) assert storm_list == expected_storm_list + # added list of all files for reference for creating subsets all_fake_fcst = ['fcst/20141214_00/ML1201072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', 'fcst/20141214_00/ML1221072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', @@ -421,6 +425,8 @@ def test_get_storms_list(metplus_config): 'obs/20141215_00/ML1291072014/OBS_TILE_F006_gfs_4_20141215_0000_006.nc', 'obs/20141215_00/ML1291072014/OBS_TILE_F012_gfs_4_20141215_0000_012.nc', ] + + @pytest.mark.parametrize( 'time_info, expect_fcst_subset, expect_obs_subset', [ # filter by init all storms @@ -485,6 +491,7 @@ def test_get_storms_list(metplus_config): ]), ] ) +@pytest.mark.wrapper def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, expect_obs_subset): """! Test to ensure that get_all_files only gets the files that are relevant to the runtime settings and not every file in the directory @@ -515,7 +522,7 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, wrapper.c_dict['FCST_INPUT_DIR'] = fcst_input_dir wrapper.c_dict['OBS_INPUT_DIR'] = obs_input_dir - assert(wrapper.get_all_files()) + assert wrapper.get_all_files() expected_fcst = [ 'fcst/20141214_00/ML1201072014/FCST_TILE_F000_gfs_4_20141214_0000_000.nc', @@ -548,18 +555,19 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, obs_files = [item['obs'] for item in wrapper.c_dict['ALL_FILES']] obs_files = [item for sub in obs_files for item in sub] - assert(fcst_files == expected_fcst_files) - assert(obs_files == expected_obs_files) + assert fcst_files == expected_fcst_files + assert obs_files == expected_obs_files fcst_files_sub, obs_files_sub = wrapper.subset_input_files(time_info) - assert(fcst_files_sub and obs_files_sub) - assert(len(fcst_files_sub) == len(obs_files_sub)) + assert fcst_files_sub and obs_files_sub + assert len(fcst_files_sub) == len(obs_files_sub) for actual_file, expected_file in zip(fcst_files_sub, expect_fcst_subset): - assert(actual_file.replace(tile_input_dir, '').lstrip('/') == expected_file) + assert actual_file.replace(tile_input_dir, '').lstrip('/') == expected_file for actual_file, expected_file in zip(obs_files_sub, expect_obs_subset): - assert(actual_file.replace(tile_input_dir, '').lstrip('/') == expected_file) + assert actual_file.replace(tile_input_dir, '').lstrip('/') == expected_file + @pytest.mark.parametrize( 'config_overrides, time_info, storm_id, lead_group, expect_fcst_subset, expect_obs_subset', [ @@ -707,6 +715,7 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, ]), ] ) +@pytest.mark.wrapper def test_get_fcst_and_obs_path(metplus_config, config_overrides, time_info, storm_id, lead_group, expect_fcst_subset, expect_obs_subset): @@ -740,7 +749,7 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, test_out_dirname) wrapper.c_dict['OUTPUT_DIR'] = output_dir - assert(wrapper.get_all_files()) + assert wrapper.get_all_files() # read output files and compare to expected list if storm_id == '*': @@ -775,7 +784,7 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, fcst_path, obs_path = wrapper._get_fcst_and_obs_path(time_info, storm_id, lead_group) - assert(fcst_path == fcst_file_path and obs_path == obs_file_path) + assert fcst_path == fcst_file_path and obs_path == obs_file_path with open(fcst_file_path, 'r') as file_handle: actual_fcsts = file_handle.readlines() @@ -783,7 +792,7 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, for actual_file, expected_file in zip(actual_fcsts, expect_fcst_subset): actual_file = actual_file.replace(tile_input_dir, '').lstrip('/') - assert(actual_file == expected_file) + assert actual_file == expected_file with open(obs_file_path, 'r') as file_handle: actual_obs_files = file_handle.readlines() @@ -791,7 +800,8 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, for actual_file, expected_file in zip(actual_obs_files, expect_obs_subset): actual_file = actual_file.replace(tile_input_dir, '').lstrip('/') - assert(actual_file == expected_file) + assert actual_file == expected_file + @pytest.mark.parametrize( 'storm_id, leads, expected_result', [ @@ -823,6 +833,7 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, '_FILES_F012_to_F018'), ] ) +@pytest.mark.wrapper def test_get_ascii_filename(metplus_config, storm_id, leads, expected_result): wrapper = series_analysis_wrapper(metplus_config) @@ -830,7 +841,7 @@ def test_get_ascii_filename(metplus_config, storm_id, leads, actual_result = wrapper._get_ascii_filename(data_type, storm_id, leads) - assert(actual_result == f"{data_type}{expected_result}") + assert actual_result == f"{data_type}{expected_result}" if leads is None: return @@ -839,7 +850,9 @@ def test_get_ascii_filename(metplus_config, storm_id, leads, actual_result = wrapper._get_ascii_filename(data_type, storm_id, lead_seconds) - assert(actual_result == f"{data_type}{expected_result}") + assert actual_result == f"{data_type}{expected_result}" + + @pytest.mark.parametrize( # no storm ID, label 'template, storm_id, label, expected_result', [ @@ -856,6 +869,7 @@ def test_get_ascii_filename(metplus_config, storm_id, leads, 'ML1221072014', '', '20141031_12/ML1221072014_'), ] ) +@pytest.mark.wrapper def test_get_output_dir(metplus_config, template, storm_id, label, expected_result): time_info = {'init': datetime(2014, 10, 31, 12, 15), 'valid': datetime(2014, 10, 31, 18, 15), @@ -867,6 +881,8 @@ def test_get_output_dir(metplus_config, template, storm_id, label, expected_resu actual_result = wrapper.get_output_dir(time_info, storm_id, label) assert(actual_result == os.path.join(output_dir, expected_result)) + +@pytest.mark.wrapper def test_get_netcdf_min_max(metplus_config): expected_min = 0.0 expected_max = 8.0 @@ -880,9 +896,11 @@ def test_get_netcdf_min_max(metplus_config): 'basin_global_tenth_degree.nc') variable_name = 'basin' min, max = wrapper._get_netcdf_min_max(filepath, variable_name) - assert(min == expected_min) - assert(max == expected_max) + assert min == expected_min + assert max == expected_max + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis.py b/internal_tests/pytests/stat_analysis/test_stat_analysis.py index 2c567aeaa7..dc596becdc 100644 --- a/internal_tests/pytests/stat_analysis/test_stat_analysis.py +++ b/internal_tests/pytests/stat_analysis/test_stat_analysis.py @@ -1,48 +1,16 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import datetime -import sys -import logging import pytest -import datetime -import produtil.setup +import os +import datetime from metplus.wrappers.stat_analysis_wrapper import StatAnalysisWrapper -from metplus.util import met_util as util - - -# -# These are tests (not necessarily unit tests) for the -# MET stat_analysis wrapper, stat_analysis_wrapper.py -# NOTE: This test requires pytest, which is NOT part of the standard Python -# library. -# These tests require one configuration file in addition to the three -# required METplus configuration files: test_stat_analysis.conf. This contains -# the information necessary for running all the tests. Each test can be -# customized to replace various settings if needed. -# - -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# - - -# Add a test configuration -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") - -# @pytest.fixture -def cmdopt(request): - return request.config.getoption("-c") - -# -# ------------Pytest fixtures that can be used for all tests --------------- -# -#@pytest.fixture +from metplus.util import handle_tmp_dir + +METPLUS_BASE = os.getcwd().split('/internal_tests')[0] + + def stat_analysis_wrapper(metplus_config): """! Returns a default StatAnalysisWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -54,38 +22,11 @@ def stat_analysis_wrapper(metplus_config): extra_configs = [] extra_configs.append(os.path.join(os.path.dirname(__file__), 'test.conf')) config = metplus_config(extra_configs) - util.handle_tmp_dir(config) + handle_tmp_dir(config) return StatAnalysisWrapper(config) -# ------------------TESTS GO BELOW --------------------------- -# - -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# To test numerous files for filesize, use parametrization: -# @pytest.mark.parametrize( -# 'key, value', [ -# ('/usr/local/met-6.1/bin/point_stat', 382180), -# ('/usr/local/met-6.1/bin/stat_analysis', 3438944), -# ('/usr/local/met-6.1/bin/pb2nc', 3009056) -# -# ] -# ) -# def test_file_sizes(key, value): -# st = stat_analysis_wrapper() -# # Retrieve the value of the class attribute that corresponds -# # to the key in the parametrization -# files_in_dir = [] -# for dirpath, dirnames, files in os.walk("/usr/local/met-6.1/bin"): -# for name in files: -# files_in_dir.append(os.path.join(dirpath, name)) -# if actual_key in files_in_dir: -# # The actual_key is one of the files of interest we retrieved from -# # the output directory. Verify that it's file size is what we -# # expected. -# assert actual_key == key -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -METPLUS_BASE = os.getcwd().split('/internal_tests')[0] +@pytest.mark.wrapper def test_get_command(metplus_config): # Independently test that the stat_analysis command # is being put together correctly with @@ -102,8 +43,10 @@ def test_get_command(metplus_config): st.lookindir = '/path/to/lookin_dir' st.c_dict['CONFIG_FILE'] = '/path/to/STATAnalysisConfig' test_command = st.get_command() - assert(expected_command == test_command) + assert expected_command == test_command + +@pytest.mark.wrapper def test_create_c_dict(metplus_config): # Independently test that c_dict is being created # and that the wrapper and config reader @@ -111,30 +54,32 @@ def test_create_c_dict(metplus_config): st = stat_analysis_wrapper(metplus_config) # Test 1 c_dict = st.create_c_dict() - assert(c_dict['LOOP_ORDER'] == 'times') + assert c_dict['LOOP_ORDER'] == 'times' assert(os.path.realpath(c_dict['CONFIG_FILE']) == (METPLUS_BASE+'/internal_tests/' +'config/STATAnalysisConfig')) assert(c_dict['OUTPUT_DIR'] == (st.config.getdir('OUTPUT_BASE') +'/stat_analysis')) - assert('FCST_INIT_HOUR_LIST' in c_dict['GROUP_LIST_ITEMS']) + assert 'FCST_INIT_HOUR_LIST' in c_dict['GROUP_LIST_ITEMS'] assert('FCST_VALID_HOUR_LIST' in c_dict['LOOP_LIST_ITEMS'] and 'MODEL_LIST' in c_dict['LOOP_LIST_ITEMS']) - assert(c_dict['VAR_LIST'] == []) - assert(c_dict['MODEL_LIST'] == ['MODEL_TEST']) - assert(c_dict['DESC_LIST'] == []) - assert(c_dict['FCST_LEAD_LIST'] == []) - assert(c_dict['OBS_LEAD_LIST'] == []) - assert(c_dict['FCST_VALID_HOUR_LIST'] == ['000000']) - assert(c_dict['FCST_INIT_HOUR_LIST'] == ['000000', '060000', '120000', '180000']) - assert(c_dict['OBS_VALID_HOUR_LIST'] == []) - assert(c_dict['OBS_INIT_HOUR_LIST'] == []) - assert(c_dict['VX_MASK_LIST'] == []) - assert(c_dict['INTERP_MTHD_LIST'] == []) - assert(c_dict['INTERP_PNTS_LIST'] == []) - assert(c_dict['COV_THRESH_LIST'] == []) - assert(c_dict['ALPHA_LIST'] == []) - assert(c_dict['LINE_TYPE_LIST'] == []) - + assert c_dict['VAR_LIST'] == [] + assert c_dict['MODEL_LIST'] == ['MODEL_TEST'] + assert c_dict['DESC_LIST'] == [] + assert c_dict['FCST_LEAD_LIST'] == [] + assert c_dict['OBS_LEAD_LIST'] == [] + assert c_dict['FCST_VALID_HOUR_LIST'] == ['000000'] + assert c_dict['FCST_INIT_HOUR_LIST'] == ['000000', '060000', '120000', '180000'] + assert c_dict['OBS_VALID_HOUR_LIST'] == [] + assert c_dict['OBS_INIT_HOUR_LIST'] == [] + assert c_dict['VX_MASK_LIST'] == [] + assert c_dict['INTERP_MTHD_LIST'] == [] + assert c_dict['INTERP_PNTS_LIST'] == [] + assert c_dict['COV_THRESH_LIST'] == [] + assert c_dict['ALPHA_LIST'] == [] + assert c_dict['LINE_TYPE_LIST'] == [] + + +@pytest.mark.wrapper def test_list_to_str(metplus_config): # Independently test that a list of strings # are being converted to a one @@ -149,6 +94,8 @@ def test_list_to_str(metplus_config): test_list = st.list_to_str([ '0', '1', '2' ]) assert(expected_list == test_list) + +@pytest.mark.wrapper def test_set_lists_as_loop_or_group(metplus_config): # Independently test that the lists that are set # in the config file are being set @@ -209,6 +156,7 @@ def test_set_lists_as_loop_or_group(metplus_config): assert(all(elem in expected_lists_to_loop_items for elem in test_lists_to_loop_items)) + @pytest.mark.parametrize( 'expression, expected_result', [ ('>1', 'gt1'), @@ -223,13 +171,16 @@ def test_set_lists_as_loop_or_group(metplus_config): 'lt805,lt1609,lt4828,lt8045,ge8045,lt16090'), ] ) +@pytest.mark.wrapper def test_format_thresh(metplus_config, expression, expected_result): - # Idependently test the creation of + # Independently test the creation of # string values for defining thresholds st = stat_analysis_wrapper(metplus_config) - assert(st.format_thresh(expression) == expected_result) + assert st.format_thresh(expression) == expected_result + +@pytest.mark.wrapper def test_build_stringsub_dict(metplus_config): # Independently test the building of # the dictionary used in the stringtemplate @@ -431,7 +382,9 @@ def test_build_stringsub_dict(metplus_config): datetime.datetime(1900, 1, 1, 0, 0, 0)) assert(test_stringsub_dict['obs_init_hour_end'] == datetime.datetime(1900, 1, 1, 23, 59 ,59)) - + + +@pytest.mark.wrapper def test_get_output_filename(metplus_config): # Independently test the building of # the output file name @@ -488,7 +441,7 @@ def test_get_output_filename(metplus_config): lists_to_loop, lists_to_group, config_dict) - assert(expected_output_filename == test_output_filename) + assert expected_output_filename == test_output_filename # Test 2 expected_output_filename = ( 'MODEL_TEST_MODEL_TEST_ANL_' @@ -508,7 +461,7 @@ def test_get_output_filename(metplus_config): lists_to_loop, lists_to_group, config_dict) - assert(expected_output_filename == test_output_filename) + assert expected_output_filename == test_output_filename # Test 3 expected_output_filename = ( 'MODEL_TEST_MODEL_TEST_ANL' @@ -528,7 +481,7 @@ def test_get_output_filename(metplus_config): lists_to_loop, lists_to_group, config_dict) - assert(expected_output_filename == test_output_filename) + assert expected_output_filename == test_output_filename # Test 4 expected_output_filename = ( 'MODEL_TEST_MODEL_TEST_ANL' @@ -546,8 +499,10 @@ def test_get_output_filename(metplus_config): lists_to_loop, lists_to_group, config_dict) - assert(expected_output_filename == test_output_filename) + assert expected_output_filename == test_output_filename + +@pytest.mark.wrapper def test_get_lookin_dir(metplus_config): # Independently test the building of # the lookin directory @@ -602,7 +557,7 @@ def test_get_lookin_dir(metplus_config): test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) - assert(expected_lookin_dir == test_lookin_dir) + assert expected_lookin_dir == test_lookin_dir # Test 2 expected_lookin_dir = os.path.join(stat_analysis_pytest_dir, @@ -612,7 +567,7 @@ def test_get_lookin_dir(metplus_config): test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) - assert(expected_lookin_dir == test_lookin_dir) + assert expected_lookin_dir == test_lookin_dir # Test 3 - no matches for lookin dir wildcard expected_lookin_dir = '' @@ -621,8 +576,10 @@ def test_get_lookin_dir(metplus_config): test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) - assert(expected_lookin_dir == test_lookin_dir) + assert expected_lookin_dir == test_lookin_dir + +@pytest.mark.wrapper def test_format_valid_init(metplus_config): # Independently test the formatting # of the valid and initialization date and hours @@ -723,6 +680,8 @@ def test_format_valid_init(metplus_config): assert(config_dict['OBS_INIT_END'] == '20190101_120000') assert(config_dict['OBS_INIT_HOUR'] == '"000000", "120000"') + +@pytest.mark.wrapper def test_parse_model_info(metplus_config): # Independently test the creation of # the model information dictionary @@ -760,6 +719,8 @@ def test_parse_model_info(metplus_config): assert(test_model_info_list[0]['out_stat_filename_type'] == expected_out_stat_filename_type) + +@pytest.mark.wrapper def test_run_stat_analysis(metplus_config): # Test running of stat_analysis st = stat_analysis_wrapper(metplus_config) @@ -772,10 +733,11 @@ def test_run_stat_analysis(metplus_config): st.c_dict['DATE_END'] = '20190101' st.c_dict['DATE_TYPE'] = 'VALID' st.run_stat_analysis() - assert(os.path.exists(expected_filename)) + assert os.path.exists(expected_filename) assert(os.path.getsize(expected_filename) == os.path.getsize(comparison_filename)) + @pytest.mark.parametrize( 'data_type, config_list, expected_list', [ ('FCST', '\"0,*,*\"', ["0,*,*"]), @@ -788,14 +750,17 @@ def test_run_stat_analysis(metplus_config): ('OBS', '\"(0,*,*)\", \"(1,*,*)\"', ["0,*,*", "1,*,*"]), ] ) +@pytest.mark.wrapper def test_get_level_list(metplus_config, data_type, config_list, expected_list): config = metplus_config() config.set('config', f'{data_type}_LEVEL_LIST', config_list) saw = StatAnalysisWrapper(config) - assert(saw.get_level_list(data_type) == expected_list) + assert saw.get_level_list(data_type) == expected_list + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' config = metplus_config() diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py b/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py index 01ed2be651..6356fb1aab 100644 --- a/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py +++ b/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py @@ -1,48 +1,17 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import datetime -import sys -import logging import pytest -import datetime -import glob - -import produtil.setup -from metplus.wrappers.stat_analysis_wrapper import StatAnalysisWrapper -from metplus.util import met_util as util +import os -# -# These are tests (not necessarily unit tests) for the -# MET stat_analysis wrapper, stat_analysis_wrapper.py -# NOTE: This test requires pytest, which is NOT part of the standard Python -# library. -# These tests require one configuration file in addition to the three -# required METplus configuration files: test_stat_analysis.conf. This contains -# the information necessary for running all the tests. Each test can be -# customized to replace various settings if needed. -# +import glob -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# +from metplus.wrappers.stat_analysis_wrapper import StatAnalysisWrapper +from metplus.util import handle_tmp_dir +METPLUS_BASE = os.getcwd().split('/internal_tests')[0] -# Add a test configuration -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") -# @pytest.fixture -def cmdopt(request): - return request.config.getoption("-c") - -# -# ------------Pytest fixtures that can be used for all tests --------------- -# -#@pytest.fixture def stat_analysis_wrapper(metplus_config): """! Returns a default StatAnalysisWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -54,39 +23,11 @@ def stat_analysis_wrapper(metplus_config): extra_configs = [] extra_configs.append(os.path.join(os.path.dirname(__file__), 'test_plotting.conf')) config = metplus_config(extra_configs) - util.handle_tmp_dir(config) + handle_tmp_dir(config) return StatAnalysisWrapper(config) -# ------------------TESTS GO BELOW --------------------------- -# - -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -# To test numerous files for filesize, use parametrization: -# @pytest.mark.parametrize( -# 'key, value', [ -# ('/usr/local/met-6.1/bin/point_stat', 382180), -# ('/usr/local/met-6.1/bin/stat_analysis', 3438944), -# ('/usr/local/met-6.1/bin/pb2nc', 3009056) -# -# ] -# ) -# def test_file_sizes(key, value): -# st = stat_analysis_wrapper() -# # Retrieve the value of the class attribute that corresponds -# # to the key in the parametrization -# files_in_dir = [] -# for dirpath, dirnames, files in os.walk("/usr/local/met-6.1/bin"): -# for name in files: -# files_in_dir.append(os.path.join(dirpath, name)) -# if actual_key in files_in_dir: -# # The actual_key is one of the files of interest we retrieved from -# # the output directory. Verify that it's file size is what we -# # expected. -# assert actual_key == key -#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -METPLUS_BASE = os.getcwd().split('/internal_tests')[0] - +@pytest.mark.wrapper def test_set_lists_as_loop_or_group(metplus_config): # Independently test that the lists that are set # in the config file are being set @@ -148,6 +89,7 @@ def test_set_lists_as_loop_or_group(metplus_config): for elem in test_lists_to_loop_items)) +@pytest.mark.wrapper def test_get_output_filename(metplus_config): # Independently test the building of # the output file name @@ -218,66 +160,10 @@ def test_get_output_filename(metplus_config): lists_to_loop, lists_to_group, config_dict) - assert (expected_output_filename == test_output_filename) - + assert expected_output_filename == test_output_filename -def test_parse_model_info(metplus_config): - pytest.skip("This function will be removed from MakePlots") - # Independently test the creation of - # the model information dictionary - # and the reading from the config file - # are as expected - st = stat_analysis_wrapper(metplus_config) - # Test 1 - expected_name1 = 'MODEL_TEST1' - expected_reference_name1 = 'MODEL_TEST1' - expected_obtype1 = 'MODEL_TEST1_ANL' - expected_dump_row_filename_template1 = ( - '{model?fmt=%s}_{obtype?fmt=%s}_valid{valid_beg?fmt=%Y%m%d}' - 'to{valid_end?fmt=%Y%m%d}_valid{valid_hour_beg?fmt=%H%M}to' - '{valid_hour_end?fmt=%H%M}Z_init{init_hour_beg?fmt=%H%M}to' - '{init_hour_end?fmt=%H%M}Z_fcst_lead{fcst_lead?fmt=%s}_' - 'fcst{fcst_var?fmt=%s}{fcst_level?fmt=%s}{fcst_thresh?fmt=%s}' - '{interp_mthd?fmt=%s}_obs{obs_var?fmt=%s}{obs_level?fmt=%s}' - '{obs_thresh?fmt=%s}{interp_mthd?fmt=%s}_vxmask{vx_mask?fmt=%s}' - '_dump_row.stat' - ) - expected_dump_row_filename_type1 = 'user' - expected_out_stat_filename_template1 = 'NA' - expected_out_stat_filename_type1 = 'NA' - expected_name2 = 'TEST2_MODEL' - expected_reference_name2 = 'TEST2_MODEL' - expected_obtype2 = 'ANLYS2' - expected_dump_row_filename_template2 = expected_dump_row_filename_template1 - expected_dump_row_filename_type2 = 'user' - expected_out_stat_filename_template2 = 'NA' - expected_out_stat_filename_type2 = 'NA' - test_model_info_list = st.parse_model_info() - assert (test_model_info_list[0]['name'] == expected_name1) - assert (test_model_info_list[0]['reference_name'] == - expected_reference_name1) - assert (test_model_info_list[0]['obtype'] == expected_obtype1) - assert (test_model_info_list[0]['dump_row_filename_template'] == - expected_dump_row_filename_template1) - assert (test_model_info_list[0]['dump_row_filename_type'] == - expected_dump_row_filename_type1) - assert (test_model_info_list[0]['out_stat_filename_template'] == - expected_out_stat_filename_template1) - assert (test_model_info_list[0]['out_stat_filename_type'] == - expected_out_stat_filename_type1) - assert (test_model_info_list[1]['name'] == expected_name2) - assert (test_model_info_list[1]['reference_name'] == - expected_reference_name2) - assert (test_model_info_list[1]['obtype'] == expected_obtype2) - assert (test_model_info_list[1]['dump_row_filename_template'] == - expected_dump_row_filename_template2) - assert (test_model_info_list[1]['dump_row_filename_type'] == - expected_dump_row_filename_type2) - assert (test_model_info_list[1]['out_stat_filename_template'] == - expected_out_stat_filename_template2) - assert (test_model_info_list[1]['out_stat_filename_type'] == - expected_out_stat_filename_type2) +@pytest.mark.long def test_filter_for_plotting(metplus_config): # Test running of stat_analysis st = stat_analysis_wrapper(metplus_config) @@ -510,6 +396,6 @@ def test_filter_for_plotting(metplus_config): os.listdir(st.config.getdir('OUTPUT_BASE') +'/plotting/stat_analysis') ) - assert(ntest_files == 32) + assert ntest_files == 32 for expected_filename in expected_filename_list: - assert(os.path.exists(expected_filename)) + assert os.path.exists(expected_filename) diff --git a/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py b/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py index 575fd2bf6e..657021b34a 100644 --- a/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py +++ b/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -import os -import sys import pytest -import datetime + +import os from metplus.wrappers.tc_gen_wrapper import TCGenWrapper + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ @@ -285,6 +285,7 @@ ] ) +@pytest.mark.wrapper def test_tc_gen(metplus_config, config_overrides, env_var_values): # expected number of 2016 files (including file_list line) expected_genesis_count = 7 @@ -382,11 +383,11 @@ def test_tc_gen(metplus_config, config_overrides, env_var_values): all_cmds = wrapper.run_all_times() print(f"ALL COMMANDS: {all_cmds}") - assert(len(all_cmds) == len(expected_cmds)) + assert len(all_cmds) == len(expected_cmds) for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly # including deprecated env vars (not in wrapper env var keys) @@ -396,27 +397,29 @@ def test_tc_gen(metplus_config, config_overrides, env_var_values): for env_var_key in env_var_keys: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None value = match.split('=', 1)[1] - assert(env_var_values.get(env_var_key, '') == value) + assert env_var_values.get(env_var_key, '') == value # verify file count of genesis, edeck, shape, and track file list files with open(genesis_path, 'r') as file_handle: lines = file_handle.read().splitlines() - assert(len(lines) == expected_genesis_count) + assert len(lines) == expected_genesis_count with open(edeck_path, 'r') as file_handle: lines = file_handle.read().splitlines() - assert(len(lines) == expected_edeck_count) + assert len(lines) == expected_edeck_count with open(shape_path, 'r') as file_handle: lines = file_handle.read().splitlines() - assert(len(lines) == expected_shape_count) + assert len(lines) == expected_shape_count with open(track_path, 'r') as file_handle: lines = file_handle.read().splitlines() - assert(len(lines) == expected_track_count) + assert len(lines) == expected_track_count + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py b/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py index a0845be227..265564a29b 100644 --- a/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py +++ b/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 +import pytest + import os from datetime import datetime -import pytest from metplus.wrappers.tc_pairs_wrapper import TCPairsWrapper @@ -15,6 +16,7 @@ time_fmt = '%Y%m%d%H' run_times = ['2014121318'] + def set_minimum_config_settings(config, loop_by='INIT'): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -41,6 +43,7 @@ def set_minimum_config_settings(config, loop_by='INIT'): # can set adeck or edeck variables config.set('config', 'TC_PAIRS_ADECK_TEMPLATE', adeck_template) + @pytest.mark.parametrize( 'config_overrides, isOK', [ ({}, True), @@ -80,6 +83,7 @@ def test_read_storm_info(metplus_config, config_overrides, isOK): ('2020100700_F000_261N_1101W_FOF', 'wildcard', 'wildcard'), ] ) +@pytest.mark.wrapper def test_parse_storm_id(metplus_config, storm_id, basin, cyclone): """! Check that storm ID is parsed properly to get basin and cyclone. Check that it returns wildcard expressions basin and cyclone cannot be @@ -107,6 +111,7 @@ def test_parse_storm_id(metplus_config, storm_id, basin, cyclone): assert actual_basin == expected_basin assert actual_cyclone == expected_cyclone + @pytest.mark.parametrize( 'basin,cyclone,expected_files,expected_wildcard', [ ('al', '0104', ['get_bdeck_balq2014123118.gfso.0104'], False), @@ -123,6 +128,7 @@ def test_parse_storm_id(metplus_config, storm_id, basin, cyclone): 'get_bdeck_bmlq2014123118.gfso.0105'], True), ] ) +@pytest.mark.wrapper def test_get_bdeck(metplus_config, basin, cyclone, expected_files, expected_wildcard): """! Checks that the correct list of empty test files are found and the @@ -150,11 +156,12 @@ def test_get_bdeck(metplus_config, basin, cyclone, expected_files, wrapper = TCPairsWrapper(config) actual_files, actual_wildcard = wrapper._get_bdeck(basin, cyclone, time_info) - assert(actual_wildcard == expected_wildcard) - assert(len(actual_files) == len(expected_files)) + assert actual_wildcard == expected_wildcard + assert len(actual_files) == len(expected_files) for actual_file, expected_file in zip(sorted(actual_files), sorted(expected_files)): - assert(os.path.basename(actual_file) == expected_file) + assert os.path.basename(actual_file) == expected_file + @pytest.mark.parametrize( 'template, filename,other_basin,other_cyclone', [ @@ -178,6 +185,7 @@ def test_get_bdeck(metplus_config, basin, cyclone, expected_files, '20141009bml.dat', 'ml', None), ] ) +@pytest.mark.wrapper def test_get_basin_cyclone_from_bdeck(metplus_config, template, filename, other_cyclone, other_basin): fake_dir = '/fake/dir' @@ -210,6 +218,7 @@ def test_get_basin_cyclone_from_bdeck(metplus_config, template, filename, assert actual_basin == expected_basin assert actual_cyclone == expected_cyclone + @pytest.mark.parametrize( 'config_overrides, storm_type, values_to_check', [ # 0: storm_id @@ -231,6 +240,7 @@ def test_get_basin_cyclone_from_bdeck(metplus_config, template, filename, 'cyclone', ['09', '10', '09', '10']), ] ) +@pytest.mark.wrapper def test_tc_pairs_storm_id_lists(metplus_config, config_overrides, storm_type, values_to_check): config = metplus_config() @@ -272,20 +282,21 @@ def test_tc_pairs_storm_id_lists(metplus_config, config_overrides, print(f"CMD{idx}: {cmd}") print(f"ENV{idx}: {env_list}") - assert(len(all_cmds) == len(values_to_check)) + assert len(all_cmds) == len(values_to_check) for (cmd, env_vars), value_to_check in zip(all_cmds, values_to_check): env_var_key = f'METPLUS_{storm_type.upper()}' match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert (match is not None) + assert match is not None print(f"Checking env var: {env_var_key}") actual_value = match.split('=', 1)[1] expected_value = f'{storm_type} = ["{value_to_check}"];' assert actual_value == expected_value + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ # 0: no config overrides that set env vars @@ -370,6 +381,7 @@ def test_tc_pairs_storm_id_lists(metplus_config, config_overrides, ] ) +@pytest.mark.wrapper def test_tc_pairs_loop_order_processes(metplus_config, config_overrides, env_var_values): # run using init and valid time variables @@ -425,26 +437,27 @@ def test_tc_pairs_loop_order_processes(metplus_config, config_overrides, all_cmds = wrapper.run_all_times() print(f"ALL COMMANDS: {all_cmds}") - assert(len(all_cmds) == len(expected_cmds)) + assert len(all_cmds) == len(expected_cmds) for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # ensure commands are generated as expected - assert(cmd == expected_cmd) + assert cmd == expected_cmd # check that environment variables were set properly for env_var_key in wrapper.WRAPPER_ENV_VAR_KEYS: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None print(f'Checking env var: {env_var_key}') actual_value = match.split('=', 1)[1] - assert(env_var_values.get(env_var_key, '') == actual_value) + assert env_var_values.get(env_var_key, '') == actual_value if remove_beg: del env_var_values[f'METPLUS_{loop_by}_BEG'] if remove_end: del env_var_values[f'METPLUS_{loop_by}_END'] + @pytest.mark.parametrize( 'config_overrides, env_var_values', [ # 0: no config overrides that set env vars @@ -460,6 +473,7 @@ def test_tc_pairs_loop_order_processes(metplus_config, config_overrides, {'METPLUS_CYCLONE': 'cyclone = ["1005", "0104"];'}), ] ) +@pytest.mark.wrapper def test_tc_pairs_read_all_files(metplus_config, config_overrides, env_var_values): # run using init and valid time variables @@ -512,22 +526,24 @@ def test_tc_pairs_read_all_files(metplus_config, config_overrides, all_cmds = wrapper.run_all_times() print(f"ALL COMMANDS: {all_cmds}") - assert(len(all_cmds) == len(expected_cmds)) + assert len(all_cmds) == len(expected_cmds) for (cmd, env_vars), expected_cmd in zip(all_cmds, expected_cmds): # check that environment variables were set properly for env_var_key in wrapper.WRAPPER_ENV_VAR_KEYS: match = next((item for item in env_vars if item.startswith(env_var_key)), None) - assert(match is not None) + assert match is not None print(f'Checking env var: {env_var_key}') actual_value = match.split('=', 1)[1] - assert(env_var_values.get(env_var_key, '') == actual_value) + assert env_var_values.get(env_var_key, '') == actual_value # unset begin and end for next loop del env_var_values[f'METPLUS_{loop_by}_BEG'] del env_var_values[f'METPLUS_{loop_by}_END'] + +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py b/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py index e84b90b09a..fe66cff3f8 100644 --- a/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py +++ b/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py @@ -1,21 +1,22 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 + +import pytest import os import sys -import pytest import datetime -import produtil - from metplus.wrappers.tc_stat_wrapper import TCStatWrapper from metplus.util import ti_calculate + def get_config(metplus_config): extra_configs = [] extra_configs.append(os.path.join(os.path.dirname(__file__), 'tc_stat_conf.conf')) return metplus_config(extra_configs) + def tc_stat_wrapper(metplus_config): """! Returns a default TCStatWrapper with /path/to entries in the metplus_system.conf and metplus_runtime.conf configuration @@ -27,6 +28,7 @@ def tc_stat_wrapper(metplus_config): config = get_config(metplus_config) return TCStatWrapper(config) + @pytest.mark.parametrize( 'overrides, c_dict', [ ({'TC_STAT_INIT_BEG': '20150301', @@ -106,7 +108,8 @@ def tc_stat_wrapper(metplus_config): 'INIT_STR_EXC_VAL': 'init_str_exc_val = ["HUWARN"];'}), ] - ) +) +@pytest.mark.wrapper def test_override_config_in_c_dict(metplus_config, overrides, c_dict): config = get_config(metplus_config) instance = 'tc_stat_overrides' @@ -119,6 +122,7 @@ def test_override_config_in_c_dict(metplus_config, overrides, c_dict): assert (wrapper.env_var_dict.get(f'METPLUS_{key}') == expected_value or wrapper.c_dict.get(key) == expected_value) + @pytest.mark.parametrize( 'jobs, init_dt, expected_output', [ # single fake job @@ -143,6 +147,7 @@ def test_override_config_in_c_dict(metplus_config, overrides, c_dict): ), ] ) +@pytest.mark.wrapper def test_handle_jobs(metplus_config, jobs, init_dt, expected_output): if init_dt: time_info = ti_calculate({'init': init_dt}) @@ -158,7 +163,7 @@ def test_handle_jobs(metplus_config, jobs, init_dt, expected_output): wrapper.c_dict['JOBS'].append(job.replace('', output_dir)) output = wrapper.handle_jobs(time_info) - assert(output == expected_output.replace('', output_dir)) + assert output == expected_output.replace('', output_dir) def cleanup_test_dirs(parent_dirs, output_dir): @@ -168,6 +173,7 @@ def cleanup_test_dirs(parent_dirs, output_dir): if os.path.exists(parent_dir_sub): os.removedirs(parent_dir_sub) + @pytest.mark.parametrize( 'jobs, init_dt, expected_output, parent_dirs', [ # single fake job, no parent dir @@ -216,6 +222,7 @@ def cleanup_test_dirs(parent_dirs, output_dir): ), ] ) +@pytest.mark.wrapper def test_handle_jobs_create_parent_dir(metplus_config, jobs, init_dt, expected_output, parent_dirs): # if init time is provided, calculate other time dict items @@ -254,6 +261,7 @@ def test_handle_jobs_create_parent_dir(metplus_config, jobs, init_dt, cleanup_test_dirs(parent_dirs, output_dir) +@pytest.mark.wrapper def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py b/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py index e50c64ce29..a6f2e416c4 100644 --- a/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py +++ b/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py @@ -1,12 +1,8 @@ #!/usr/bin/env python3 -import os -import sys -import re import pytest -from datetime import datetime -import produtil +import os from metplus.wrappers.tcmpr_plotter_wrapper import TCMPRPlotterWrapper @@ -18,6 +14,7 @@ TIME_FMT = '%Y%m%d%H' RUN_TIME = '20141214' + def set_minimum_config_settings(config): # set config variables to prevent command from running and bypass check # if input files actually exist @@ -37,6 +34,7 @@ def set_minimum_config_settings(config): config.set('config', 'TCMPR_PLOTTER_PLOT_OUTPUT_DIR', '{OUTPUT_BASE}/TCMPRPlotter/tcmpr_plots') + @pytest.mark.parametrize( 'config_overrides,expected_loop_args', [ # 0: no loop args @@ -99,6 +97,7 @@ def set_minimum_config_settings(config): 'plot': [('pitem1', 'P Label 1'), ('pitem2', 'P Label 2')]}), ] ) +@pytest.mark.wrapper def test_read_loop_info(metplus_config, config_overrides, expected_loop_args): config = metplus_config() @@ -111,6 +110,7 @@ def test_read_loop_info(metplus_config, config_overrides, expected_loop_args): wrapper = TCMPRPlotterWrapper(config) assert wrapper.read_loop_info() == expected_loop_args + @pytest.mark.parametrize( 'config_overrides,expected_strings', [ # 0: no optional arguments @@ -178,7 +178,7 @@ def test_read_loop_info(metplus_config, config_overrides, expected_loop_args): '-dep ditem1 -plot pitem1')]), ] ) - +@pytest.mark.wrapper def test_tcmpr_plotter_loop(metplus_config, config_overrides, expected_strings): config = metplus_config() @@ -271,7 +271,7 @@ def test_tcmpr_plotter_loop(metplus_config, config_overrides, ({'TCMPR_PLOTTER_PLOT_TYPES': 'item1'}, '-plot item1'), ] ) - +@pytest.mark.wrapper def test_tcmpr_plotter(metplus_config, config_overrides, expected_string): # add a space before value if expected string has a value if expected_string: diff --git a/internal_tests/pytests/user_script/test_user_script.py b/internal_tests/pytests/user_script/test_user_script.py index de69d76340..b45fe88108 100644 --- a/internal_tests/pytests/user_script/test_user_script.py +++ b/internal_tests/pytests/user_script/test_user_script.py @@ -1,17 +1,13 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -import os -import sys -import re -import logging -from collections import namedtuple import pytest + +import re from datetime import datetime -import produtil from metplus.wrappers.user_script_wrapper import UserScriptWrapper -from metplus.util import time_util + def sub_clock_time(input_cmd, clock_time): """! Helper function to replace clock time from config in expected output @@ -54,6 +50,7 @@ def sub_clock_time(input_cmd, clock_time): return output_cmd + def set_run_type_info(config, run_type): """! Set time values for init or valid time in config object @@ -347,6 +344,7 @@ def set_run_type_info(config, run_type): ['echo a'] * 12 + ['echo b'] * 12), ] ) +@pytest.mark.wrapper def test_run_user_script_all_times(metplus_config, input_configs, run_types, expected_cmds): config = metplus_config() diff --git a/internal_tests/pytests/config/config_1.conf b/internal_tests/pytests/util/config/config_1.conf similarity index 100% rename from internal_tests/pytests/config/config_1.conf rename to internal_tests/pytests/util/config/config_1.conf diff --git a/internal_tests/pytests/config/config_2.conf b/internal_tests/pytests/util/config/config_2.conf similarity index 100% rename from internal_tests/pytests/config/config_2.conf rename to internal_tests/pytests/util/config/config_2.conf diff --git a/internal_tests/pytests/config/config_3.conf b/internal_tests/pytests/util/config/config_3.conf similarity index 100% rename from internal_tests/pytests/config/config_3.conf rename to internal_tests/pytests/util/config/config_3.conf diff --git a/internal_tests/pytests/config/test_config.py b/internal_tests/pytests/util/config/test_config.py similarity index 99% rename from internal_tests/pytests/config/test_config.py rename to internal_tests/pytests/util/config/test_config.py index a18678af32..c76b46d7a6 100644 --- a/internal_tests/pytests/config/test_config.py +++ b/internal_tests/pytests/util/config/test_config.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import pytest @@ -28,6 +28,7 @@ (None, None), ] ) +@pytest.mark.util def test_getseconds(metplus_config, input_value, result): conf = metplus_config() if input_value is not None: diff --git a/internal_tests/pytests/config_metplus/test_config_metplus.py b/internal_tests/pytests/util/config_metplus/test_config_metplus.py similarity index 99% rename from internal_tests/pytests/config_metplus/test_config_metplus.py rename to internal_tests/pytests/util/config_metplus/test_config_metplus.py index f0e669443b..55b2c407c5 100644 --- a/internal_tests/pytests/config_metplus/test_config_metplus.py +++ b/internal_tests/pytests/util/config_metplus/test_config_metplus.py @@ -9,6 +9,7 @@ def test_get_default_config_list(): test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, os.pardir, 'data', diff --git a/internal_tests/pytests/logging/test_logging.py b/internal_tests/pytests/util/logging/test_logging.py similarity index 98% rename from internal_tests/pytests/logging/test_logging.py rename to internal_tests/pytests/util/logging/test_logging.py index 7e31851c3a..cacebd7bd8 100644 --- a/internal_tests/pytests/logging/test_logging.py +++ b/internal_tests/pytests/util/logging/test_logging.py @@ -1,9 +1,10 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 + +import pytest import logging import re import os -import pytest # # -----------Mandatory----------- diff --git a/internal_tests/pytests/met_config/test_met_config.py b/internal_tests/pytests/util/met_config/test_met_config.py similarity index 100% rename from internal_tests/pytests/met_config/test_met_config.py rename to internal_tests/pytests/util/met_config/test_met_config.py diff --git a/internal_tests/pytests/met_util/test_met_util.py b/internal_tests/pytests/util/met_util/test_met_util.py similarity index 100% rename from internal_tests/pytests/met_util/test_met_util.py rename to internal_tests/pytests/util/met_util/test_met_util.py diff --git a/internal_tests/pytests/metplus_check/test_metplus_check.py b/internal_tests/pytests/util/metplus_check/test_metplus_check.py similarity index 100% rename from internal_tests/pytests/metplus_check/test_metplus_check.py rename to internal_tests/pytests/util/metplus_check/test_metplus_check.py diff --git a/internal_tests/pytests/StringTemplateSubstitution/test_string_template_substitution.py b/internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py similarity index 100% rename from internal_tests/pytests/StringTemplateSubstitution/test_string_template_substitution.py rename to internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py diff --git a/internal_tests/pytests/time_util/test_time_util.py b/internal_tests/pytests/util/time_util/test_time_util.py similarity index 100% rename from internal_tests/pytests/time_util/test_time_util.py rename to internal_tests/pytests/util/time_util/test_time_util.py From 46104bae50032dbc4e475c9f31605dfdf70329e8 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 08:16:18 -0600 Subject: [PATCH 02/35] per #685, change logic to check if test category is not equal to 'pytest' to does not start with 'pytest' to allow groups of pytests --- .github/workflows/testing.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 1b47909874..deff878b83 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -139,24 +139,24 @@ jobs: # copy logs with errors to error_logs directory to save as artifact - name: Save error logs id: save-errors - if: ${{ always() && steps.run_tests.conclusion == 'failure' && matrix.categories != 'pytests' }} + if: ${{ always() && steps.run_tests.conclusion == 'failure' && !startsWith(matrix.categories,'pytests') }} run: .github/jobs/save_error_logs.sh # run difference testing - name: Run difference tests id: run-diff - if: ${{ needs.job_control.outputs.run_diff == 'true' && steps.run_tests.conclusion == 'success' && matrix.categories != 'pytests' }} + if: ${{ needs.job_control.outputs.run_diff == 'true' && steps.run_tests.conclusion == 'success' && !startsWith(matrix.categories,'pytests') }} run: .github/jobs/run_difference_tests.sh ${{ matrix.categories }} ${{ steps.get-artifact-name.outputs.artifact_name }} # copy output data to save as artifact - name: Save output data id: save-output - if: ${{ always() && steps.run_tests.conclusion != 'skipped' && matrix.categories != 'pytests' }} + if: ${{ always() && steps.run_tests.conclusion != 'skipped' && !startsWith(matrix.categories,'pytests') }} run: .github/jobs/copy_output_to_artifact.sh ${{ steps.get-artifact-name.outputs.artifact_name }} - name: Upload output data artifact uses: actions/upload-artifact@v2 - if: ${{ always() && steps.run_tests.conclusion != 'skipped' && matrix.categories != 'pytests' }} + if: ${{ always() && steps.run_tests.conclusion != 'skipped' && !startsWith(matrix.categories,'pytests') }} with: name: ${{ steps.get-artifact-name.outputs.artifact_name }} path: artifact/${{ steps.get-artifact-name.outputs.artifact_name }} From b509feeba0716a102482228b21c42251f2ef92ba Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:07:30 -0600 Subject: [PATCH 03/35] per #685, run pytests with markers to subset tests into groups --- .github/actions/run_tests/entrypoint.sh | 7 ++++--- .github/jobs/get_use_cases_to_run.sh | 10 ++++++++-- .github/parm/pytest_groups.txt | 2 ++ internal_tests/pytests/pytest.ini | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 .github/parm/pytest_groups.txt diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 53692ab10f..e91e3977d7 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -33,7 +33,7 @@ fi # # running unit tests (pytests) # -if [ "$INPUT_CATEGORIES" == "pytests" ]; then +if [ "$INPUT_CATEGORIES" == pytests* ]; then export METPLUS_ENV_TAG="pytest" export METPLUS_IMG_TAG=${branch_name} echo METPLUS_ENV_TAG=${METPLUS_ENV_TAG} @@ -55,8 +55,9 @@ if [ "$INPUT_CATEGORIES" == "pytests" ]; then -f .github/actions/run_tests/Dockerfile.run \ . - echo Running Pytests - command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus" + marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` + echo Running Pytests marker=$marker + command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m $marker" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" exit $? fi diff --git a/.github/jobs/get_use_cases_to_run.sh b/.github/jobs/get_use_cases_to_run.sh index 39c250474c..0c0790e4a2 100755 --- a/.github/jobs/get_use_cases_to_run.sh +++ b/.github/jobs/get_use_cases_to_run.sh @@ -1,6 +1,7 @@ #! /bin/bash use_case_groups_filepath=.github/parm/use_case_groups.json +pytests_groups_filepath=.github/parm/pytest_groups.txt # set matrix to string of an empty array in case no use cases will be run matrix="[]" @@ -31,12 +32,17 @@ fi if [ "$run_unit_tests" == "true" ]; then echo Adding unit tests to list to run + pytests="" + for x in `cat $pytests_groups_filepath`; do + pytests=$pytests"\"pytests_$x\"," + done + # if matrix is empty, set to an array that only includes pytests if [ "$matrix" == "[]" ]; then - matrix="[\"pytests\"]" + matrix="[${pytests:0: -1}]" # otherwise prepend item to list else - matrix="[\"pytests\", ${matrix:1}" + matrix="[${pytests}${matrix:1}" fi fi diff --git a/.github/parm/pytest_groups.txt b/.github/parm/pytest_groups.txt new file mode 100644 index 0000000000..5a7c92618e --- /dev/null +++ b/.github/parm/pytest_groups.txt @@ -0,0 +1,2 @@ +wrapper +util diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini index 98138a6620..aef09b9d38 100644 --- a/internal_tests/pytests/pytest.ini +++ b/internal_tests/pytests/pytest.ini @@ -1,4 +1,4 @@ [pytest] markers = - util - wrapper + util: custom marker for testing metplus/util logic + wrapper: custom marker for testing metplus/wrapper logic From 57c8ddecaa85cf6fc1535ab5491a8bd2df2e798b Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:14:14 -0600 Subject: [PATCH 04/35] fixed check if string starts with pytests --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index e91e3977d7..63ee484805 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -33,7 +33,7 @@ fi # # running unit tests (pytests) # -if [ "$INPUT_CATEGORIES" == pytests* ]; then +if [[ "$INPUT_CATEGORIES" == pytests* ]]; then export METPLUS_ENV_TAG="pytest" export METPLUS_IMG_TAG=${branch_name} echo METPLUS_ENV_TAG=${METPLUS_ENV_TAG} From 811b256af9b194d1b9d4207dae718bebdc08d9c9 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:26:48 -0600 Subject: [PATCH 05/35] added missing pytest marker name --- internal_tests/pytests/pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini index aef09b9d38..e245b56961 100644 --- a/internal_tests/pytests/pytest.ini +++ b/internal_tests/pytests/pytest.ini @@ -2,3 +2,4 @@ markers = util: custom marker for testing metplus/util logic wrapper: custom marker for testing metplus/wrapper logic + long: custom marker for tests that take a long time to run \ No newline at end of file From c4875680cda33c8f5dece687b50a8e9688beed42 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:27:34 -0600 Subject: [PATCH 06/35] added logic to support running all pytests that do not match a given marker with the 'not ' syntax --- .github/actions/run_tests/entrypoint.sh | 3 +++ .github/parm/pytest_groups.txt | 2 ++ 2 files changed, 5 insertions(+) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 63ee484805..3e73be26c8 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -56,6 +56,9 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then . marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` + if [[ "$marker" == not* ]]; then + marker=not `echo marker | awk -F_ '{print $2}'` + fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m $marker" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" diff --git a/.github/parm/pytest_groups.txt b/.github/parm/pytest_groups.txt index 5a7c92618e..98156cd4b1 100644 --- a/.github/parm/pytest_groups.txt +++ b/.github/parm/pytest_groups.txt @@ -1,2 +1,4 @@ wrapper util +not_wrapper +long From f5b31dae7f18951ab62d2a50ebd54dcd30e3fd5f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:53:00 -0600 Subject: [PATCH 07/35] change pytest group to wrapper because the test expects another test to have run prior to running --- .../pytests/stat_analysis/test_stat_analysis_plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py b/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py index 6356fb1aab..bd93351a15 100644 --- a/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py +++ b/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py @@ -163,7 +163,7 @@ def test_get_output_filename(metplus_config): assert expected_output_filename == test_output_filename -@pytest.mark.long +@pytest.mark.wrapper def test_filter_for_plotting(metplus_config): # Test running of stat_analysis st = stat_analysis_wrapper(metplus_config) From 95ef58e3747684e4bda3f6f581a00ca6caba0dcc Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 09:53:21 -0600 Subject: [PATCH 08/35] fix 'not' logic by adding quotation marks around value --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 3e73be26c8..a51ca0c7c3 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -57,7 +57,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` if [[ "$marker" == not* ]]; then - marker=not `echo marker | awk -F_ '{print $2}'` + marker=\"not `echo marker | awk -F_ '{print $2}'`\" fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m $marker" From 3bdab1e2f3826e49f387ad630d3b3bf2251bd37f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:08:00 -0600 Subject: [PATCH 09/35] another approach to fixing not functionality for tests --- .github/actions/run_tests/entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index a51ca0c7c3..b662c504de 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -57,10 +57,10 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` if [[ "$marker" == not* ]]; then - marker=\"not `echo marker | awk -F_ '{print $2}'`\" + marker=not `echo marker | awk -F_ '{print $2}'` fi echo Running Pytests marker=$marker - command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m $marker" + command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" exit $? fi From b66448e4551a58027a7427b098efd95977bc9b5a Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:15:49 -0600 Subject: [PATCH 10/35] added util marker to more tests --- .../pytests/util/config/test_config.py | 59 +++++---- .../config_metplus/test_config_metplus.py | 124 ++++++++++++------ 2 files changed, 124 insertions(+), 59 deletions(-) diff --git a/internal_tests/pytests/util/config/test_config.py b/internal_tests/pytests/util/config/test_config.py index c76b46d7a6..7c054ab3d8 100644 --- a/internal_tests/pytests/util/config/test_config.py +++ b/internal_tests/pytests/util/config/test_config.py @@ -1,16 +1,14 @@ #!/usr/bin/env python3 -import sys import pytest -import datetime + import os from configparser import NoOptionError from shutil import which -import produtil - from metplus.util import met_util as util + @pytest.mark.parametrize( 'input_value, result', [ (3600, 3600), @@ -36,10 +34,11 @@ def test_getseconds(metplus_config, input_value, result): try: seconds = conf.getseconds('config', 'TEST_SECONDS') - assert(seconds == result) + assert seconds == result except NoOptionError: if result is None: - assert(True) + assert True + # value = None -- config variable not set @pytest.mark.parametrize( @@ -56,6 +55,7 @@ def test_getseconds(metplus_config, input_value, result): (None, '1', '1'), ] ) +@pytest.mark.util def test_getstr(metplus_config, input_value, default, result): conf = metplus_config() if input_value is not None: @@ -63,10 +63,11 @@ def test_getstr(metplus_config, input_value, default, result): # catch NoOptionError exception and pass test if default is None try: - assert(result == conf.getstr('config', 'TEST_GETSTR', default)) + assert result == conf.getstr('config', 'TEST_GETSTR', default) except NoOptionError: if default is None: - assert(True) + assert True + # value = None -- config variable not set @pytest.mark.parametrize( @@ -79,6 +80,7 @@ def test_getstr(metplus_config, input_value, default, result): ] ) +@pytest.mark.util def test_getdir(metplus_config, input_value, default, result): conf = metplus_config() if input_value is not None: @@ -86,13 +88,14 @@ def test_getdir(metplus_config, input_value, default, result): # catch NoOptionError exception and pass test if default is None try: - assert(result == conf.getdir('TEST_GETSTR', default=default)) + assert result == conf.getdir('TEST_GETSTR', default=default) except NoOptionError: if result is 'NoOptionError': - assert(True) + assert True except ValueError: if result is 'ValueError': - assert(True) + assert True + # value = None -- config variable not set @pytest.mark.parametrize( @@ -105,6 +108,7 @@ def test_getdir(metplus_config, input_value, default, result): ('{valid?fmt=%Y%m%d}_{NOT_REAL_VAR}', None, '{valid?fmt=%Y%m%d}_{NOT_REAL_VAR}'), ] ) +@pytest.mark.util def test_getraw(metplus_config, input_value, default, result): conf = metplus_config() conf.set('config', 'TEST_EXTRA', 'extra') @@ -113,7 +117,7 @@ def test_getraw(metplus_config, input_value, default, result): if input_value is not None: conf.set('config', 'TEST_GETRAW', input_value) - assert(result == conf.getraw('config', 'TEST_GETRAW', default=default)) + assert result == conf.getraw('config', 'TEST_GETRAW', default=default) # value = None -- config variable not set @@ -138,6 +142,7 @@ def test_getraw(metplus_config, input_value, default, result): (None, None, None), ] ) +@pytest.mark.util def test_getbool(metplus_config, input_value, default, result): conf = metplus_config() if input_value is not None: @@ -145,10 +150,11 @@ def test_getbool(metplus_config, input_value, default, result): # catch NoOptionError exception and pass test if default is None try: - assert(result == conf.getbool('config', 'TEST_GETBOOL', default)) + assert result == conf.getbool('config', 'TEST_GETBOOL', default) except NoOptionError: if result is None: - assert(True) + assert True + # value = None -- config variable not set @pytest.mark.parametrize( @@ -159,12 +165,13 @@ def test_getbool(metplus_config, input_value, default, result): ('sh', which('sh')), ] ) +@pytest.mark.util def test_getexe(metplus_config, input_value, result): conf = metplus_config() if input_value is not None: conf.set('config', 'TEST_GETEXE', input_value) - assert(result == conf.getexe('TEST_GETEXE')) + assert result == conf.getexe('TEST_GETEXE') # value = None -- config variable not set @pytest.mark.parametrize( @@ -187,10 +194,11 @@ def test_getfloat(metplus_config, input_value, default, result): conf.set('config', 'TEST_GETFLOAT', input_value) try: - assert(result == conf.getfloat('config', 'TEST_GETFLOAT', default)) + assert result == conf.getfloat('config', 'TEST_GETFLOAT', default) except ValueError: if result is None: - assert(True) + assert True + # value = None -- config variable not set @pytest.mark.parametrize( @@ -210,16 +218,18 @@ def test_getfloat(metplus_config, input_value, default, result): ('', 2.2, util.MISSING_DATA_VALUE), ] ) +@pytest.mark.util def test_getint(metplus_config, input_value, default, result): conf = metplus_config() if input_value is not None: conf.set('config', 'TEST_GETINT', input_value) try: - assert(result == conf.getint('config', 'TEST_GETINT', default)) + assert result == conf.getint('config', 'TEST_GETINT', default) except ValueError: if result is None: - assert(True) + assert True + @pytest.mark.parametrize( 'config_key, expected_result', [ @@ -230,6 +240,7 @@ def test_getint(metplus_config, input_value, default, result): ('VAR_TO_TEST_A', 'A3'), ] ) +@pytest.mark.util def test_move_all_to_config_section(metplus_config, config_key, expected_result): config_files = ['config_1.conf', 'config_2.conf', @@ -238,7 +249,8 @@ def test_move_all_to_config_section(metplus_config, config_key, expected_result) test_dir = os.path.dirname(__file__) config_files = [os.path.join(test_dir, item) for item in config_files] config = metplus_config(config_files) - assert(config.getstr('config', config_key) == expected_result) + assert config.getstr('config', config_key) == expected_result + @pytest.mark.parametrize( 'overrides, config_key, expected_result', [ @@ -267,10 +279,12 @@ def test_move_all_to_config_section(metplus_config, config_key, expected_result) 'CMD_LINE_1', '2'), ] ) +@pytest.mark.util def test_move_all_to_config_section_cmd_line(metplus_config, overrides, config_key, expected_result): config = metplus_config(overrides) - assert(config.getstr('config', config_key, '') == expected_result) + assert config.getstr('config', config_key, '') == expected_result + @pytest.mark.parametrize( 'config_name, expected_result', [ @@ -315,6 +329,7 @@ def test_move_all_to_config_section_cmd_line(metplus_config, overrides, ), ] ) +@pytest.mark.util def test_getraw_nested_curly_braces(metplus_config, config_name, expected_result): @@ -324,4 +339,4 @@ def test_getraw_nested_curly_braces(metplus_config, config_files = [os.path.join(test_dir, item) for item in config_files] config = metplus_config(config_files) sec, name = config_name.split('.', 1) - assert(config.getraw(sec, name) == expected_result) + assert config.getraw(sec, name) == expected_result diff --git a/internal_tests/pytests/util/config_metplus/test_config_metplus.py b/internal_tests/pytests/util/config_metplus/test_config_metplus.py index 55b2c407c5..07a32655f5 100644 --- a/internal_tests/pytests/util/config_metplus/test_config_metplus.py +++ b/internal_tests/pytests/util/config_metplus/test_config_metplus.py @@ -1,12 +1,15 @@ #!/usr/bin/env python3 import pytest + import pprint import os from datetime import datetime from metplus.util import config_metplus + +@pytest.mark.util def test_get_default_config_list(): test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, @@ -38,6 +41,7 @@ def test_get_default_config_list(): assert actual_new == expected_new assert actual_both == expected_both + @pytest.mark.parametrize( 'regex,index,id,expected_result', [ # 0: No ID @@ -65,6 +69,7 @@ def test_get_default_config_list(): '2': ['NAME', 'MEMBERS', 'REQUIRED', 'MIN_REQ']}), ] ) +@pytest.mark.util def test_find_indices_in_config_section(metplus_config, regex, index, id, expected_result): config = metplus_config() @@ -84,7 +89,6 @@ def test_find_indices_in_config_section(metplus_config, regex, index, config.set('config', 'TC_PAIRS_CONSENSUS2_REQUIRED', 'True') config.set('config', 'TC_PAIRS_CONSENSUS2_MIN_REQ', '2') - indices = config_metplus.find_indices_in_config_section(regex, config, index_index=index, id_index=id) @@ -95,6 +99,7 @@ def test_find_indices_in_config_section(metplus_config, regex, index, assert indices == expected_result + @pytest.mark.parametrize( 'conf_items, met_tool, expected_result', [ ({'CUSTOM_LOOP_LIST': "one, two, three"}, '', ['one', 'two', 'three']), @@ -111,12 +116,14 @@ def test_find_indices_in_config_section(metplus_config, regex, index, 'POINT2GRID_CUSTOM_LOOP_LIST': "four, five",}, 'point2grid', ['four', 'five']), ] ) +@pytest.mark.util def test_get_custom_string_list(metplus_config, conf_items, met_tool, expected_result): config = metplus_config() for conf_key, conf_value in conf_items.items(): config.set('config', conf_key, conf_value) - assert(config_metplus.get_custom_string_list(config, met_tool) == expected_result) + assert config_metplus.get_custom_string_list(config, met_tool) == expected_result + @pytest.mark.parametrize( 'config_var_name, expected_indices, set_met_tool', [ @@ -134,6 +141,7 @@ def test_get_custom_string_list(metplus_config, conf_items, met_tool, expected_r ('BOTH_VAR12_FIELD_NAME', ['12'], False), ] ) +@pytest.mark.util def test_find_var_indices_fcst(metplus_config, config_var_name, expected_indices, @@ -146,9 +154,10 @@ def test_find_var_indices_fcst(metplus_config, data_types=data_types, met_tool=met_tool) - assert(len(var_name_indices) == len(expected_indices)) + assert len(var_name_indices) == len(expected_indices) for actual_index in var_name_indices: - assert(actual_index in expected_indices) + assert actual_index in expected_indices + @pytest.mark.parametrize( 'data_type, met_tool, expected_out', [ @@ -173,10 +182,12 @@ def test_find_var_indices_fcst(metplus_config, ] ) +@pytest.mark.util def test_get_field_search_prefixes(data_type, met_tool, expected_out): assert(config_metplus.get_field_search_prefixes(data_type, met_tool) == expected_out) + @pytest.mark.parametrize( 'item_list, extension, is_valid', [ (['FCST'], 'NAME', False), @@ -216,9 +227,11 @@ def test_get_field_search_prefixes(data_type, met_tool, expected_out): ] ) +@pytest.mark.util def test_is_var_item_valid(metplus_config, item_list, extension, is_valid): conf = metplus_config() - assert(config_metplus.is_var_item_valid(item_list, '1', extension, conf)[0] == is_valid) + assert config_metplus.is_var_item_valid(item_list, '1', extension, conf)[0] == is_valid + @pytest.mark.parametrize( 'item_list, configs_to_set, is_valid', [ @@ -257,12 +270,14 @@ def test_is_var_item_valid(metplus_config, item_list, extension, is_valid): ] ) +@pytest.mark.util def test_is_var_item_valid_levels(metplus_config, item_list, configs_to_set, is_valid): conf = metplus_config() for key, value in configs_to_set.items(): conf.set('config', key, value) - assert(config_metplus.is_var_item_valid(item_list, '1', 'LEVELS', conf)[0] == is_valid) + assert config_metplus.is_var_item_valid(item_list, '1', 'LEVELS', conf)[0] == is_valid + # search prefixes are valid prefixes to append to field info variables # config_overrides are a dict of config vars and their values @@ -301,6 +316,7 @@ def test_is_var_item_valid_levels(metplus_config, item_list, configs_to_set, is_ ] ) +@pytest.mark.util def test_get_field_config_variables(metplus_config, search_prefixes, config_overrides, @@ -318,7 +334,8 @@ def test_get_field_config_variables(metplus_config, index, search_prefixes) - assert(field_configs.get(field_info_type) == expected_value) + assert field_configs.get(field_info_type) == expected_value + @pytest.mark.parametrize( 'config_keys, field_key, expected_value', [ @@ -366,6 +383,7 @@ def test_get_field_config_variables(metplus_config, ([], 'output_names', None), ] ) +@pytest.mark.util def test_get_field_config_variables_synonyms(metplus_config, config_keys, field_key, @@ -380,7 +398,8 @@ def test_get_field_config_variables_synonyms(metplus_config, index, [prefix]) - assert(field_configs.get(field_key) == expected_value) + assert field_configs.get(field_key) == expected_value + # field info only defined in the FCST_* variables @pytest.mark.parametrize( @@ -390,6 +409,7 @@ def test_get_field_config_variables_synonyms(metplus_config, ('OBS', False), ] ) +@pytest.mark.util def test_parse_var_list_fcst_only(metplus_config, data_type, list_created): conf = metplus_config() conf.set('config', 'FCST_VAR1_NAME', "NAME1") @@ -399,7 +419,7 @@ def test_parse_var_list_fcst_only(metplus_config, data_type, list_created): # this should not occur because OBS variables are missing if config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf, time_info=None, data_type=data_type) @@ -415,7 +435,8 @@ def test_parse_var_list_fcst_only(metplus_config, data_type, list_created): var_list[2]['fcst_level'] == "LEVELS21" and \ var_list[3]['fcst_level'] == "LEVELS22") else: - assert(not var_list) + assert not var_list + # field info only defined in the OBS_* variables @pytest.mark.parametrize( @@ -425,6 +446,7 @@ def test_parse_var_list_fcst_only(metplus_config, data_type, list_created): ('FCST', False), ] ) +@pytest.mark.util def test_parse_var_list_obs(metplus_config, data_type, list_created): conf = metplus_config() conf.set('config', 'OBS_VAR1_NAME', "NAME1") @@ -434,7 +456,7 @@ def test_parse_var_list_obs(metplus_config, data_type, list_created): # this should not occur because FCST variables are missing if config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf, time_info=None, data_type=data_type) @@ -450,7 +472,7 @@ def test_parse_var_list_obs(metplus_config, data_type, list_created): var_list[2]['obs_level'] == "LEVELS21" and \ var_list[3]['obs_level'] == "LEVELS22") else: - assert(not var_list) + assert not var_list # field info only defined in the BOTH_* variables @@ -461,6 +483,7 @@ def test_parse_var_list_obs(metplus_config, data_type, list_created): ('OBS', 'obs'), ] ) +@pytest.mark.util def test_parse_var_list_both(metplus_config, data_type, list_created): conf = metplus_config() conf.set('config', 'BOTH_VAR1_NAME', "NAME1") @@ -470,7 +493,7 @@ def test_parse_var_list_both(metplus_config, data_type, list_created): # this should not occur because BOTH variables are used if not config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf, time_info=None, data_type=data_type) print(f'var_list:{var_list}') @@ -483,9 +506,11 @@ def test_parse_var_list_both(metplus_config, data_type, list_created): not var_list[1][f'{list_to_check}_level'] == "LEVELS12" or \ not var_list[2][f'{list_to_check}_level'] == "LEVELS21" or \ not var_list[3][f'{list_to_check}_level'] == "LEVELS22": - assert(False) + assert False + # field info defined in both FCST_* and OBS_* variables +@pytest.mark.util def test_parse_var_list_fcst_and_obs(metplus_config): conf = metplus_config() conf.set('config', 'FCST_VAR1_NAME', "FNAME1") @@ -499,7 +524,7 @@ def test_parse_var_list_fcst_and_obs(metplus_config): # this should not occur because FCST and OBS variables are found if not config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf) @@ -520,7 +545,9 @@ def test_parse_var_list_fcst_and_obs(metplus_config): var_list[3]['fcst_level'] == "FLEVELS22" and \ var_list[3]['obs_level'] == "OLEVELS22") + # VAR1 defined by FCST, VAR2 defined by OBS +@pytest.mark.util def test_parse_var_list_fcst_and_obs_alternate(metplus_config): conf = metplus_config() conf.set('config', 'FCST_VAR1_NAME', "FNAME1") @@ -531,6 +558,7 @@ def test_parse_var_list_fcst_and_obs_alternate(metplus_config): # configuration is invalid and parse var list should not give any results assert(not config_metplus.validate_configuration_variables(conf, force_check=True)[1] and not config_metplus.parse_var_list(conf)) + # VAR1 defined by OBS, VAR2 by FCST, VAR3 by both FCST AND OBS @pytest.mark.parametrize( 'data_type, list_len, name_levels', [ @@ -539,6 +567,7 @@ def test_parse_var_list_fcst_and_obs_alternate(metplus_config): ('OBS', 4, ('ONAME1:OLEVELS11','ONAME1:OLEVELS12','ONAME3:OLEVELS31','ONAME3:OLEVELS32')), ] ) +@pytest.mark.util def test_parse_var_list_fcst_and_obs_and_both(metplus_config, data_type, list_len, name_levels): conf = metplus_config() conf.set('config', 'OBS_VAR1_NAME', "ONAME1") @@ -552,15 +581,15 @@ def test_parse_var_list_fcst_and_obs_and_both(metplus_config, data_type, list_le # configuration is invalid and parse var list should not give any results if config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf, time_info=None, data_type=data_type) if len(var_list) != list_len: - assert(False) + assert False if data_type is None: - assert(len(var_list) == 0) + assert len(var_list) == 0 if name_levels is not None: dt_lower = data_type.lower() @@ -572,12 +601,13 @@ def test_parse_var_list_fcst_and_obs_and_both(metplus_config, data_type, list_le for expect, reality in zip(expected,var_list): if expect[f'{dt_lower}_name'] != reality[f'{dt_lower}_name']: - assert(False) + assert False if expect[f'{dt_lower}_level'] != reality[f'{dt_lower}_level']: - assert(False) + assert False + + assert True - assert(True) # option defined in obs only @pytest.mark.parametrize( @@ -587,6 +617,7 @@ def test_parse_var_list_fcst_and_obs_and_both(metplus_config, data_type, list_le ('OBS', 0), ] ) +@pytest.mark.util def test_parse_var_list_fcst_only_options(metplus_config, data_type, list_len): conf = metplus_config() conf.set('config', 'FCST_VAR1_NAME', "NAME1") @@ -596,11 +627,12 @@ def test_parse_var_list_fcst_only_options(metplus_config, data_type, list_len): # this should not occur because OBS variables are missing if config_metplus.validate_configuration_variables(conf, force_check=True)[1]: - assert(False) + assert False var_list = config_metplus.parse_var_list(conf, time_info=None, data_type=data_type) - assert(len(var_list) == list_len) + assert len(var_list) == list_len + @pytest.mark.parametrize( 'met_tool, indices', [ @@ -609,6 +641,7 @@ def test_parse_var_list_fcst_only_options(metplus_config, data_type, list_len): ('ENSEMBLE_STAT', {}), ] ) +@pytest.mark.util def test_find_var_indices_wrapper_specific(metplus_config, met_tool, indices): conf = metplus_config() data_type = 'FCST' @@ -618,11 +651,13 @@ def test_find_var_indices_wrapper_specific(metplus_config, met_tool, indices): var_name_indices = config_metplus.find_var_name_indices(conf, data_types=[data_type], met_tool=met_tool) - assert(var_name_indices == indices) + assert var_name_indices == indices + # ensure that the field configuration used for # met_tool_wrapper/EnsembleStat/EnsembleStat.conf # works as expected +@pytest.mark.util def test_parse_var_list_ensemble(metplus_config): config = metplus_config() config.set('config', 'ENS_VAR1_NAME', 'APCP') @@ -705,13 +740,15 @@ def test_parse_var_list_ensemble(metplus_config): assert(len(ensemble_var_list) == len(expected_ens_list)) for actual_ens, expected_ens in zip(ensemble_var_list, expected_ens_list): for key, value in expected_ens.items(): - assert(actual_ens.get(key) == value) + assert actual_ens.get(key) == value assert(len(var_list) == len(expected_var_list)) for actual_var, expected_var in zip(var_list, expected_var_list): for key, value in expected_var.items(): - assert(actual_var.get(key) == value) + assert actual_var.get(key) == value + +@pytest.mark.util def test_parse_var_list_series_by(metplus_config): config = metplus_config() config.set('config', 'BOTH_EXTRACT_TILES_VAR1_NAME', 'RH') @@ -769,16 +806,18 @@ def test_parse_var_list_series_by(metplus_config): print(f'SeriesAnalysis var list:') pp.pprint(actual_sa_list) - assert(len(actual_et_list) == len(expected_et_list)) + assert len(actual_et_list) == len(expected_et_list) for actual_et, expected_et in zip(actual_et_list, expected_et_list): for key, value in expected_et.items(): - assert(actual_et.get(key) == value) + assert actual_et.get(key) == value assert(len(actual_sa_list) == len(expected_sa_list)) for actual_sa, expected_sa in zip(actual_sa_list, expected_sa_list): for key, value in expected_sa.items(): - assert(actual_sa.get(key) == value) + assert actual_sa.get(key) == value + +@pytest.mark.util def test_parse_var_list_priority_fcst(metplus_config): priority_list = ['FCST_GRID_STAT_VAR1_NAME', 'FCST_GRID_STAT_VAR1_INPUT_FIELD_NAME', @@ -807,13 +846,15 @@ def test_parse_var_list_priority_fcst(metplus_config): data_type='FCST', met_tool='grid_stat') - assert(len(var_list) == 1) - assert(var_list[0].get('fcst_name') == priority_list[0].lower()) + assert len(var_list) == 1 + assert var_list[0].get('fcst_name') == priority_list[0].lower() priority_list.pop(0) + # test that if wrapper specific field info is specified, it only gets # values from that list. All generic values should be read if no # wrapper specific field info variables are specified +@pytest.mark.util def test_parse_var_list_wrapper_specific(metplus_config): conf = metplus_config() conf.set('config', 'FCST_VAR1_NAME', "ENAME1") @@ -847,6 +888,7 @@ def test_parse_var_list_wrapper_specific(metplus_config): g_var_list[0]['fcst_level'] == "GLEVELS11" and g_var_list[1]['fcst_level'] == "GLEVELS12") + @pytest.mark.parametrize( 'config_overrides, expected_results', [ # 2 levels @@ -897,6 +939,7 @@ def test_parse_var_list_wrapper_specific(metplus_config): ]), ] ) +@pytest.mark.util def test_parse_var_list_py_embed_multi_levels(metplus_config, config_overrides, expected_results): config = metplus_config() @@ -907,19 +950,19 @@ def test_parse_var_list_py_embed_multi_levels(metplus_config, config_overrides, var_list = config_metplus.parse_var_list(config, time_info=time_info, data_type=None) - assert(len(var_list) == len(expected_results)) + assert len(var_list) == len(expected_results) for var_item, expected_result in zip(var_list, expected_results): - assert(var_item['fcst_name'] == expected_result) + assert var_item['fcst_name'] == expected_result # run again with data type specified var_list = config_metplus.parse_var_list(config, time_info=time_info, data_type='FCST') - assert(len(var_list) == len(expected_results)) + assert len(var_list) == len(expected_results) for var_item, expected_result in zip(var_list, expected_results): - assert(var_item['fcst_name'] == expected_result) + assert var_item['fcst_name'] == expected_result @pytest.mark.parametrize( @@ -956,12 +999,14 @@ def test_parse_var_list_py_embed_multi_levels(metplus_config, config_overrides, ('StatAnalysis, MakePlots', ['StatAnalysis']), ] ) +@pytest.mark.util def test_get_process_list(metplus_config, input_list, expected_list): conf = metplus_config() conf.set('config', 'PROCESS_LIST', input_list) process_list = config_metplus.get_process_list(conf) output_list = [item[0] for item in process_list] - assert(output_list == expected_list) + assert output_list == expected_list + @pytest.mark.parametrize( 'input_list, expected_list', [ @@ -988,12 +1033,15 @@ def test_get_process_list(metplus_config, input_list, expected_list): ('TCStat', 'two')]), ] ) +@pytest.mark.util def test_get_process_list_instances(metplus_config, input_list, expected_list): conf = metplus_config() conf.set('config', 'PROCESS_LIST', input_list) output_list = config_metplus.get_process_list(conf) - assert(output_list == expected_list) + assert output_list == expected_list + +@pytest.mark.util def test_getraw_sub_and_nosub(metplus_config): raw_string = '{MODEL}_{CURRENT_FCST_NAME}' sub_actual = 'FCST_NAME' @@ -1008,6 +1056,8 @@ def test_getraw_sub_and_nosub(metplus_config): sub_value = config.getraw('config', 'OUTPUT_PREFIX', sub_vars=True) assert sub_value == sub_actual + +@pytest.mark.util def test_getraw_instance_with_unset_var(metplus_config): """! Replicates bug where CURRENT_FCST_NAME is substituted with an empty string when copied from an instance section From 6afeaecfe2efc5868a66ecca2234a3591d40dc3c Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:15:57 -0600 Subject: [PATCH 11/35] fixed typo in not logic --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index b662c504de..be090c3d6e 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -57,7 +57,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` if [[ "$marker" == not* ]]; then - marker=not `echo marker | awk -F_ '{print $2}'` + marker=not `echo $marker | awk -F_ '{print $2}'` fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" From aed62bda4afa11f93380ace816516736a2678466 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:26:53 -0600 Subject: [PATCH 12/35] added util marker to more tests again --- .../pytests/util/logging/test_logging.py | 25 +--- .../util/met_config/test_met_config.py | 23 +++- .../pytests/util/met_util/test_met_util.py | 112 ++++++++++++------ 3 files changed, 100 insertions(+), 60 deletions(-) diff --git a/internal_tests/pytests/util/logging/test_logging.py b/internal_tests/pytests/util/logging/test_logging.py index cacebd7bd8..68eca3262d 100644 --- a/internal_tests/pytests/util/logging/test_logging.py +++ b/internal_tests/pytests/util/logging/test_logging.py @@ -6,22 +6,8 @@ import re import os -# -# -----------Mandatory----------- -# configuration and fixture to support METplus configuration files beyond -# the metplus_data, metplus_system, and metplus_runtime conf files. -# - - -# Add a test configuration -def pytest_addoption(parser): - parser.addoption("-c", action="store", help=" -c ") - - -# @pytest.fixture -def cmdopt(request): - return request.config.getoption("-c") +@pytest.mark.util def test_log_level(metplus_config): # Verify that the log level is set to what we indicated in the config file. config = metplus_config() @@ -31,6 +17,7 @@ def test_log_level(metplus_config): assert fixture_logger.isEnabledFor(level) +@pytest.mark.util def test_log_level_key(metplus_config): # Verify that the LOG_LEVEL key is in the config file config_instance = metplus_config() @@ -39,6 +26,7 @@ def test_log_level_key(metplus_config): assert config_instance.has_option(section, option) +@pytest.mark.util def test_logdir_exists(metplus_config): # Verify that the expected log dir exists. config = metplus_config() @@ -48,6 +36,7 @@ def test_logdir_exists(metplus_config): assert os.path.exists(log_dir) +@pytest.mark.util def test_logfile_exists(metplus_config): # Verify that a logfile with format metplus.log exists # We are assuming that there can be numerous files in the log directory. @@ -68,9 +57,3 @@ def test_logfile_exists(metplus_config): else: # There is no log directory assert False - - - - - - diff --git a/internal_tests/pytests/util/met_config/test_met_config.py b/internal_tests/pytests/util/met_config/test_met_config.py index 0f990e678b..0f3adb6587 100644 --- a/internal_tests/pytests/util/met_config/test_met_config.py +++ b/internal_tests/pytests/util/met_config/test_met_config.py @@ -6,6 +6,7 @@ from metplus.util.met_config import _read_climo_file_name, _read_climo_field from metplus.util import CLIMO_TYPES + @pytest.mark.parametrize( 'config_overrides, expected_value', [ # 0 no relevant config set @@ -30,6 +31,7 @@ '{ name="TMP"; level="(*,*)"; }'), ] ) +@pytest.mark.util def test_read_climo_field(metplus_config, config_overrides, expected_value): app_name = 'app' for climo_type in ('MEAN', 'STDEV'): @@ -45,6 +47,7 @@ def test_read_climo_field(metplus_config, config_overrides, expected_value): _read_climo_field(climo_type, config, app_name) assert config.getraw('config', expected_var) == expected_value + @pytest.mark.parametrize( 'config_overrides, expected_value', [ # 0 no relevant config set @@ -127,6 +130,7 @@ def test_read_climo_field(metplus_config, config_overrides, expected_value): 'hour_interval = 12;}')), ] ) +@pytest.mark.util def test_handle_climo_dict(metplus_config, config_overrides, expected_value): app_name = 'app' for climo_type in ('MEAN', 'STDEV'): @@ -145,27 +149,30 @@ def test_handle_climo_dict(metplus_config, config_overrides, expected_value): expected_sub = expected_value.replace('', climo_type.lower()) assert output_dict[expected_var] == expected_sub + @pytest.mark.parametrize( 'name, data_type, mp_configs, extra_args', [ ('beg', 'int', 'BEG', None), ('end', 'int', ['END'], None), ] ) +@pytest.mark.util def test_met_config_info(name, data_type, mp_configs, extra_args): item = METConfig(name=name, data_type=data_type) item.metplus_configs = mp_configs item.extra_args = extra_args - assert(item.name == name) - assert(item.data_type == data_type) + assert item.name == name + assert item.data_type == data_type if isinstance(mp_configs, list): - assert(item.metplus_configs == mp_configs) + assert item.metplus_configs == mp_configs else: - assert(item.metplus_configs == [mp_configs]) + assert item.metplus_configs == [mp_configs] if not extra_args: - assert(item.extra_args == {}) + assert item.extra_args == {} + @pytest.mark.parametrize( 'data_type, expected_function', [ @@ -178,11 +185,12 @@ def test_met_config_info(name, data_type, mp_configs, extra_args): ('bad_name', None), ] ) +@pytest.mark.util def test_set_met_config_function(data_type, expected_function): try: function_found = set_met_config_function(data_type) function_name = function_found.__name__ if function_found else None - assert(function_name == expected_function) + assert function_name == expected_function except ValueError: assert expected_function is None @@ -196,9 +204,11 @@ def test_set_met_config_function(data_type, expected_function): ('G002', '"G002"'), ] ) +@pytest.mark.util def test_format_regrid_to_grid(input, output): assert format_regrid_to_grid(input) == output + @pytest.mark.parametrize( 'config_overrides, expected_value', [ # 0 no climo variables set @@ -232,6 +242,7 @@ def test_format_regrid_to_grid(input, output): 'PYTHON_XARRAY'), ] ) +@pytest.mark.util def test_read_climo_file_name(metplus_config, config_overrides, expected_value): # name of app used for testing to read/set config variables diff --git a/internal_tests/pytests/util/met_util/test_met_util.py b/internal_tests/pytests/util/met_util/test_met_util.py index ccc7d6bcd9..8241ea4528 100644 --- a/internal_tests/pytests/util/met_util/test_met_util.py +++ b/internal_tests/pytests/util/met_util/test_met_util.py @@ -1,16 +1,17 @@ #!/usr/bin/env python3 -import sys +import pytest + import datetime import os from dateutil.relativedelta import relativedelta import pprint -import pytest from metplus.util import met_util as util from metplus.util import time_util from metplus.util.config_metplus import parse_var_list + @pytest.mark.parametrize( 'key, value', [ ({"gt2.3", "gt5.5"}, True), @@ -43,14 +44,15 @@ ([">SFP70", ">SFP80", ">SFP90", ">SFP95"], True), ] ) +@pytest.mark.util def test_threshold(key, value): - assert(util.validate_thresholds(key) == value) + assert util.validate_thresholds(key) == value + # parses a threshold and returns a list of tuples of # comparison and number, i.e.: # 'gt4' => [('gt', 4)] # gt4&<5 => [('gt', 4), ('lt', 5)] - @pytest.mark.parametrize( 'key, value', [ ('gt4', [('gt', 4)]), @@ -81,8 +83,10 @@ def test_threshold(key, value): (" Date: Thu, 7 Jul 2022 10:27:06 -0600 Subject: [PATCH 13/35] fixed logic to split string --- .github/actions/run_tests/entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index be090c3d6e..14eb38f460 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -55,9 +55,9 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then -f .github/actions/run_tests/Dockerfile.run \ . - marker=`echo $INPUT_CATEGORIES | awk -F_ '{print $2}'` + marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" if [[ "$marker" == not* ]]; then - marker=not `echo $marker | awk -F_ '{print $2}'` + marker=not "$( cut -d '_' -f 2- <<< "marker" )" fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" From 831037a26ac382c97494bfad7d9513b743e47412 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:39:26 -0600 Subject: [PATCH 14/35] marked rest of util tests with util marker --- .../util/metplus_check/test_metplus_check.py | 18 +- .../string_manip/test_util_string_manip.py | 10 +- .../test_string_template_substitution.py | 155 ++++++++++++------ .../util/time_looping/test_time_looping.py | 6 + .../pytests/util/time_util/test_time_util.py | 32 ++-- 5 files changed, 155 insertions(+), 66 deletions(-) diff --git a/internal_tests/pytests/util/metplus_check/test_metplus_check.py b/internal_tests/pytests/util/metplus_check/test_metplus_check.py index 568777f996..75377c0bbe 100644 --- a/internal_tests/pytests/util/metplus_check/test_metplus_check.py +++ b/internal_tests/pytests/util/metplus_check/test_metplus_check.py @@ -1,9 +1,10 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import pytest from metplus.util import metplus_check + # test that: # the same version of python passes # a later version of python passes @@ -19,8 +20,10 @@ ('4.0.0', '3.6.3', True), # later major ] ) +@pytest.mark.util def test_metplus_check_python(user, supported, torf): - assert(metplus_check.metplus_check_python_version(user, supported) == torf) + assert metplus_check.metplus_check_python_version(user, supported) == torf + # checking METPLUS_DISABLE_PLOT_WRAPPERS and METPLUS_ENABLE_PLOT_WRAPPERS # both cannot be set @@ -33,8 +36,10 @@ def test_metplus_check_python(user, supported, torf): 'METPLUS_DISABLE_PLOT_WRAPPERS': 'yes'}, False), #both set ] ) +@pytest.mark.util def test_metplus_check_environment_variables(env_vars, expected_result): - assert(metplus_check.metplus_check_environment_variables(env_vars) == expected_result) + assert metplus_check.metplus_check_environment_variables(env_vars) == expected_result + # checking METPLUS_DISABLE_PLOT_WRAPPERS and METPLUS_ENABLE_PLOT_WRAPPERS to determine # if plot wrappers are enabled. Default is True (run). Both cannot be set @@ -47,8 +52,10 @@ def test_metplus_check_environment_variables(env_vars, expected_result): 'METPLUS_DISABLE_PLOT_WRAPPERS': 'yes'}, None), #both set ] ) +@pytest.mark.util def test_plot_wrappers_are_enabled(env_vars, expected_result): - assert(metplus_check.plot_wrappers_are_enabled(env_vars) == expected_result) + assert metplus_check.plot_wrappers_are_enabled(env_vars) == expected_result + @pytest.mark.parametrize( 'value, expected_result', [ @@ -75,5 +82,6 @@ def test_plot_wrappers_are_enabled(env_vars, expected_result): ('f', False), ] ) +@pytest.mark.util def test_evaluates_to_true(value, expected_result): - assert(metplus_check.evaluates_to_true(value) == expected_result) + assert metplus_check.evaluates_to_true(value) == expected_result diff --git a/internal_tests/pytests/util/string_manip/test_util_string_manip.py b/internal_tests/pytests/util/string_manip/test_util_string_manip.py index 0442ecc72f..f218415837 100644 --- a/internal_tests/pytests/util/string_manip/test_util_string_manip.py +++ b/internal_tests/pytests/util/string_manip/test_util_string_manip.py @@ -7,6 +7,7 @@ from metplus.util.string_manip import * from metplus.util.string_manip import _fix_list + @pytest.mark.parametrize( 'before, after', [ ('string', 'string'), @@ -16,8 +17,10 @@ (None, ''), ] ) +@pytest.mark.util def test_remove_quotes(before, after): - assert(remove_quotes(before) == after) + assert remove_quotes(before) == after + @pytest.mark.parametrize( 'string_list, output_list', [ @@ -69,15 +72,19 @@ def test_remove_quotes(before, after): ["some_value", "some_other_value"]), ] ) +@pytest.mark.util def test_getlist(string_list, output_list): test_list = getlist(string_list) assert test_list == output_list + +@pytest.mark.util def test_getlist_int(): string_list = '6, 7, 42' test_list = getlistint(string_list) assert test_list == [6, 7, 42] + @pytest.mark.parametrize( 'list_string, output_list', [ ('begin_end_incr(3,12,3)', @@ -133,5 +140,6 @@ def test_getlist_int(): ] ) +@pytest.mark.util def test_getlist_begin_end_incr(list_string, output_list): assert getlist(list_string) == output_list diff --git a/internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py b/internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py index b19a6c45f4..dfffe82f22 100644 --- a/internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py +++ b/internal_tests/pytests/util/string_template_substitution/test_string_template_substitution.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import pytest -import logging + import datetime import os @@ -9,22 +9,27 @@ from metplus.util import get_tags,format_one_time_item, format_hms from metplus.util import add_to_dict, populate_match_dict, get_fmt_info + +@pytest.mark.util def test_cycle_hour(): cycle_string = 0 valid_string = datetime.datetime.strptime("20180103", '%Y%m%d') templ = "prefix.{valid?fmt=%Y%m%d}.tm{cycle?fmt=%2H}" expected_filename = "prefix.20180103.tm00" filename = do_string_sub(templ, valid=valid_string, cycle=cycle_string) - assert(filename == expected_filename) + assert filename == expected_filename +@pytest.mark.util def test_offset_hour(): expected_hour = "03" offset = 10800 templ = "{offset?fmt=%2H}" offset_hour = do_string_sub(templ, offset=offset) - assert (offset_hour == expected_hour) + assert offset_hour == expected_hour + +@pytest.mark.util def test_gdas_substitution(): # Test that the string template substitution works correctly for GDAS # prepbufr files, which do not make use of the cycle hour or the offset @@ -34,7 +39,8 @@ def test_gdas_substitution(): templ = "prepbufr.gdas.{valid?fmt=%Y%m%d%H}.nc" expected_filename = 'prepbufr.gdas.' + valid_string + '.nc' filename = do_string_sub(templ, valid=valid_obj) - assert(filename == expected_filename) + assert filename == expected_filename + @pytest.mark.parametrize( 'template, filepath, expected_valid', [ @@ -64,11 +70,14 @@ def test_gdas_substitution(): "201412150000"), ] ) +@pytest.mark.util def test_parse_template(template, filepath, expected_valid): out = parse_template(template, filepath) ftime = out['valid'].strftime('%Y%m%d%H%M') - assert (ftime == expected_valid) + assert ftime == expected_valid + +@pytest.mark.util def test_h_lead_no_pad_1_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%1H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -76,9 +85,10 @@ def test_h_lead_no_pad_1_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A3h") + assert out_string == "1987020103_A3h" +@pytest.mark.util def test_h_lead_no_pad_2_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%1H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -86,9 +96,10 @@ def test_h_lead_no_pad_2_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A12h") + assert out_string == "1987020103_A12h" +@pytest.mark.util def test_h_lead_no_pad_3_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%1H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -96,9 +107,10 @@ def test_h_lead_no_pad_3_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A102h") + assert out_string == "1987020103_A102h" +@pytest.mark.util def test_h_lead_pad_1_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%.1H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -106,9 +118,10 @@ def test_h_lead_pad_1_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A3h") + assert out_string == "1987020103_A3h" +@pytest.mark.util def test_h_lead_pad_2_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%.2H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -116,9 +129,10 @@ def test_h_lead_pad_2_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A03h") + assert out_string == "1987020103_A03h" +@pytest.mark.util def test_h_lead_pad_2_digit_sub(): file_template = "{init?fmt=%Y%m%d%H}_A{lead?fmt=%.3H}h" init_time = datetime.datetime.strptime("1987020103", '%Y%m%d%H') @@ -126,9 +140,10 @@ def test_h_lead_pad_2_digit_sub(): out_string = do_string_sub(file_template, init=init_time, lead=lead_time) - assert(out_string == "1987020103_A003h") + assert out_string == "1987020103_A003h" +@pytest.mark.util def test_ym_date_dir_init(): # Test that the ym directory can be read in and does substitution correctly # e.g. /d1/METplus_TC/adeck_orig/201708/atcfunix.gfs.2017080100 @@ -142,6 +157,7 @@ def test_ym_date_dir_init(): assert filename == expected_filename +@pytest.mark.util def test_ym_date_dir(): # Test that the ym directory can be read in and does substitution correctly # e.g. /d1/METplus_TC/adeck_orig/201708/atcfunix.gfs.2017080100 @@ -154,6 +170,7 @@ def test_ym_date_dir(): assert filename == expected_filename +@pytest.mark.util def test_ymd_date_dir(): # Test that the ymd directory can be read in and does substitution correctly # e.g. /d1/METplus_TC/adeck_orig/20170811/atcfunix.gfs.2017080100 @@ -167,6 +184,7 @@ def test_ymd_date_dir(): assert filename == expected_filename +@pytest.mark.util def test_ymd_region_cyclone(): # Test that we can recreate the full file path with a date, # region, and cyclone @@ -182,6 +200,8 @@ def test_ymd_region_cyclone(): expected_full_file = '/d1/METplus_TC/bdeck/201708/bal052017.dat' assert full_file == expected_full_file + +@pytest.mark.util def test_crow_variable_hour(): # Test that do_string_sub() correctly creates the valid hour # without any zero-padding when given the following as input: @@ -206,21 +226,25 @@ def test_crow_variable_hour(): crow_2_output == crow_input_file_2 and crow_3_output == crow_input_file_3) + +@pytest.mark.util def test_multiple_valid_substitution_valid(): valid_string = datetime.datetime.strptime("2018020112", '%Y%m%d%H') lead_string = int("123") * 3600 templ = "{valid?fmt=%Y%m%d%H}/gfs.t{valid?fmt=%H}.pgrb2.0p25.{lead?fmt=%HHH}" expected_filename = "2018020112/gfs.t12.pgrb2.0p25.123" filename = do_string_sub(templ, valid=valid_string, lead=lead_string) - assert(filename == expected_filename) + assert filename == expected_filename + +@pytest.mark.util def test_multiple_valid_substitution_init(): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') lead_string = 0 templ = "{init?fmt=%Y%m%d%H}/gfs.t{init?fmt=%H}z.pgrb2.0p25.f{lead?fmt=%.2H}" expected_filename = "2017060400/gfs.t00z.pgrb2.0p25.f00" filename = do_string_sub(templ, init=init_string, lead=lead_string) - assert(filename == expected_filename) + assert filename == expected_filename def test_multiple_valid_substitution_init_and_valid(): @@ -231,8 +255,10 @@ def test_multiple_valid_substitution_init_and_valid(): expected_filename = "2017060400/gfs.t00z.pgrb2.0p25.f00" filename = do_string_sub(templ, init=init_string, lead=lead_string, valid=valid_string) - assert(filename == expected_filename) + assert filename == expected_filename + +@pytest.mark.util def test_multiple_valid_substitution_init_and_valid_w_lead(): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') valid_string = datetime.datetime.strptime("2017060500", '%Y%m%d%H') @@ -241,39 +267,47 @@ def test_multiple_valid_substitution_init_and_valid_w_lead(): expected_filename = "2017060500/gfs.t00z.pgrb2.0p25.f24" filename = do_string_sub(templ, init=init_string, lead=lead_string, valid=valid_string) - assert(filename == expected_filename) + assert filename == expected_filename + +@pytest.mark.util def test_multiple_valid_substitution_init_complex(): init_string = datetime.datetime.strptime("2016061018", '%Y%m%d%H') lead_string = int("6") * 3600 templ = "ncar.ral.CoSPA.HRRR.{init?fmt=%Y-%m-%dT%H:%M:%S}.PT{lead?fmt=%.2H}:00.nc" expected_filename = "ncar.ral.CoSPA.HRRR.2016-06-10T18:00:00.PT06:00.nc" filename = do_string_sub(templ, init=init_string, lead=lead_string) - assert(filename == expected_filename) + assert filename == expected_filename + # NOTE: this test has a shift in init time, which may not be supported # parse_template will currently error out if it finds a shift that is not # on valid time +@pytest.mark.util def test_shift_time(): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') templ = "{init?fmt=%Y%m%d%H?shift=86400}" expected_filename = "2017060500" filename = do_string_sub(templ, init=init_string) - assert(filename == expected_filename) + assert filename == expected_filename + # NOTE: this test has a shift in init time, which may not be supported # parse_template will currently error out if it finds a shift that is not # on valid time +@pytest.mark.util def test_shift_time_negative(): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') templ = "{init?fmt=%Y%m%d%H?shift=-86400}" expected_filename = "2017060300" filename = do_string_sub(templ, init=init_string) - assert(filename == expected_filename) + assert filename == expected_filename + # NOTE: this test has a shift in lead time, which may not be supported # parse_template will currently error out if it finds a shift that is not # on valid time +@pytest.mark.util def test_shift_time_lead_negative(): init_string = datetime.datetime.strptime("2019020700", '%Y%m%d%H') lead_string = int("60") * 3600 @@ -287,51 +321,58 @@ def test_shift_time_extract(): templ = "{valid?fmt=%Y%m%d%H?shift=-21600}" filename = "2017060400" dt = parse_template(templ, filename)['valid'] - assert(dt.strftime('%Y%m%d%H') == valid_dt.strftime('%Y%m%d%H')) + assert dt.strftime('%Y%m%d%H') == valid_dt.strftime('%Y%m%d%H') + +@pytest.mark.util def test_ccpa_template(): - passed = True valid_string = datetime.datetime.strptime("2019022403", '%Y%m%d%H') lead_string = 10800 templ = "ccpa.{valid?fmt=%Y%m%d}/06/ccpa.t{valid?fmt=%H}z.{lead?fmt=%.2H}h.hrap.conus.gb2" expected_filename = "ccpa.20190224/06/ccpa.t03z.03h.hrap.conus.gb2" filename = do_string_sub(templ, valid=valid_string, lead=lead_string) - if filename != expected_filename: - passed = False + assert filename == expected_filename valid_string = datetime.datetime.strptime("2019022406", '%Y%m%d%H') lead_string = int("6") * 3600 expected_filename = "ccpa.20190224/06/ccpa.t06z.06h.hrap.conus.gb2" filename = do_string_sub(templ, valid=valid_string, lead=lead_string) - if filename == expected_filename: - passed = False + assert filename == expected_filename - return passed +@pytest.mark.util def test_filename_matches_template(): template = "{init?fmt=%Y%m%d%H}_dog_A{lead?fmt=%HH}h" filepath = "1987020103_dog_A03h" out = parse_template(template, filepath) ftime = out['valid'].strftime('%Y%m%d%H%M') - assert(ftime == "198702010600") + assert ftime == "198702010600" + +@pytest.mark.util def test_filename_does_not_match_template(): template = "{init?fmt=%Y%m%d%H}_dog_A{lead?fmt=%HH}h" filepath = "1987020103_cat_A03h" out = parse_template(template, filepath) - assert(out == None) + assert out == None + +@pytest.mark.util def test_filename_does_not_match_template_end(): template = "{init?fmt=%Y%m%d%H}_dog_A{lead?fmt=%HH}h" filepath = "1987020103_dog_A03d" out = parse_template(template, filepath) - assert(out == None) + assert out == None + +@pytest.mark.util def test_get_tags(): template = '*{basin?fmt=%s}_some_stuff_{cyclone?fmt=%02d}_{date?fmt=%Y%m}' tags = get_tags(template) - assert( tags[0] == '*' and tags[1] == 'basin' and tags[2] == 'cyclone' and tags[3] == 'date') + assert( tags[0] == '*' and tags[1] == 'basin' and tags[2] == 'cyclone' and + tags[3] == 'date') + # format should be something like H 2H 3H 2M etc # key is the time value integer to convert, like 1 or 60 @@ -376,9 +417,10 @@ def test_get_tags(): ('.3d', 1, '001', 'd'), ] ) - +@pytest.mark.util def test_format_one_time_item(format, key ,value, ttype): - assert(format_one_time_item(format, key, ttype) == value) + assert format_one_time_item(format, key, ttype) == value + # format is the time format to use, like, %M or %H%M # seconds is the integer number of seconds of the offset to use, i.e. 3601 @@ -405,16 +447,20 @@ def test_format_one_time_item(format, key ,value, ttype): ('%d', 86401, '01'), ] ) +@pytest.mark.util def test_format_hms(format, seconds, value): # format should be something like %M or %H%M - assert(format_hms(format, seconds == value)) + assert format_hms(format, seconds == value) + +@pytest.mark.util def test_underscore_in_time_fmt(): valid_string = datetime.datetime.strptime("20170604010203", '%Y%m%d%H%M%S') templ = "{valid?fmt=%Y%m%d_%H%M%S}" expected_filename = "20170604_010203" filename = do_string_sub(templ, valid=valid_string) - assert(filename == expected_filename) + assert filename == expected_filename + @pytest.mark.parametrize( 'match, match_dict, full_str, new_len, expected_result', [ @@ -429,8 +475,10 @@ def test_underscore_in_time_fmt(): ('valid+H', {'init+Y': '2019', 'valid+H': '2'}, '02.01.12.and.some.more.stuff', 2, True), ] ) +@pytest.mark.util def test_add_to_dict(match, match_dict, full_str, new_len, expected_result): - assert(add_to_dict(match, match_dict, full_str, new_len) == expected_result) + assert add_to_dict(match, match_dict, full_str, new_len) == expected_result + @pytest.mark.parametrize( 'template, filepath, expected_match_dict, expected_valid_shift', [ @@ -480,16 +528,17 @@ def test_add_to_dict(match, match_dict, full_str, new_len, expected_result): None), ] ) +@pytest.mark.util def test_populate_match_dict(template, filepath, expected_match_dict, expected_valid_shift): try: match_dict, valid_shift = populate_match_dict(template, filepath) # if expecting match_dict to be None, assert that actual is also None if expected_match_dict is None: - assert(match_dict is None) + assert match_dict is None elif match_dict is None: # if expected is not None, fail if actual is None - assert(False) + assert False return num_keys = len(match_dict.keys()) @@ -498,25 +547,26 @@ def test_populate_match_dict(template, filepath, expected_match_dict, expected_v print(f"Number of match_dict keys do not match. Actual: {num_keys}, Expected: {expected_num_keys}") print(f"Found: {match_dict}") print(f"Expected: {expected_match_dict}") - assert(False) + assert False for key, value in match_dict.items(): if key not in expected_match_dict: print(f"Key {key} not found in expected match_dict: {expected_match_dict}") - assert(False) + assert False if value != expected_match_dict[key]: print(f"Match dict value from {key} does not match expected value") - assert(False) + assert False if valid_shift != expected_valid_shift: print(f"Incorrect valid shift. Actual {valid_shift}, Expected: {expected_valid_shift}") - assert(False) + assert False - assert(True) + assert True except TypeError: - assert(expected_match_dict is None and expected_valid_shift is None) + assert expected_match_dict is None and expected_valid_shift is None + @pytest.mark.parametrize( 'fmt, filepath, identifier, expected_fmt_len, expected_match_dict', [ @@ -528,19 +578,21 @@ def test_populate_match_dict(template, filepath, expected_match_dict, expected_v {'valid+Y': '2020', 'valid+m': '02', 'valid+d': '01',},), ] ) +@pytest.mark.util def test_get_fmt_info(fmt, filepath, identifier, expected_fmt_len, expected_match_dict): match_dict = {} fmt_len = get_fmt_info(fmt, filepath, match_dict, identifier) if fmt_len != expected_fmt_len: print(f"FMT length: {fmt_len}, match_dict: {match_dict}") - assert(False) + assert False if match_dict != expected_match_dict: print(f"Match Dictionary: {match_dict}") print(f"Expected Dictionary: {expected_match_dict}") - assert(False) + assert False + + assert True - assert(True) @pytest.mark.parametrize( 'templ, expected_filename', [ @@ -549,6 +601,7 @@ def test_get_fmt_info(fmt, filepath, identifier, expected_fmt_len, expected_matc "2017060400_{missing_tag?fmt=%H}_f06"), ] ) +@pytest.mark.util def test_do_string_sub_skip_missing_tags(templ, expected_filename): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') lead_string = int("6") * 3600 @@ -557,7 +610,8 @@ def test_do_string_sub_skip_missing_tags(templ, expected_filename): lead=lead_string, skip_missing_tags=True, recurse=True) - assert(filename == expected_filename) + assert filename == expected_filename + @pytest.mark.parametrize( 'templ, expected_filename', [ @@ -570,6 +624,7 @@ def test_do_string_sub_skip_missing_tags(templ, expected_filename): "climo_stdev = {field = [{name= \"filename.py other.nc:0406\";}]};") ] ) +@pytest.mark.util def test_do_string_sub_recurse(templ, expected_filename): init_string = datetime.datetime.strptime("2017060400", '%Y%m%d%H') lead_string = int("6") * 3600 @@ -578,7 +633,8 @@ def test_do_string_sub_recurse(templ, expected_filename): lead=lead_string, skip_missing_tags=True, recurse=True) - assert(filename == expected_filename) + assert filename == expected_filename + @pytest.mark.parametrize( 'templ, expected_filename', [ @@ -587,6 +643,7 @@ def test_do_string_sub_recurse(templ, expected_filename): "201706/b([a-zA-Z]{2})q201706*.gfso.([0-9]{2,4})"), ] ) +@pytest.mark.util def test_do_string_sub_no_recurse_no_missing(templ, expected_filename): date_dt = datetime.datetime.strptime("2017060400", '%Y%m%d%H') basin_regex = "([a-zA-Z]{2})" @@ -595,7 +652,8 @@ def test_do_string_sub_no_recurse_no_missing(templ, expected_filename): date=date_dt, basin=basin_regex, cyclone=cyclone_regex) - assert(filename == expected_filename) + assert filename == expected_filename + @pytest.mark.parametrize( 'filepath, template, expected_result', [ @@ -606,6 +664,7 @@ def test_do_string_sub_no_recurse_no_missing(templ, expected_filename): ('filename.2019020104.ext.gz', 'file.{valid?fmt=%Y%m%d%H}.ext', None), ] ) +@pytest.mark.util def test_get_time_from_file(filepath, template, expected_result): result = get_time_from_file(filepath, template) diff --git a/internal_tests/pytests/util/time_looping/test_time_looping.py b/internal_tests/pytests/util/time_looping/test_time_looping.py index 7326734b48..e1286fa270 100644 --- a/internal_tests/pytests/util/time_looping/test_time_looping.py +++ b/internal_tests/pytests/util/time_looping/test_time_looping.py @@ -2,6 +2,8 @@ from metplus.util.time_looping import * + +@pytest.mark.util def test_time_generator_list(metplus_config): for prefix in ['INIT', 'VALID']: config = metplus_config() @@ -23,6 +25,8 @@ def test_time_generator_list(metplus_config): except StopIteration: assert True + +@pytest.mark.util def test_time_generator_increment(metplus_config): for prefix in ['INIT', 'VALID']: config = metplus_config() @@ -48,6 +52,8 @@ def test_time_generator_increment(metplus_config): except StopIteration: assert True + +@pytest.mark.util def test_time_generator_error_check(metplus_config): """! Test that None is returned by the time generator when the time looping config variables are not set properly. Tests: diff --git a/internal_tests/pytests/util/time_util/test_time_util.py b/internal_tests/pytests/util/time_util/test_time_util.py index bec515faff..86da6b3140 100644 --- a/internal_tests/pytests/util/time_util/test_time_util.py +++ b/internal_tests/pytests/util/time_util/test_time_util.py @@ -7,6 +7,7 @@ from metplus.util import time_util + @pytest.mark.parametrize( 'rd, seconds, time_string, time_letter_only, hours', [ (relativedelta(seconds=1), 1, '1 second', '1S', 0), @@ -41,11 +42,13 @@ (relativedelta(seconds=-3721), -3721, '-1 hour 2 minutes 1 second', '-1H2M1S', -1), ] ) +@pytest.mark.util def test_ti_get_seconds_and_string(rd, seconds, time_string, time_letter_only, hours): - assert(time_util.ti_get_seconds_from_relativedelta(rd) == seconds) - assert(time_util.ti_get_lead_string(rd) == time_string) - assert(time_util.ti_get_lead_string(rd, letter_only=True) == time_letter_only) - assert(time_util.ti_get_hours_from_relativedelta(rd) == hours) + assert time_util.ti_get_seconds_from_relativedelta(rd) == seconds + assert time_util.ti_get_lead_string(rd) == time_string + assert time_util.ti_get_lead_string(rd, letter_only=True) == time_letter_only + assert time_util.ti_get_hours_from_relativedelta(rd) == hours + @pytest.mark.parametrize( 'key, value', [ @@ -66,10 +69,12 @@ def test_ti_get_seconds_and_string(rd, seconds, time_string, time_letter_only, h ('393d', datetime(2020, 2, 29, 0) ), # leap year ] ) +@pytest.mark.util def test_get_relativedelta(key, value): # start time is 2019-02-01_0Z start_time = datetime(2019, 2, 1, 0) - assert(start_time + time_util.get_relativedelta(key) == value) + assert start_time + time_util.get_relativedelta(key) == value + @pytest.mark.parametrize( 'time_string, default_unit, met_time', [ @@ -87,25 +92,28 @@ def test_get_relativedelta(key, value): ('3723S', None, '010203'), ] ) +@pytest.mark.util def test_time_string_to_met_time(time_string, default_unit, met_time): - assert(time_util.time_string_to_met_time(time_string, default_unit) == met_time) + assert time_util.time_string_to_met_time(time_string, default_unit) == met_time + @pytest.mark.parametrize( 'input_dict, expected_time_info', [ ({'init': datetime(2014, 10, 31, 12), 'lead': relativedelta(hours=3)}, {'init': datetime(2014, 10, 31, 12), - 'lead': 10800, - 'valid': datetime(2014, 10, 31, 15)} - ), + 'lead': 10800, + 'valid': datetime(2014, 10, 31, 15)} + ), ] ) +@pytest.mark.util def test_ti_calculate(input_dict, expected_time_info): time_info = time_util.ti_calculate(input_dict) for key, value in expected_time_info.items(): - assert(time_info[key] == value) + assert time_info[key] == value time_info2 = time_util.ti_calculate(time_info) for key, value in expected_time_info.items(): - assert(time_info[key] == value) - assert(time_info2[key] == value) + assert time_info[key] == value + assert time_info2[key] == value From 71eb108b9d3a7e608f07766aafb217065b6bdc91 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:39:44 -0600 Subject: [PATCH 15/35] fixed another typo in string splitting logic --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 14eb38f460..6f81eb2a85 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -57,7 +57,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" if [[ "$marker" == not* ]]; then - marker=not "$( cut -d '_' -f 2- <<< "marker" )" + marker=not "$( cut -d '_' -f 2- <<< "$marker" )" fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" From 166a0a2dcd7269daea7b634b38e6e9e49411f325 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 10:48:06 -0600 Subject: [PATCH 16/35] tested change that should properly split strings --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 6f81eb2a85..19a0b16f97 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -57,7 +57,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" if [[ "$marker" == not* ]]; then - marker=not "$( cut -d '_' -f 2- <<< "$marker" )" + marker="not $( cut -d '_' -f 2- <<< "$marker" )" fi echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" From b01dec1b6cf6dfeff8d108e24908e850a8663ff8 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:05:13 -0600 Subject: [PATCH 17/35] moved wrapper tests into wrapper directory --- .../pytests/{ => wrappers}/ascii2nc/test_ascii2nc_wrapper.py | 0 .../{ => wrappers}/command_builder/test_command_builder.py | 0 .../{ => wrappers}/compare_gridded/test_compare_gridded.py | 0 .../{ => wrappers}/ensemble_stat/test_ensemble_stat_wrapper.py | 0 .../pytests/{ => wrappers}/extract_tiles/extract_tiles_test.conf | 0 .../pytests/{ => wrappers}/extract_tiles/test_extract_tiles.py | 0 .../{ => wrappers}/gen_ens_prod/test_gen_ens_prod_wrapper.py | 0 .../pytests/{ => wrappers}/gen_vx_mask/test_gen_vx_mask.py | 0 internal_tests/pytests/{ => wrappers}/grid_diag/test_grid_diag.py | 0 .../pytests/{ => wrappers}/grid_stat/test_grid_stat_wrapper.py | 0 .../pytests/{ => wrappers}/ioda2nc/test_ioda2nc_wrapper.py | 0 .../pytests/{ => wrappers}/met_db_load/test_met_db_load.py | 0 internal_tests/pytests/{ => wrappers}/mode/test_mode_wrapper.py | 0 internal_tests/pytests/{ => wrappers}/mtd/test_mtd_wrapper.py | 0 internal_tests/pytests/{ => wrappers}/pb2nc/__init__.py | 0 internal_tests/pytests/{ => wrappers}/pb2nc/conf1 | 0 internal_tests/pytests/{ => wrappers}/pb2nc/test_pb2nc_wrapper.py | 0 internal_tests/pytests/{ => wrappers}/pcp_combine/test1.conf | 0 .../pytests/{ => wrappers}/pcp_combine/test_deprecated.conf | 0 .../{ => wrappers}/pcp_combine/test_pcp_combine_wrapper.py | 0 .../pytests/{ => wrappers}/point2grid/test_point2grid.py | 0 internal_tests/pytests/{ => wrappers}/point_stat/__init__.py | 0 .../{ => wrappers}/point_stat/point_stat_test_conus_sfc.conf | 0 .../{ => wrappers}/point_stat/point_stat_test_upper_air.conf | 0 .../point_stat/refactor_point_stat_test_conus_sfc.conf | 0 .../pytests/{ => wrappers}/point_stat/test_point_stat_wrapper.py | 0 .../{ => wrappers}/regrid_data_plane/test_regrid_data_plane.py | 0 .../pytests/{ => wrappers}/runtime_freq/test_runtime_freq.py | 0 .../pytests/{ => wrappers}/series_analysis/series_test.conf | 0 .../{ => wrappers}/series_analysis/test_series_analysis.py | 0 internal_tests/pytests/{ => wrappers}/stat_analysis/test.conf | 0 .../pytests/{ => wrappers}/stat_analysis/test_plotting.conf | 0 .../pytests/{ => wrappers}/stat_analysis/test_stat_analysis.py | 0 .../{ => wrappers}/stat_analysis/test_stat_analysis_plotting.py | 0 .../pytests/{ => wrappers}/tc_gen/test_tc_gen_wrapper.py | 0 .../pytests/{ => wrappers}/tc_pairs/tc_pairs_wrapper_test.conf | 0 .../pytests/{ => wrappers}/tc_pairs/test_tc_pairs_wrapper.py | 0 internal_tests/pytests/{ => wrappers}/tc_stat/tc_stat_conf.conf | 0 .../pytests/{ => wrappers}/tc_stat/test_tc_stat_wrapper.py | 0 .../pytests/{ => wrappers}/user_script/test_user_script.py | 0 40 files changed, 0 insertions(+), 0 deletions(-) rename internal_tests/pytests/{ => wrappers}/ascii2nc/test_ascii2nc_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/command_builder/test_command_builder.py (100%) rename internal_tests/pytests/{ => wrappers}/compare_gridded/test_compare_gridded.py (100%) rename internal_tests/pytests/{ => wrappers}/ensemble_stat/test_ensemble_stat_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/extract_tiles/extract_tiles_test.conf (100%) rename internal_tests/pytests/{ => wrappers}/extract_tiles/test_extract_tiles.py (100%) rename internal_tests/pytests/{ => wrappers}/gen_ens_prod/test_gen_ens_prod_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/gen_vx_mask/test_gen_vx_mask.py (100%) rename internal_tests/pytests/{ => wrappers}/grid_diag/test_grid_diag.py (100%) rename internal_tests/pytests/{ => wrappers}/grid_stat/test_grid_stat_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/ioda2nc/test_ioda2nc_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/met_db_load/test_met_db_load.py (100%) rename internal_tests/pytests/{ => wrappers}/mode/test_mode_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/mtd/test_mtd_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/pb2nc/__init__.py (100%) rename internal_tests/pytests/{ => wrappers}/pb2nc/conf1 (100%) rename internal_tests/pytests/{ => wrappers}/pb2nc/test_pb2nc_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/pcp_combine/test1.conf (100%) rename internal_tests/pytests/{ => wrappers}/pcp_combine/test_deprecated.conf (100%) rename internal_tests/pytests/{ => wrappers}/pcp_combine/test_pcp_combine_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/point2grid/test_point2grid.py (100%) rename internal_tests/pytests/{ => wrappers}/point_stat/__init__.py (100%) rename internal_tests/pytests/{ => wrappers}/point_stat/point_stat_test_conus_sfc.conf (100%) rename internal_tests/pytests/{ => wrappers}/point_stat/point_stat_test_upper_air.conf (100%) rename internal_tests/pytests/{ => wrappers}/point_stat/refactor_point_stat_test_conus_sfc.conf (100%) rename internal_tests/pytests/{ => wrappers}/point_stat/test_point_stat_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/regrid_data_plane/test_regrid_data_plane.py (100%) rename internal_tests/pytests/{ => wrappers}/runtime_freq/test_runtime_freq.py (100%) rename internal_tests/pytests/{ => wrappers}/series_analysis/series_test.conf (100%) rename internal_tests/pytests/{ => wrappers}/series_analysis/test_series_analysis.py (100%) rename internal_tests/pytests/{ => wrappers}/stat_analysis/test.conf (100%) rename internal_tests/pytests/{ => wrappers}/stat_analysis/test_plotting.conf (100%) rename internal_tests/pytests/{ => wrappers}/stat_analysis/test_stat_analysis.py (100%) rename internal_tests/pytests/{ => wrappers}/stat_analysis/test_stat_analysis_plotting.py (100%) rename internal_tests/pytests/{ => wrappers}/tc_gen/test_tc_gen_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/tc_pairs/tc_pairs_wrapper_test.conf (100%) rename internal_tests/pytests/{ => wrappers}/tc_pairs/test_tc_pairs_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/tc_stat/tc_stat_conf.conf (100%) rename internal_tests/pytests/{ => wrappers}/tc_stat/test_tc_stat_wrapper.py (100%) rename internal_tests/pytests/{ => wrappers}/user_script/test_user_script.py (100%) diff --git a/internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py b/internal_tests/pytests/wrappers/ascii2nc/test_ascii2nc_wrapper.py similarity index 100% rename from internal_tests/pytests/ascii2nc/test_ascii2nc_wrapper.py rename to internal_tests/pytests/wrappers/ascii2nc/test_ascii2nc_wrapper.py diff --git a/internal_tests/pytests/command_builder/test_command_builder.py b/internal_tests/pytests/wrappers/command_builder/test_command_builder.py similarity index 100% rename from internal_tests/pytests/command_builder/test_command_builder.py rename to internal_tests/pytests/wrappers/command_builder/test_command_builder.py diff --git a/internal_tests/pytests/compare_gridded/test_compare_gridded.py b/internal_tests/pytests/wrappers/compare_gridded/test_compare_gridded.py similarity index 100% rename from internal_tests/pytests/compare_gridded/test_compare_gridded.py rename to internal_tests/pytests/wrappers/compare_gridded/test_compare_gridded.py diff --git a/internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py similarity index 100% rename from internal_tests/pytests/ensemble_stat/test_ensemble_stat_wrapper.py rename to internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py diff --git a/internal_tests/pytests/extract_tiles/extract_tiles_test.conf b/internal_tests/pytests/wrappers/extract_tiles/extract_tiles_test.conf similarity index 100% rename from internal_tests/pytests/extract_tiles/extract_tiles_test.conf rename to internal_tests/pytests/wrappers/extract_tiles/extract_tiles_test.conf diff --git a/internal_tests/pytests/extract_tiles/test_extract_tiles.py b/internal_tests/pytests/wrappers/extract_tiles/test_extract_tiles.py similarity index 100% rename from internal_tests/pytests/extract_tiles/test_extract_tiles.py rename to internal_tests/pytests/wrappers/extract_tiles/test_extract_tiles.py diff --git a/internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py b/internal_tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py similarity index 100% rename from internal_tests/pytests/gen_ens_prod/test_gen_ens_prod_wrapper.py rename to internal_tests/pytests/wrappers/gen_ens_prod/test_gen_ens_prod_wrapper.py diff --git a/internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py b/internal_tests/pytests/wrappers/gen_vx_mask/test_gen_vx_mask.py similarity index 100% rename from internal_tests/pytests/gen_vx_mask/test_gen_vx_mask.py rename to internal_tests/pytests/wrappers/gen_vx_mask/test_gen_vx_mask.py diff --git a/internal_tests/pytests/grid_diag/test_grid_diag.py b/internal_tests/pytests/wrappers/grid_diag/test_grid_diag.py similarity index 100% rename from internal_tests/pytests/grid_diag/test_grid_diag.py rename to internal_tests/pytests/wrappers/grid_diag/test_grid_diag.py diff --git a/internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py b/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py similarity index 100% rename from internal_tests/pytests/grid_stat/test_grid_stat_wrapper.py rename to internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py diff --git a/internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py b/internal_tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py similarity index 100% rename from internal_tests/pytests/ioda2nc/test_ioda2nc_wrapper.py rename to internal_tests/pytests/wrappers/ioda2nc/test_ioda2nc_wrapper.py diff --git a/internal_tests/pytests/met_db_load/test_met_db_load.py b/internal_tests/pytests/wrappers/met_db_load/test_met_db_load.py similarity index 100% rename from internal_tests/pytests/met_db_load/test_met_db_load.py rename to internal_tests/pytests/wrappers/met_db_load/test_met_db_load.py diff --git a/internal_tests/pytests/mode/test_mode_wrapper.py b/internal_tests/pytests/wrappers/mode/test_mode_wrapper.py similarity index 100% rename from internal_tests/pytests/mode/test_mode_wrapper.py rename to internal_tests/pytests/wrappers/mode/test_mode_wrapper.py diff --git a/internal_tests/pytests/mtd/test_mtd_wrapper.py b/internal_tests/pytests/wrappers/mtd/test_mtd_wrapper.py similarity index 100% rename from internal_tests/pytests/mtd/test_mtd_wrapper.py rename to internal_tests/pytests/wrappers/mtd/test_mtd_wrapper.py diff --git a/internal_tests/pytests/pb2nc/__init__.py b/internal_tests/pytests/wrappers/pb2nc/__init__.py similarity index 100% rename from internal_tests/pytests/pb2nc/__init__.py rename to internal_tests/pytests/wrappers/pb2nc/__init__.py diff --git a/internal_tests/pytests/pb2nc/conf1 b/internal_tests/pytests/wrappers/pb2nc/conf1 similarity index 100% rename from internal_tests/pytests/pb2nc/conf1 rename to internal_tests/pytests/wrappers/pb2nc/conf1 diff --git a/internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py b/internal_tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py similarity index 100% rename from internal_tests/pytests/pb2nc/test_pb2nc_wrapper.py rename to internal_tests/pytests/wrappers/pb2nc/test_pb2nc_wrapper.py diff --git a/internal_tests/pytests/pcp_combine/test1.conf b/internal_tests/pytests/wrappers/pcp_combine/test1.conf similarity index 100% rename from internal_tests/pytests/pcp_combine/test1.conf rename to internal_tests/pytests/wrappers/pcp_combine/test1.conf diff --git a/internal_tests/pytests/pcp_combine/test_deprecated.conf b/internal_tests/pytests/wrappers/pcp_combine/test_deprecated.conf similarity index 100% rename from internal_tests/pytests/pcp_combine/test_deprecated.conf rename to internal_tests/pytests/wrappers/pcp_combine/test_deprecated.conf diff --git a/internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py b/internal_tests/pytests/wrappers/pcp_combine/test_pcp_combine_wrapper.py similarity index 100% rename from internal_tests/pytests/pcp_combine/test_pcp_combine_wrapper.py rename to internal_tests/pytests/wrappers/pcp_combine/test_pcp_combine_wrapper.py diff --git a/internal_tests/pytests/point2grid/test_point2grid.py b/internal_tests/pytests/wrappers/point2grid/test_point2grid.py similarity index 100% rename from internal_tests/pytests/point2grid/test_point2grid.py rename to internal_tests/pytests/wrappers/point2grid/test_point2grid.py diff --git a/internal_tests/pytests/point_stat/__init__.py b/internal_tests/pytests/wrappers/point_stat/__init__.py similarity index 100% rename from internal_tests/pytests/point_stat/__init__.py rename to internal_tests/pytests/wrappers/point_stat/__init__.py diff --git a/internal_tests/pytests/point_stat/point_stat_test_conus_sfc.conf b/internal_tests/pytests/wrappers/point_stat/point_stat_test_conus_sfc.conf similarity index 100% rename from internal_tests/pytests/point_stat/point_stat_test_conus_sfc.conf rename to internal_tests/pytests/wrappers/point_stat/point_stat_test_conus_sfc.conf diff --git a/internal_tests/pytests/point_stat/point_stat_test_upper_air.conf b/internal_tests/pytests/wrappers/point_stat/point_stat_test_upper_air.conf similarity index 100% rename from internal_tests/pytests/point_stat/point_stat_test_upper_air.conf rename to internal_tests/pytests/wrappers/point_stat/point_stat_test_upper_air.conf diff --git a/internal_tests/pytests/point_stat/refactor_point_stat_test_conus_sfc.conf b/internal_tests/pytests/wrappers/point_stat/refactor_point_stat_test_conus_sfc.conf similarity index 100% rename from internal_tests/pytests/point_stat/refactor_point_stat_test_conus_sfc.conf rename to internal_tests/pytests/wrappers/point_stat/refactor_point_stat_test_conus_sfc.conf diff --git a/internal_tests/pytests/point_stat/test_point_stat_wrapper.py b/internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py similarity index 100% rename from internal_tests/pytests/point_stat/test_point_stat_wrapper.py rename to internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py diff --git a/internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py b/internal_tests/pytests/wrappers/regrid_data_plane/test_regrid_data_plane.py similarity index 100% rename from internal_tests/pytests/regrid_data_plane/test_regrid_data_plane.py rename to internal_tests/pytests/wrappers/regrid_data_plane/test_regrid_data_plane.py diff --git a/internal_tests/pytests/runtime_freq/test_runtime_freq.py b/internal_tests/pytests/wrappers/runtime_freq/test_runtime_freq.py similarity index 100% rename from internal_tests/pytests/runtime_freq/test_runtime_freq.py rename to internal_tests/pytests/wrappers/runtime_freq/test_runtime_freq.py diff --git a/internal_tests/pytests/series_analysis/series_test.conf b/internal_tests/pytests/wrappers/series_analysis/series_test.conf similarity index 100% rename from internal_tests/pytests/series_analysis/series_test.conf rename to internal_tests/pytests/wrappers/series_analysis/series_test.conf diff --git a/internal_tests/pytests/series_analysis/test_series_analysis.py b/internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py similarity index 100% rename from internal_tests/pytests/series_analysis/test_series_analysis.py rename to internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py diff --git a/internal_tests/pytests/stat_analysis/test.conf b/internal_tests/pytests/wrappers/stat_analysis/test.conf similarity index 100% rename from internal_tests/pytests/stat_analysis/test.conf rename to internal_tests/pytests/wrappers/stat_analysis/test.conf diff --git a/internal_tests/pytests/stat_analysis/test_plotting.conf b/internal_tests/pytests/wrappers/stat_analysis/test_plotting.conf similarity index 100% rename from internal_tests/pytests/stat_analysis/test_plotting.conf rename to internal_tests/pytests/wrappers/stat_analysis/test_plotting.conf diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis.py b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py similarity index 100% rename from internal_tests/pytests/stat_analysis/test_stat_analysis.py rename to internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py diff --git a/internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py similarity index 100% rename from internal_tests/pytests/stat_analysis/test_stat_analysis_plotting.py rename to internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py diff --git a/internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py b/internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py similarity index 100% rename from internal_tests/pytests/tc_gen/test_tc_gen_wrapper.py rename to internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py diff --git a/internal_tests/pytests/tc_pairs/tc_pairs_wrapper_test.conf b/internal_tests/pytests/wrappers/tc_pairs/tc_pairs_wrapper_test.conf similarity index 100% rename from internal_tests/pytests/tc_pairs/tc_pairs_wrapper_test.conf rename to internal_tests/pytests/wrappers/tc_pairs/tc_pairs_wrapper_test.conf diff --git a/internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py b/internal_tests/pytests/wrappers/tc_pairs/test_tc_pairs_wrapper.py similarity index 100% rename from internal_tests/pytests/tc_pairs/test_tc_pairs_wrapper.py rename to internal_tests/pytests/wrappers/tc_pairs/test_tc_pairs_wrapper.py diff --git a/internal_tests/pytests/tc_stat/tc_stat_conf.conf b/internal_tests/pytests/wrappers/tc_stat/tc_stat_conf.conf similarity index 100% rename from internal_tests/pytests/tc_stat/tc_stat_conf.conf rename to internal_tests/pytests/wrappers/tc_stat/tc_stat_conf.conf diff --git a/internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py b/internal_tests/pytests/wrappers/tc_stat/test_tc_stat_wrapper.py similarity index 100% rename from internal_tests/pytests/tc_stat/test_tc_stat_wrapper.py rename to internal_tests/pytests/wrappers/tc_stat/test_tc_stat_wrapper.py diff --git a/internal_tests/pytests/user_script/test_user_script.py b/internal_tests/pytests/wrappers/user_script/test_user_script.py similarity index 100% rename from internal_tests/pytests/user_script/test_user_script.py rename to internal_tests/pytests/wrappers/user_script/test_user_script.py From 0df52e62d900d4db39a7de6db8f6869b1c8409fa Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:05:38 -0600 Subject: [PATCH 18/35] changed marker for plotting tests --- .../make_plots/test_make_plots_wrapper.py | 4 ++-- .../pytests/plotting/plot_util/test_plot_util.py | 16 ++++++++-------- .../pytests/tcmpr_plotter/test_tcmpr_plotter.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py b/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py index 40d9bb8396..2be97153a6 100644 --- a/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py +++ b/internal_tests/pytests/plotting/make_plots/test_make_plots_wrapper.py @@ -23,7 +23,7 @@ def make_plots_wrapper(metplus_config): return MakePlotsWrapper(config) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_command(metplus_config): # Independently test that the make_plots python # command is being put together correctly with @@ -39,7 +39,7 @@ def test_get_command(metplus_config): assert(expected_command == test_command) -@pytest.mark.wrapper +@pytest.mark.plotting def test_create_c_dict(metplus_config): # Independently test that c_dict is being created # and that the wrapper and config reader diff --git a/internal_tests/pytests/plotting/plot_util/test_plot_util.py b/internal_tests/pytests/plotting/plot_util/test_plot_util.py index a4d6ea3ad8..d22ce6b7aa 100644 --- a/internal_tests/pytests/plotting/plot_util/test_plot_util.py +++ b/internal_tests/pytests/plotting/plot_util/test_plot_util.py @@ -17,7 +17,7 @@ logger = logging.getLogger('~/metplus_pytest_plot_util.log') -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_date_arrays(): # Independently test the creation of # the date arrays, one used for plotting @@ -185,7 +185,7 @@ def test_get_date_arrays(): expected_expected_stat_file_dates[l]) -@pytest.mark.wrapper +@pytest.mark.plotting def test_format_thresh(): # Independently test the formatting # of thresholds @@ -275,7 +275,7 @@ def test_format_thresh(): assert(test_thresh_letter == expected_thresh_letter) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_stat_file_base_columns(): # Independently test getting list # of the base MET version .stat file columns @@ -312,7 +312,7 @@ def test_get_stat_file_base_columns(): assert(test_stat_file_base_columns == expected_stat_file_base_columns) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_stat_file_line_type_columns(): # Independently test getting list # of the line type MET version .stat file columns @@ -423,7 +423,7 @@ def test_get_stat_file_line_type_columns(): expected_stat_file_line_type_columns) -@pytest.mark.wrapper +@pytest.mark.plotting def get_clevels(): # Independently test creating an array # of levels centered about 0 to plot @@ -437,7 +437,7 @@ def get_clevels(): assert(test_clevels == expected_clevels) -@pytest.mark.wrapper +@pytest.mark.plotting def test_calculate_average(): # Independently test getting the average # of a data array based on method @@ -679,7 +679,7 @@ def test_calculate_ci(): assert(test_intvl == expected_intvl) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_stat_plot_name(): # Independently test getting the # a more formalized statistic name @@ -720,7 +720,7 @@ def test_get_stat_plot_name(): assert(test_stat_plot_name == expected_stat_plot_name) -@pytest.mark.wrapper +@pytest.mark.plotting def test_calculate_stat(): # Independently test calculating # statistic values diff --git a/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py b/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py index a6f2e416c4..519bcbb943 100644 --- a/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py +++ b/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py @@ -97,7 +97,7 @@ def set_minimum_config_settings(config): 'plot': [('pitem1', 'P Label 1'), ('pitem2', 'P Label 2')]}), ] ) -@pytest.mark.wrapper +@pytest.mark.plotting def test_read_loop_info(metplus_config, config_overrides, expected_loop_args): config = metplus_config() @@ -178,7 +178,7 @@ def test_read_loop_info(metplus_config, config_overrides, expected_loop_args): '-dep ditem1 -plot pitem1')]), ] ) -@pytest.mark.wrapper +@pytest.mark.plotting def test_tcmpr_plotter_loop(metplus_config, config_overrides, expected_strings): config = metplus_config() @@ -271,7 +271,7 @@ def test_tcmpr_plotter_loop(metplus_config, config_overrides, ({'TCMPR_PLOTTER_PLOT_TYPES': 'item1'}, '-plot item1'), ] ) -@pytest.mark.wrapper +@pytest.mark.plotting def test_tcmpr_plotter(metplus_config, config_overrides, expected_string): # add a space before value if expected string has a value if expected_string: From 552d2ece25d16e3dac59afc257a019d79be6304c Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:06:00 -0600 Subject: [PATCH 19/35] added plotting marker --- internal_tests/pytests/pytest.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini index e245b56961..41a629e63b 100644 --- a/internal_tests/pytests/pytest.ini +++ b/internal_tests/pytests/pytest.ini @@ -2,4 +2,5 @@ markers = util: custom marker for testing metplus/util logic wrapper: custom marker for testing metplus/wrapper logic - long: custom marker for tests that take a long time to run \ No newline at end of file + long: custom marker for tests that take a long time to run + plotting: custom marker for tests that involve plotting From 5e9698810c0977032fb9fbb641106459194591a4 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:06:57 -0600 Subject: [PATCH 20/35] improved logic for removing underscore after 'not' and around 'or' to specify more complex marker strings --- .github/actions/run_tests/entrypoint.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 19a0b16f97..5a3e85be2b 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -55,10 +55,11 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then -f .github/actions/run_tests/Dockerfile.run \ . + # strip off pytests_ from marker string marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" - if [[ "$marker" == not* ]]; then - marker="not $( cut -d '_' -f 2- <<< "$marker" )" - fi + # remove underscore after 'not' and around 'or' + marker="${marker//_or_/ or }" + marker="${marker//not_/not }" echo Running Pytests marker=$marker command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" From 4bf56aeec06cad8a385501920f717bf5511874a0 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:07:12 -0600 Subject: [PATCH 21/35] test running group of 3 markers --- .github/parm/pytest_groups.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/parm/pytest_groups.txt b/.github/parm/pytest_groups.txt index 98156cd4b1..027fca5622 100644 --- a/.github/parm/pytest_groups.txt +++ b/.github/parm/pytest_groups.txt @@ -1,4 +1 @@ -wrapper -util -not_wrapper -long +plotting_or_long_or_util From d8169db07c2aa2d71d198947ef38b2132f284763 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 13:14:53 -0600 Subject: [PATCH 22/35] fixed path the broke when test file was moved into a lower directory --- .../tcmpr_plotter/test_tcmpr_plotter.py | 0 .../wrappers/stat_analysis/test_stat_analysis.py | 15 ++++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) rename internal_tests/pytests/{ => plotting}/tcmpr_plotter/test_tcmpr_plotter.py (100%) diff --git a/internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py b/internal_tests/pytests/plotting/tcmpr_plotter/test_tcmpr_plotter.py similarity index 100% rename from internal_tests/pytests/tcmpr_plotter/test_tcmpr_plotter.py rename to internal_tests/pytests/plotting/tcmpr_plotter/test_tcmpr_plotter.py diff --git a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py index dc596becdc..63a4bbf1db 100644 --- a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py +++ b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py @@ -548,22 +548,19 @@ def test_get_lookin_dir(metplus_config): 'OBS_THRESH_LIST', 'COV_THRESH_LIST', 'ALPHA_LIST', 'LINE_TYPE_LIST' ] lists_to_loop = [ 'FCST_VALID_HOUR_LIST', 'MODEL_LIST' ] - stat_analysis_pytest_dir = os.path.dirname(__file__) + pytest_data_dir = os.path.join(os.path.dirname(__file__), os.pardir, + os.pardir, os.pardir, 'data') # Test 1 - expected_lookin_dir = os.path.join(stat_analysis_pytest_dir, - '../../data/fake/20180201') - dir_path = os.path.join(stat_analysis_pytest_dir, - '../../data/fake/*') + expected_lookin_dir = os.path.join(pytest_data_dir, 'fake/20180201') + dir_path = os.path.join(pytest_data_dir, 'fake/*') test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) assert expected_lookin_dir == test_lookin_dir # Test 2 - expected_lookin_dir = os.path.join(stat_analysis_pytest_dir, - '../../data/fake/20180201') - dir_path = os.path.join(stat_analysis_pytest_dir, - '../../data/fake/{valid?fmt=%Y%m%d}') + expected_lookin_dir = os.path.join(pytest_data_dir, 'fake/20180201') + dir_path = os.path.join(pytest_data_dir, 'fake/{valid?fmt=%Y%m%d}') test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) From a2528582bfec32e2d7139136fa2ea0cc4061f52f Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 13:16:04 -0600 Subject: [PATCH 23/35] Changed StatAnalysis tests to use plotting marker because some of the tests involve plotting but the other StatAnalysis tests produce output that are used in the plotting tests --- .../stat_analysis/test_stat_analysis.py | 29 +++++++++---------- .../test_stat_analysis_plotting.py | 6 ++-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py index 63a4bbf1db..68a1b23558 100644 --- a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py +++ b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis.py @@ -26,7 +26,7 @@ def stat_analysis_wrapper(metplus_config): return StatAnalysisWrapper(config) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_command(metplus_config): # Independently test that the stat_analysis command # is being put together correctly with @@ -46,7 +46,7 @@ def test_get_command(metplus_config): assert expected_command == test_command -@pytest.mark.wrapper +@pytest.mark.plotting def test_create_c_dict(metplus_config): # Independently test that c_dict is being created # and that the wrapper and config reader @@ -79,7 +79,7 @@ def test_create_c_dict(metplus_config): assert c_dict['LINE_TYPE_LIST'] == [] -@pytest.mark.wrapper +@pytest.mark.plotting def test_list_to_str(metplus_config): # Independently test that a list of strings # are being converted to a one @@ -95,7 +95,7 @@ def test_list_to_str(metplus_config): assert(expected_list == test_list) -@pytest.mark.wrapper +@pytest.mark.plotting def test_set_lists_as_loop_or_group(metplus_config): # Independently test that the lists that are set # in the config file are being set @@ -171,7 +171,7 @@ def test_set_lists_as_loop_or_group(metplus_config): 'lt805,lt1609,lt4828,lt8045,ge8045,lt16090'), ] ) -@pytest.mark.wrapper +@pytest.mark.plotting def test_format_thresh(metplus_config, expression, expected_result): # Independently test the creation of # string values for defining thresholds @@ -180,7 +180,7 @@ def test_format_thresh(metplus_config, expression, expected_result): assert st.format_thresh(expression) == expected_result -@pytest.mark.wrapper +@pytest.mark.plotting def test_build_stringsub_dict(metplus_config): # Independently test the building of # the dictionary used in the stringtemplate @@ -384,7 +384,7 @@ def test_build_stringsub_dict(metplus_config): datetime.datetime(1900, 1, 1, 23, 59 ,59)) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_output_filename(metplus_config): # Independently test the building of # the output file name @@ -502,7 +502,7 @@ def test_get_output_filename(metplus_config): assert expected_output_filename == test_output_filename -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_lookin_dir(metplus_config): # Independently test the building of # the lookin directory @@ -568,15 +568,14 @@ def test_get_lookin_dir(metplus_config): # Test 3 - no matches for lookin dir wildcard expected_lookin_dir = '' - dir_path = os.path.join(stat_analysis_pytest_dir, - '../../data/fake/*nothingmatches*') + dir_path = os.path.join(pytest_data_dir, 'fake/*nothingmatches*') test_lookin_dir = st.get_lookin_dir(dir_path, lists_to_loop, lists_to_group, config_dict) assert expected_lookin_dir == test_lookin_dir -@pytest.mark.wrapper +@pytest.mark.plotting def test_format_valid_init(metplus_config): # Independently test the formatting # of the valid and initialization date and hours @@ -678,7 +677,7 @@ def test_format_valid_init(metplus_config): assert(config_dict['OBS_INIT_HOUR'] == '"000000", "120000"') -@pytest.mark.wrapper +@pytest.mark.plotting def test_parse_model_info(metplus_config): # Independently test the creation of # the model information dictionary @@ -717,7 +716,7 @@ def test_parse_model_info(metplus_config): expected_out_stat_filename_type) -@pytest.mark.wrapper +@pytest.mark.plotting def test_run_stat_analysis(metplus_config): # Test running of stat_analysis st = stat_analysis_wrapper(metplus_config) @@ -747,7 +746,7 @@ def test_run_stat_analysis(metplus_config): ('OBS', '\"(0,*,*)\", \"(1,*,*)\"', ["0,*,*", "1,*,*"]), ] ) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_level_list(metplus_config, data_type, config_list, expected_list): config = metplus_config() config.set('config', f'{data_type}_LEVEL_LIST', config_list) @@ -757,7 +756,7 @@ def test_get_level_list(metplus_config, data_type, config_list, expected_list): assert saw.get_level_list(data_type) == expected_list -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' config = metplus_config() diff --git a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py index bd93351a15..e869b5d580 100644 --- a/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py +++ b/internal_tests/pytests/wrappers/stat_analysis/test_stat_analysis_plotting.py @@ -27,7 +27,7 @@ def stat_analysis_wrapper(metplus_config): return StatAnalysisWrapper(config) -@pytest.mark.wrapper +@pytest.mark.plotting def test_set_lists_as_loop_or_group(metplus_config): # Independently test that the lists that are set # in the config file are being set @@ -89,7 +89,7 @@ def test_set_lists_as_loop_or_group(metplus_config): for elem in test_lists_to_loop_items)) -@pytest.mark.wrapper +@pytest.mark.plotting def test_get_output_filename(metplus_config): # Independently test the building of # the output file name @@ -163,7 +163,7 @@ def test_get_output_filename(metplus_config): assert expected_output_filename == test_output_filename -@pytest.mark.wrapper +@pytest.mark.plotting def test_filter_for_plotting(metplus_config): # Test running of stat_analysis st = stat_analysis_wrapper(metplus_config) From d9c391a9bf3a49b870784901e1db9cb024990e73 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 13:38:58 -0600 Subject: [PATCH 24/35] changed some tests from marker 'wrapper' to 'wrapper_a' to split up some of these tests into separate runs --- .github/parm/pytest_groups.txt | 2 ++ internal_tests/pytests/pytest.ini | 1 + .../test_ensemble_stat_wrapper.py | 10 +++++----- .../grid_stat/test_grid_stat_wrapper.py | 8 ++++---- .../pytests/wrappers/mode/test_mode_wrapper.py | 8 ++++---- .../point_stat/test_point_stat_wrapper.py | 6 +++--- .../series_analysis/test_series_analysis.py | 18 +++++++++--------- .../wrappers/tc_gen/test_tc_gen_wrapper.py | 4 ++-- 8 files changed, 30 insertions(+), 27 deletions(-) diff --git a/.github/parm/pytest_groups.txt b/.github/parm/pytest_groups.txt index 027fca5622..d25461ceb8 100644 --- a/.github/parm/pytest_groups.txt +++ b/.github/parm/pytest_groups.txt @@ -1 +1,3 @@ +wrapper +wrapper_a plotting_or_long_or_util diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini index 41a629e63b..63be30cc70 100644 --- a/internal_tests/pytests/pytest.ini +++ b/internal_tests/pytests/pytest.ini @@ -2,5 +2,6 @@ markers = util: custom marker for testing metplus/util logic wrapper: custom marker for testing metplus/wrapper logic + wrapper_a: custom marker for testing metplus/wrapper logic - A group long: custom marker for tests that take a long time to run plotting: custom marker for tests that involve plotting diff --git a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py index 5dab906682..ea6757eec5 100644 --- a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py @@ -106,7 +106,7 @@ def set_minimum_config_settings(config, set_fields=True): }), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_ensemble_stat_field_info(metplus_config, config_overrides, env_var_values): @@ -158,7 +158,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -666,7 +666,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_ensemble_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -723,7 +723,7 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, assert(env_var_values.get(env_var_key, '') == actual_value) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' @@ -746,7 +746,7 @@ def test_get_config_file(metplus_config): ({'ENSEMBLE_STAT_ENS_MEMBER_IDS': '1'}, 1), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_ensemble_stat_fill_missing(metplus_config, config_overrides, expected_num_files): config = metplus_config() diff --git a/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py b/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py index 598692b831..d6291085f5 100644 --- a/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py @@ -85,7 +85,7 @@ def set_minimum_config_settings(config): {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): config = metplus_config() @@ -124,7 +124,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -688,7 +688,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_grid_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -743,7 +743,7 @@ def test_grid_stat_single_field(metplus_config, config_overrides, assert env_var_values.get(env_var_key, '') == actual_value -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/wrappers/mode/test_mode_wrapper.py b/internal_tests/pytests/wrappers/mode/test_mode_wrapper.py index 3d945a022b..6f31714186 100644 --- a/internal_tests/pytests/wrappers/mode/test_mode_wrapper.py +++ b/internal_tests/pytests/wrappers/mode/test_mode_wrapper.py @@ -315,7 +315,7 @@ def set_minimum_config_settings(config): ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_mode_single_field(metplus_config, config_overrides, expected_output): config = metplus_config() @@ -398,7 +398,7 @@ def test_mode_single_field(metplus_config, config_overrides, {'METPLUS_MULTIVAR_LOGIC': 'multivar_logic = "#1 && #2 && #3";'}), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_mode_multi_variate(metplus_config, config_overrides, expected_output): config = metplus_config() @@ -503,7 +503,7 @@ def test_mode_multi_variate(metplus_config, config_overrides, ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_config_synonyms(metplus_config, config_name, env_var_name, met_name, var_type): """! Ensure that different METplus config variable names set the correct @@ -529,7 +529,7 @@ def test_config_synonyms(metplus_config, config_name, env_var_name, assert wrapper.env_var_dict[env_var_name] == expected_output -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py b/internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py index 62b08f736b..84ddf4e98c 100755 --- a/internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/point_stat/test_point_stat_wrapper.py @@ -39,7 +39,7 @@ def set_minimum_config_settings(config): config.set('config', 'POINT_STAT_OUTPUT_TEMPLATE', '{valid?fmt=%Y%m%d%H}') -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_met_dictionary_in_var_options(metplus_config): config = metplus_config() set_minimum_config_settings(config) @@ -470,7 +470,7 @@ def test_met_dictionary_in_var_options(metplus_config): {'METPLUS_FCST_FILE_TYPE': 'file_type = NETCDF_PINT;'}), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_point_stat_all_fields(metplus_config, config_overrides, env_var_values): level_no_quotes = '(*,*)' @@ -579,7 +579,7 @@ def test_point_stat_all_fields(metplus_config, config_overrides, assert env_var_values.get(env_var_key, '') == value -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py b/internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py index bb067af134..ca534272c0 100644 --- a/internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py +++ b/internal_tests/pytests/wrappers/series_analysis/test_series_analysis.py @@ -293,7 +293,7 @@ def set_minimum_config_settings(config): ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_series_analysis_single_field(metplus_config, config_overrides, env_var_values): @@ -345,7 +345,7 @@ def test_series_analysis_single_field(metplus_config, config_overrides, assert env_var_values.get(env_var_key, '') == actual_value -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_fcst_file_info(metplus_config): """ Verify that the tuple created by get_fcst_file_info is not an empty tuple, and that the number, beginning @@ -380,7 +380,7 @@ def test_get_fcst_file_info(metplus_config): assert end == expected_end -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_storms_list(metplus_config): """Verify that the expected number of storms are found for the init time 20141214_00 @@ -491,7 +491,7 @@ def test_get_storms_list(metplus_config): ]), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, expect_obs_subset): """! Test to ensure that get_all_files only gets the files that are relevant to the runtime settings and not every file in the directory @@ -715,7 +715,7 @@ def test_get_all_files_and_subset(metplus_config, time_info, expect_fcst_subset, ]), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_fcst_and_obs_path(metplus_config, config_overrides, time_info, storm_id, lead_group, expect_fcst_subset, expect_obs_subset): @@ -833,7 +833,7 @@ def test_get_fcst_and_obs_path(metplus_config, config_overrides, '_FILES_F012_to_F018'), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_ascii_filename(metplus_config, storm_id, leads, expected_result): wrapper = series_analysis_wrapper(metplus_config) @@ -869,7 +869,7 @@ def test_get_ascii_filename(metplus_config, storm_id, leads, 'ML1221072014', '', '20141031_12/ML1221072014_'), ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_output_dir(metplus_config, template, storm_id, label, expected_result): time_info = {'init': datetime(2014, 10, 31, 12, 15), 'valid': datetime(2014, 10, 31, 18, 15), @@ -882,7 +882,7 @@ def test_get_output_dir(metplus_config, template, storm_id, label, expected_resu assert(actual_result == os.path.join(output_dir, expected_result)) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_netcdf_min_max(metplus_config): expected_min = 0.0 expected_max = 8.0 @@ -900,7 +900,7 @@ def test_get_netcdf_min_max(metplus_config): assert max == expected_max -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' diff --git a/internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py b/internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py index 657021b34a..d424abbeae 100644 --- a/internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py +++ b/internal_tests/pytests/wrappers/tc_gen/test_tc_gen_wrapper.py @@ -285,7 +285,7 @@ ] ) -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_tc_gen(metplus_config, config_overrides, env_var_values): # expected number of 2016 files (including file_list line) expected_genesis_count = 7 @@ -419,7 +419,7 @@ def test_tc_gen(metplus_config, config_overrides, env_var_values): assert len(lines) == expected_track_count -@pytest.mark.wrapper +@pytest.mark.wrapper_a def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' From 1f1d0b058b93e1b7b81b739b9d5d4bd836ebd33b Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:17:30 -0600 Subject: [PATCH 25/35] test to see if running pytests in single job but split into groups by marker will improve the timing enough --- .github/actions/run_tests/entrypoint.sh | 19 ++++++++++++++----- .github/jobs/get_use_cases_to_run.sh | 9 +++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 5a3e85be2b..7341419d0d 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -55,13 +55,22 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then -f .github/actions/run_tests/Dockerfile.run \ . + pytests_groups_filepath=.github/parm/pytest_groups.txt # strip off pytests_ from marker string - marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" + #marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" # remove underscore after 'not' and around 'or' - marker="${marker//_or_/ or }" - marker="${marker//not_/not }" - echo Running Pytests marker=$marker - command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests; /usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" + #marker="${marker//_or_/ or }" + #marker="${marker//not_/not }" + echo Running Pytests #marker=$marker + command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests;" + command+="status=0;" + for x in `cat $pytests_groups_filepath`; do + marker="${x//_or_/ or }" + marker="${marker//not_/not }" + command+= "/usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" + command+=";if [ $? != 0 ]; then status=1; fi;" + done + command+="if [ $status != 0 ]; then false; fi" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" exit $? fi diff --git a/.github/jobs/get_use_cases_to_run.sh b/.github/jobs/get_use_cases_to_run.sh index 0c0790e4a2..304b5ee5db 100755 --- a/.github/jobs/get_use_cases_to_run.sh +++ b/.github/jobs/get_use_cases_to_run.sh @@ -32,10 +32,11 @@ fi if [ "$run_unit_tests" == "true" ]; then echo Adding unit tests to list to run - pytests="" - for x in `cat $pytests_groups_filepath`; do - pytests=$pytests"\"pytests_$x\"," - done + #pytests="" + #for x in `cat $pytests_groups_filepath`; do + # pytests=$pytests"\"pytests_$x\"," + #done + pytests="\"pytests\"," # if matrix is empty, set to an array that only includes pytests if [ "$matrix" == "[]" ]; then From dc8211171e3c09efcd209f1e8126138a427b812d Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:33:42 -0600 Subject: [PATCH 26/35] fixed typos in new logic --- .github/actions/run_tests/entrypoint.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 7341419d0d..f852e5b7d1 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -67,10 +67,10 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then for x in `cat $pytests_groups_filepath`; do marker="${x//_or_/ or }" marker="${marker//not_/not }" - command+= "/usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" - command+=";if [ $? != 0 ]; then status=1; fi;" + command+="/usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" + command+=";if [ \$? != 0 ]; then status=1; fi;" done - command+="if [ $status != 0 ]; then false; fi" + command+="if [ \$status != 0 ]; then false; fi" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" exit $? fi From 69b0cce55a1d8df7d0907911a6892d4aea80411e Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:16:36 -0600 Subject: [PATCH 27/35] removed code that is no longer needed (added comment in issue #685 if this logic is desired in the future) --- .github/actions/run_tests/entrypoint.sh | 16 ++++------------ .github/jobs/get_use_cases_to_run.sh | 6 +----- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index f852e5b7d1..1897d77802 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -8,6 +8,8 @@ WS_PATH=$RUNNER_WORKSPACE/$REPO_NAME # set CI jobs directory variable to easily move it CI_JOBS_DIR=.github/jobs +PYTESTS_GROUPS_FILEPATH=.github/parm/pytest_groups.txt + source ${GITHUB_WORKSPACE}/${CI_JOBS_DIR}/bash_functions.sh # get branch name for push or pull request events @@ -30,9 +32,7 @@ if [ $? != 0 ]; then ${GITHUB_WORKSPACE}/${CI_JOBS_DIR}/docker_setup.sh fi -# # running unit tests (pytests) -# if [[ "$INPUT_CATEGORIES" == pytests* ]]; then export METPLUS_ENV_TAG="pytest" export METPLUS_IMG_TAG=${branch_name} @@ -55,16 +55,10 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then -f .github/actions/run_tests/Dockerfile.run \ . - pytests_groups_filepath=.github/parm/pytest_groups.txt - # strip off pytests_ from marker string - #marker="$( cut -d '_' -f 2- <<< "$INPUT_CATEGORIES" )" - # remove underscore after 'not' and around 'or' - #marker="${marker//_or_/ or }" - #marker="${marker//not_/not }" - echo Running Pytests #marker=$marker + echo Running Pytests command="export METPLUS_PYTEST_HOST=docker; cd internal_tests/pytests;" command+="status=0;" - for x in `cat $pytests_groups_filepath`; do + for x in `cat $PYTESTS_GROUPS_FILEPATH`; do marker="${x//_or_/ or }" marker="${marker//not_/not }" command+="/usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" @@ -75,9 +69,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then exit $? fi -# # running use case tests -# # split apart use case category and subset list from input CATEGORIES=`echo $INPUT_CATEGORIES | awk -F: '{print $1}'` diff --git a/.github/jobs/get_use_cases_to_run.sh b/.github/jobs/get_use_cases_to_run.sh index 304b5ee5db..341d1c4801 100755 --- a/.github/jobs/get_use_cases_to_run.sh +++ b/.github/jobs/get_use_cases_to_run.sh @@ -1,7 +1,7 @@ #! /bin/bash use_case_groups_filepath=.github/parm/use_case_groups.json -pytests_groups_filepath=.github/parm/pytest_groups.txt + # set matrix to string of an empty array in case no use cases will be run matrix="[]" @@ -32,10 +32,6 @@ fi if [ "$run_unit_tests" == "true" ]; then echo Adding unit tests to list to run - #pytests="" - #for x in `cat $pytests_groups_filepath`; do - # pytests=$pytests"\"pytests_$x\"," - #done pytests="\"pytests\"," # if matrix is empty, set to an array that only includes pytests From 050c1a24b48cd03a24c0ba9a9b48e7b328434c28 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:22:44 -0600 Subject: [PATCH 28/35] per #685, divided pytests into smaller groups --- .github/parm/pytest_groups.txt | 5 ++++- internal_tests/pytests/pytest.ini | 4 +++- .../ensemble_stat/test_ensemble_stat_wrapper.py | 10 +++++----- .../wrappers/grid_stat/test_grid_stat_wrapper.py | 8 ++++---- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/parm/pytest_groups.txt b/.github/parm/pytest_groups.txt index d25461ceb8..374b99da80 100644 --- a/.github/parm/pytest_groups.txt +++ b/.github/parm/pytest_groups.txt @@ -1,3 +1,6 @@ +util wrapper wrapper_a -plotting_or_long_or_util +wrapper_b +wrapper_c +plotting_or_long diff --git a/internal_tests/pytests/pytest.ini b/internal_tests/pytests/pytest.ini index 63be30cc70..8630509ec0 100644 --- a/internal_tests/pytests/pytest.ini +++ b/internal_tests/pytests/pytest.ini @@ -1,7 +1,9 @@ [pytest] markers = util: custom marker for testing metplus/util logic - wrapper: custom marker for testing metplus/wrapper logic wrapper_a: custom marker for testing metplus/wrapper logic - A group + wrapper_b: custom marker for testing metplus/wrapper logic - B group + wrapper_c: custom marker for testing metplus/wrapper logic - C group + wrapper: custom marker for testing metplus/wrapper logic - all others long: custom marker for tests that take a long time to run plotting: custom marker for tests that involve plotting diff --git a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py index ea6757eec5..b28d875704 100644 --- a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py @@ -106,7 +106,7 @@ def set_minimum_config_settings(config, set_fields=True): }), ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_c def test_ensemble_stat_field_info(metplus_config, config_overrides, env_var_values): @@ -158,7 +158,7 @@ def test_ensemble_stat_field_info(metplus_config, config_overrides, {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_c def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -666,7 +666,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_c def test_ensemble_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -723,7 +723,7 @@ def test_ensemble_stat_single_field(metplus_config, config_overrides, assert(env_var_values.get(env_var_key, '') == actual_value) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_c def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' @@ -746,7 +746,7 @@ def test_get_config_file(metplus_config): ({'ENSEMBLE_STAT_ENS_MEMBER_IDS': '1'}, 1), ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_c def test_ensemble_stat_fill_missing(metplus_config, config_overrides, expected_num_files): config = metplus_config() diff --git a/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py b/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py index d6291085f5..701821dc99 100644 --- a/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/grid_stat/test_grid_stat_wrapper.py @@ -85,7 +85,7 @@ def set_minimum_config_settings(config): {'FCST_IS_PROB': False, 'OBS_IS_PROB': False}), ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_b def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): config = metplus_config() @@ -124,7 +124,7 @@ def test_grid_stat_is_prob(metplus_config, config_overrides, expected_values): {'CLIMO_STDEV_FILE': '"/climo/stdev/dir/gs_stdev_YMDH.tmpl"', }), ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_b def test_handle_climo_file_variables(metplus_config, config_overrides, env_var_values): """! Ensure that old and new variables for setting climo_mean and @@ -688,7 +688,7 @@ def test_handle_climo_file_variables(metplus_config, config_overrides, ] ) -@pytest.mark.wrapper_a +@pytest.mark.wrapper_b def test_grid_stat_single_field(metplus_config, config_overrides, env_var_values): @@ -743,7 +743,7 @@ def test_grid_stat_single_field(metplus_config, config_overrides, assert env_var_values.get(env_var_key, '') == actual_value -@pytest.mark.wrapper_a +@pytest.mark.wrapper_b def test_get_config_file(metplus_config): fake_config_name = '/my/config/file' From 038d049edd51a775cc32be4345218b16895135ec Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:24:00 -0600 Subject: [PATCH 29/35] added a test that will fail to test that the entire pytest job will fail as expected --- .../wrappers/ensemble_stat/test_ensemble_stat_wrapper.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py index b28d875704..0e1aa07210 100644 --- a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py @@ -776,3 +776,8 @@ def test_ensemble_stat_fill_missing(metplus_config, config_overrides, actual_num_files = len(file_handle.read().splitlines()) - 1 assert actual_num_files == expected_num_files + + +@pytest.mark.wrapper_c +def test_failure_testing(): + assert False From 23fe0ee1417e8e8e31dd8dbbc3c97855b7d65155 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 16:38:20 -0600 Subject: [PATCH 30/35] add error message if any pytests failed to help reviewer search for failed tests --- .github/actions/run_tests/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/run_tests/entrypoint.sh b/.github/actions/run_tests/entrypoint.sh index 1897d77802..7e0a4b72d7 100644 --- a/.github/actions/run_tests/entrypoint.sh +++ b/.github/actions/run_tests/entrypoint.sh @@ -64,7 +64,7 @@ if [[ "$INPUT_CATEGORIES" == pytests* ]]; then command+="/usr/local/envs/pytest/bin/pytest -vv --cov=../../metplus -m \"$marker\"" command+=";if [ \$? != 0 ]; then status=1; fi;" done - command+="if [ \$status != 0 ]; then false; fi" + command+="if [ \$status != 0 ]; then echo ERROR: Some pytests failed. Search for FAILED to review; false; fi" time_command docker run -v $WS_PATH:$GITHUB_WORKSPACE --workdir $GITHUB_WORKSPACE $RUN_TAG bash -c "$command" exit $? fi From e7860cd0c5506d3eaf14b833debf90b092c6f825 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:00:11 -0600 Subject: [PATCH 31/35] removed failing test after confirming that entire pytest job properly reports an error if any test fails --- .../wrappers/ensemble_stat/test_ensemble_stat_wrapper.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py index 0e1aa07210..b28d875704 100644 --- a/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py +++ b/internal_tests/pytests/wrappers/ensemble_stat/test_ensemble_stat_wrapper.py @@ -776,8 +776,3 @@ def test_ensemble_stat_fill_missing(metplus_config, config_overrides, actual_num_files = len(file_handle.read().splitlines()) - 1 assert actual_num_files == expected_num_files - - -@pytest.mark.wrapper_c -def test_failure_testing(): - assert False From 3cf5b9127db876b8efb04bce9bbcc816f1555d2a Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:08:48 -0600 Subject: [PATCH 32/35] turn on single use case group to make sure logic to build matrix of test jobs to run still works as expected --- .github/parm/use_case_groups.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index 9a9a0627bc..e053dfbdb0 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -27,7 +27,7 @@ { "category": "convection_allowing_models", "index_list": "0", - "run": false + "run": true }, { "category": "convection_allowing_models", From 962cd22eefbadb1765864e5c49aae2a89db03f35 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:32:43 -0600 Subject: [PATCH 33/35] turn off use case after confirming tests were created properly --- .github/parm/use_case_groups.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/parm/use_case_groups.json b/.github/parm/use_case_groups.json index e053dfbdb0..9a9a0627bc 100644 --- a/.github/parm/use_case_groups.json +++ b/.github/parm/use_case_groups.json @@ -27,7 +27,7 @@ { "category": "convection_allowing_models", "index_list": "0", - "run": true + "run": false }, { "category": "convection_allowing_models", From 0a4ed3fadabfad7e1969d067d1b34972aa89d4d5 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:41:49 -0600 Subject: [PATCH 34/35] added documentation to contributor's guide to describe changes to unit test logic --- .../continuous_integration.rst | 32 ++++++++++++++ docs/Contributors_Guide/testing.rst | 44 +++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/docs/Contributors_Guide/continuous_integration.rst b/docs/Contributors_Guide/continuous_integration.rst index 2ad3091b6b..dbb293112e 100644 --- a/docs/Contributors_Guide/continuous_integration.rst +++ b/docs/Contributors_Guide/continuous_integration.rst @@ -557,6 +557,38 @@ process can be found in the :ref:`use_case_input_data` section of the Add Use Cases chapter of the Contributor's Guide. +.. _cg-ci-unit-tests: + +Unit Tests +---------- + +Unit tests are run via pytest. +Groups of pytests are run in the 'pytests' job. +The list of groups that will be run in the automated tests are found in +.github/parm/pytest_groups.txt. +See :ref:`cg-unit-tests` for more information on pytest groups. + +Items in pytest_groups.txt can include:: + + * A single group marker name, i.e. wrapper_a + * Multiple group marker names separated by _or_, i.e. plotting_or_long + * A group marker name to exclude starting with not_, i.e. not_wrapper + +All pytest groups are currently run in a single GitHub Actions job. +This was done because the existing automation logic builds a Docker +environment to run the tests and each testing environment takes a few minutes +to create (future improvements may speed up execution time by running the +pytests directly in the GitHub Actions environment instead of Docker). +Running the pytests in smaller groups serially takes substantially less time +than calling all of the existing pytests in a single call to pytest, +so dividing tests into groups is recommended to improve performance. +Searching for the string "deselected in" in the pytests job log can be used +to see how long each group took to run. + +Future enhancements could be made to save and parse this information for each +run to output a summary at the end of the log file to more easily see which +groups could be broken up to improve performance. + .. _cg-ci-use-case-tests: Use Case Tests diff --git a/docs/Contributors_Guide/testing.rst b/docs/Contributors_Guide/testing.rst index 3db6f6ff72..6ceb9fdc62 100644 --- a/docs/Contributors_Guide/testing.rst +++ b/docs/Contributors_Guide/testing.rst @@ -4,19 +4,55 @@ Testing Test scripts are found in the GitHub repository in the internal_tests directory. +.. _cg-unit-tests: + Unit Tests ---------- Unit tests are run with pytest. They are found in the *pytests* directory. Each tool has its own subdirectory containing its test files. -**run_pytests.sh** is a bash script that can be run to execute all of the -pytests. A report will be output showing which pytest categories failed. -When running on a new computer, a -**minimum_pytest..sh** +Unit tests can be run by running the 'pytest' command from the +internal_tests/pytests directory of the repository. +The 'pytest' Python package must be available. +A report will be output showing which pytest categories failed. +When running on a new computer, a **minimum_pytest..sh** file must be created to be able to run the script. This file contains information about the local environment so that the tests can run. +All unit tests must include one of the custom markers listed in the +internal_tests/pytests/pytest.ini file. Some examples include: + + * util + * wrapper_a + * wrapper_b + * wrapper_c + * wrapper + * long + * plotting + +To apply a marker to a unit test function, add the following on the line before +the function definition:: + + @pytest.mark. + +where is one of the custom marker strings listed in pytest.ini. + +There are many unit tests for METplus and false failures can occur if all of +the are attempted to run at once. +To run only tests with a given marker, run:: + + pytest -m + +To run all tests that do not have a given marker, run:: + + pytest -m "not " + +Multiple marker groups can be run by using the 'or' keyword:: + + pytest -m " or " + + Use Case Tests -------------- From d1a3b55d9700f6a502e850d8562ebf1b3aa26dd4 Mon Sep 17 00:00:00 2001 From: George McCabe <23407799+georgemccabe@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:44:16 -0600 Subject: [PATCH 35/35] added note about adding new pytest markers --- docs/Contributors_Guide/testing.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Contributors_Guide/testing.rst b/docs/Contributors_Guide/testing.rst index 6ceb9fdc62..93c2f9627f 100644 --- a/docs/Contributors_Guide/testing.rst +++ b/docs/Contributors_Guide/testing.rst @@ -38,6 +38,10 @@ the function definition:: where is one of the custom marker strings listed in pytest.ini. +New pytest markers should be added to the pytest.ini file with a brief +description. If they are not added to the markers list, then a warning will +be output when running the tests. + There are many unit tests for METplus and false failures can occur if all of the are attempted to run at once. To run only tests with a given marker, run::