Skip to content

Commit 025f09a

Browse files
authored
Merge pull request #4952 from ESMCI/jgfouca/imprv_baseline_handling
Improve baseline handling when regenerating baselines
2 parents be0a93e + c359287 commit 025f09a

4 files changed

Lines changed: 103 additions & 9 deletions

File tree

CIME/case/case_cmpgen_namelists.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,14 @@ def _do_full_nl_gen_impl(case, test, generate_name, baseline_root=None):
9595

9696
shutil.copytree(casedoc_dir, baseline_casedocs)
9797

98-
for item in glob.glob(os.path.join(test_dir, "user_nl*")):
99-
preexisting_baseline = os.path.join(baseline_dir, os.path.basename(item))
100-
if os.path.exists(preexisting_baseline):
101-
os.remove(preexisting_baseline)
98+
# Note: If it is ever the case that nml cmp/gen affects more files than
99+
# just user_nl* and CaseDocs, this will break assumptions all across CIME.
100+
101+
# Remove any previous user_nl files
102+
for item in glob.glob(os.path.join(baseline_dir, "user_nl*")):
103+
os.remove(item)
102104

105+
for item in glob.glob(os.path.join(test_dir, "user_nl*")):
103106
safe_copy(item, baseline_dir, preserve_meta=False)
104107

105108

CIME/hist_utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import re
77
import filecmp
8+
import shutil
89

910
from CIME.XML.standard_module_setup import *
1011
from CIME.config import Config
@@ -651,6 +652,20 @@ def _generate_baseline_impl(case, baseline_dir=None, allow_baseline_overwrite=Fa
651652
):
652653
expect(False, " Cowardly refusing to overwrite existing baseline directory")
653654

655+
# Remove stale baseline files from a previous run so they don't linger when the
656+
# new run no longer produces them. CaseDocs and user_nl* are managed by the
657+
# namelist-generation phase (which already ran), so we preserve those.
658+
preserve_list = ["CaseDocs", BLESS_LOG_NAME, TEST_STATUS_FILENAME]
659+
if os.path.isdir(basegen_dir):
660+
for item in os.listdir(basegen_dir):
661+
if item in preserve_list or item.startswith("user_nl"):
662+
continue
663+
item_path = os.path.join(basegen_dir, item)
664+
if os.path.isfile(item_path):
665+
os.remove(item_path)
666+
elif os.path.isdir(item_path):
667+
shutil.rmtree(item_path)
668+
654669
comments = "Generating baselines into '{}'\n".format(basegen_dir)
655670
num_gen = 0
656671
for model in _iter_model_file_substrs(case):

CIME/test_scheduler.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
get_project,
2929
get_timestamp,
3030
get_cime_default_driver,
31-
clear_folder,
3231
CIMEError,
3332
)
3433
from CIME.config import Config
@@ -360,10 +359,7 @@ def __init__(
360359
if os.path.isdir(test_baseline):
361360
existing_baselines.append(test_baseline)
362361
if allow_baseline_overwrite and run_count == 0:
363-
if self._namelists_only:
364-
clear_folder(os.path.join(test_baseline, "CaseDocs"))
365-
else:
366-
clear_folder(test_baseline)
362+
pass
367363
elif skip_tests_with_existing_baselines:
368364
tests_to_skip.append(test_name)
369365
expect(

CIME/tests/test_sys_test_scheduler.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,86 @@ def test_e_test_inferred_compiler(self):
586586
)
587587
self.assertEqual(os.path.split(result)[1], "gnuX")
588588

589+
def test_f_baseline_not_cleared_on_failure(self):
590+
"""
591+
Verify that existing baselines are preserved when a regeneration attempt
592+
fails. Before the fix, clear_folder ran in the TestScheduler constructor
593+
(before the test executed), so a failing run left the case with no
594+
baselines. After the fix, clearing is deferred to _generate_baseline_impl
595+
which is only reached when the run phase succeeds.
596+
597+
Steps:
598+
1. Generate baselines with TESTRUNFAIL_PASS set (test passes).
599+
2. Attempt regeneration WITHOUT TESTRUNFAIL_PASS (run fails).
600+
Baselines must remain intact.
601+
3. Compare with TESTRUNFAIL_PASS set – should succeed because the
602+
original baselines from step 1 are still present.
603+
"""
604+
test_name = "TESTRUNFAIL_P1.f19_g16.A"
605+
if self._config.create_test_flag_mode == "e3sm":
606+
genargs = ["-g", "-o", "-b", self._baseline_name, test_name]
607+
compargs = ["-c", "-b", self._baseline_name, test_name]
608+
else:
609+
genargs = ["-g", self._baseline_name, "-o", test_name]
610+
compargs = ["-c", self._baseline_name, test_name]
611+
612+
# Step 1: Generate initial baselines with a passing run.
613+
os.environ["TESTRUNFAIL_PASS"] = "True"
614+
try:
615+
self._create_test(genargs)
616+
finally:
617+
del os.environ["TESTRUNFAIL_PASS"]
618+
619+
baseline_dir = os.path.join(self._baseline_area, self._baseline_name)
620+
self.assertTrue(
621+
os.path.isdir(baseline_dir), "Baseline dir should exist after generation"
622+
)
623+
baseline_cases = glob.glob(os.path.join(baseline_dir, "*TESTRUNFAIL*"))
624+
self.assertEqual(
625+
len(baseline_cases), 1, "Expected exactly one TESTRUNFAIL baseline dir"
626+
)
627+
628+
# Collect the set of regular files created by the baseline generation.
629+
# CaseDocs is a directory managed separately by the namelist phase and
630+
# may be legitimately refreshed during a subsequent SETUP, so we only
631+
# track files here.
632+
baseline_case_dir = baseline_cases[0]
633+
baseline_files_before = {
634+
f
635+
for f in os.listdir(baseline_case_dir)
636+
if os.path.isfile(os.path.join(baseline_case_dir, f))
637+
}
638+
self.assertGreater(
639+
len(baseline_files_before),
640+
0,
641+
"Baseline must contain at least one file after generation",
642+
)
643+
644+
# Step 2: Attempt regeneration WITHOUT TESTRUNFAIL_PASS – run will fail.
645+
# Our fix ensures that baseline files are NOT cleared before the run
646+
# completes successfully.
647+
self._create_test(genargs, run_errors=True)
648+
649+
baseline_cases_after = glob.glob(os.path.join(baseline_dir, "*TESTRUNFAIL*"))
650+
self.assertEqual(len(baseline_cases_after), 1)
651+
baseline_files_after = {
652+
f
653+
for f in os.listdir(baseline_cases_after[0])
654+
if os.path.isfile(os.path.join(baseline_cases_after[0], f))
655+
}
656+
self.assertEqual(
657+
baseline_files_before,
658+
baseline_files_after,
659+
msg="Baseline files were modified by a failed generation attempt",
660+
)
661+
662+
# Step 3: Comparison against the preserved baselines should succeed.
663+
os.environ["TESTRUNFAIL_PASS"] = "True"
664+
try:
665+
self._create_test(compargs)
666+
finally:
667+
del os.environ["TESTRUNFAIL_PASS"]
668+
589669

590670
if __name__ == "__main__":
591671
unittest.main()

0 commit comments

Comments
 (0)