|
30 | 30 | import pytest |
31 | 31 |
|
32 | 32 | from smartsim import Experiment |
33 | | -from smartsim._core.config import CONFIG |
34 | 33 | from smartsim._core.control.controller import Controller, _AnonymousBatchJob |
35 | 34 | from smartsim.database.orchestrator import Orchestrator |
36 | 35 | from smartsim.entity.ensemble import Ensemble |
|
58 | 57 |
|
59 | 58 |
|
60 | 59 | @pytest.mark.parametrize( |
61 | | - "entity", |
62 | | - [pytest.param(ens, id="ensemble"), pytest.param(model, id="model")], |
| 60 | + "entity_type", |
| 61 | + [pytest.param("ensemble", id="ensemble"), pytest.param("model", id="model")], |
63 | 62 | ) |
64 | | -def test_symlink(test_dir, entity): |
| 63 | +def test_symlink(test_dir, entity_type): |
65 | 64 | """Test symlinking historical output files""" |
66 | | - entity.path = test_dir |
67 | | - if entity.type == Ensemble: |
68 | | - for member in ens.models: |
| 65 | + if entity_type == "ensemble": |
| 66 | + entity = Ensemble( |
| 67 | + "ens", params={}, run_settings=rs, batch_settings=bs, replicas=3 |
| 68 | + ) |
| 69 | + entity.path = test_dir |
| 70 | + for member in entity.models: |
69 | 71 | symlink_with_create_job_step(test_dir, member) |
70 | 72 | else: |
| 73 | + entity = Model("test_model", params={}, path=test_dir, run_settings=rs) |
71 | 74 | symlink_with_create_job_step(test_dir, entity) |
72 | 75 |
|
73 | 76 |
|
74 | 77 | def symlink_with_create_job_step(test_dir, entity): |
75 | 78 | """Function that helps cut down on repeated testing code""" |
76 | 79 | exp_dir = pathlib.Path(test_dir) |
77 | 80 | entity.path = test_dir |
78 | | - # With simplified structure, output files go directly in .smartsim directory |
79 | | - status_dir = exp_dir / ".smartsim" |
80 | | - step = controller._create_job_step(entity) |
| 81 | + # Create run_dir to simulate timestamped run structure |
| 82 | + run_dir = exp_dir / ".smartsim" / "run_test" |
| 83 | + step = controller._create_job_step(entity, run_dir) |
81 | 84 | controller.symlink_output_files(step, entity) |
82 | 85 | assert pathlib.Path(entity.path, f"{entity.name}.out").is_symlink() |
83 | 86 | assert pathlib.Path(entity.path, f"{entity.name}.err").is_symlink() |
| 87 | + # Verify symlinks point to the correct run directory |
| 88 | + expected_out = run_dir / (entity.name + ".out") |
| 89 | + expected_err = run_dir / (entity.name + ".err") |
84 | 90 | assert os.readlink(pathlib.Path(entity.path, f"{entity.name}.out")) == str( |
85 | | - status_dir / (entity.name + ".out") |
| 91 | + expected_out |
86 | 92 | ) |
87 | 93 | assert os.readlink(pathlib.Path(entity.path, f"{entity.name}.err")) == str( |
88 | | - status_dir / (entity.name + ".err") |
| 94 | + expected_err |
89 | 95 | ) |
90 | 96 |
|
91 | 97 |
|
92 | 98 | @pytest.mark.parametrize( |
93 | | - "entity", |
| 99 | + "entity_type", |
94 | 100 | [ |
95 | | - pytest.param(ens, id="ensemble"), |
96 | | - pytest.param(orc, id="orchestrator"), |
97 | | - pytest.param(anon_batch_model, id="model"), |
| 101 | + pytest.param("ensemble", id="ensemble"), |
| 102 | + pytest.param("orchestrator", id="orchestrator"), |
| 103 | + pytest.param("model", id="model"), |
98 | 104 | ], |
99 | 105 | ) |
100 | | -def test_batch_symlink(entity, test_dir): |
| 106 | +def test_batch_symlink(entity_type, test_dir): |
101 | 107 | """Test symlinking historical output files""" |
102 | 108 | exp_dir = pathlib.Path(test_dir) |
| 109 | + |
| 110 | + # Create fresh entities for each test to avoid path conflicts |
| 111 | + if entity_type == "ensemble": |
| 112 | + entity = Ensemble( |
| 113 | + "ens", params={}, run_settings=rs, batch_settings=bs, replicas=3 |
| 114 | + ) |
| 115 | + elif entity_type == "orchestrator": |
| 116 | + entity = Orchestrator( |
| 117 | + db_nodes=3, batch=True, launcher="slurm", run_command="srun" |
| 118 | + ) |
| 119 | + else: # model |
| 120 | + batch_model = Model( |
| 121 | + "batch_test_model", |
| 122 | + params={}, |
| 123 | + path=test_dir, |
| 124 | + run_settings=batch_rs, |
| 125 | + batch_settings=bs, |
| 126 | + ) |
| 127 | + entity = _AnonymousBatchJob(batch_model) |
| 128 | + |
103 | 129 | entity.path = test_dir |
104 | | - batch_step, substeps = slurm_controller._create_batch_job_step(entity) |
| 130 | + # For entities with sub-entities (like Orchestrator), set their paths too |
| 131 | + if hasattr(entity, "entities"): |
| 132 | + for sub_entity in entity.entities: |
| 133 | + sub_entity.path = test_dir |
| 134 | + |
| 135 | + # Create run_dir to simulate timestamped run structure |
| 136 | + run_dir = exp_dir / ".smartsim" / "run_test_batch" |
| 137 | + batch_step, substeps = slurm_controller._create_batch_job_step(entity, run_dir) |
105 | 138 |
|
106 | 139 | # For batch entities, we need to call symlink_output_files correctly |
107 | 140 | # Based on how the controller does it, we should pass the individual entities |
@@ -148,7 +181,9 @@ def test_symlink_error(test_dir): |
148 | 181 | path=pathlib.Path(test_dir, "badpath"), |
149 | 182 | run_settings=RunSettings("echo"), |
150 | 183 | ) |
151 | | - bad_step = controller._create_job_step(bad_model) |
| 184 | + # Create run_dir to avoid using current working directory |
| 185 | + run_dir = pathlib.Path(test_dir) / ".smartsim" / "run_test_error" |
| 186 | + bad_step = controller._create_job_step(bad_model, run_dir) |
152 | 187 | # The new behavior should auto-create directories and symlinks without errors |
153 | 188 | controller.symlink_output_files(bad_step, bad_model) |
154 | 189 |
|
|
0 commit comments