Skip to content

Commit 6b18c05

Browse files
authored
check if all profiles have proper form in Scenario ctor. closes #115 (#318)
1 parent 413c17c commit 6b18c05

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

src/scenario.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ struct Scenario {
9595
ptr(f_scenario_ctor, f_scenario_dtor),
9696
json(json)
9797
{
98+
// TODO #317 - repeat analogous checks for rates
99+
for (auto &prof: std::set<std::string>({"height", "temp", "pressure"})) {
100+
auto prof_key = prof + "_profile";
101+
if (json.find(prof_key) != json.end()) {
102+
if (!json[prof_key].is_array() || json[prof_key].size() != 2)
103+
throw std::runtime_error(prof_key + " expected to be a 2-element list (of single-element dictionaries)");
104+
for (auto i=0; i<2; ++i)
105+
if (!json[prof_key][i].is_object() || json[prof_key][i].size() != 1)
106+
throw std::runtime_error(prof_key + " expected to contain only single-element dicts");
107+
{
108+
auto elem = json[prof_key][0];
109+
if (elem.find("time") == elem.end())
110+
throw std::runtime_error(prof_key + " first element is expeced to be a single-element dict with 'time' key");
111+
}
112+
{
113+
auto elem = json[prof_key][1];
114+
if (elem.find(prof) == elem.end())
115+
throw std::runtime_error(prof_key + " second element is expeced to be a single-element dict with '" + prof + "' key");
116+
}
117+
if (
118+
!json[prof_key][0]["time"].is_array() ||
119+
!json[prof_key][1][prof].is_array() ||
120+
json[prof_key][0]["time"].size() != json[prof_key][1][prof].size()
121+
)
122+
throw std::runtime_error(prof_key + " 'time' and '" + prof + "' arrays do not have matching size");
123+
}
124+
}
125+
98126
GimmickGuard<InputGimmick> guard(json, "dist", "mode_name");
99127
f_scenario_from_json(
100128
gas_data.ptr.f_arg(),

tests/test_scenario.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class TestScenario:
7171
"temp_profile": [{"time": [0, 1, 2]}, {"temp": [1, 2, 3]}],
7272
"pressure_profile": [{"time": [0, 1, 2]}, {"pressure": [1, 2, 3]}],
7373
"height_profile": [
74-
{"time": [0, 1, 2, 3, 4]}, # TODO #115: should be caught
74+
{"time": [0, 1, 2]},
7575
{"height": [1, 2, 3]},
7676
],
7777
"gas_emissions": [
@@ -260,3 +260,62 @@ def test_time_varying_aero_multimode(key, mode_names):
260260
dist.mode(i_mode).name
261261
]["num_conc"]
262262
)
263+
264+
ERR_MSG_2_ELEM_LIST_EXPECTED = (
265+
"PROF_profile expected to be a 2-element list (of single-element dictionaries)"
266+
)
267+
ERR_MSG_ALL_ELEMS_DICTS = (
268+
"PROF_profile expected to contain only single-element dicts"
269+
)
270+
ERR_MSG_PROFILE_LEN_MATCH = (
271+
"PROF_profile 'time' and 'PROF' arrays do not have matching size"
272+
)
273+
ERR_MSG_TIME_KEY_MISSING = (
274+
"PROF_profile first element is expeced to be"
275+
" a single-element dict with 'time' key"
276+
)
277+
ERR_MSG_PROF_KEY_MISSING = (
278+
"PROF_profile second element is expeced to be"
279+
" a single-element dict with 'PROF' key"
280+
)
281+
282+
@staticmethod
283+
@pytest.mark.parametrize("prof", ("height", "temp", "pressure"))
284+
@pytest.mark.parametrize(
285+
"data, msg",
286+
(
287+
({}, ERR_MSG_2_ELEM_LIST_EXPECTED),
288+
([{}, {}, {}], ERR_MSG_2_ELEM_LIST_EXPECTED),
289+
([[], []], ERR_MSG_ALL_ELEMS_DICTS),
290+
([{}, []], ERR_MSG_ALL_ELEMS_DICTS),
291+
([[], {}], ERR_MSG_ALL_ELEMS_DICTS),
292+
([{"1": 1, "2": 2}, {}], ERR_MSG_ALL_ELEMS_DICTS),
293+
([{"1": 1, "2": 2, "3": 3}, {"1": 1, "2": 2}], ERR_MSG_ALL_ELEMS_DICTS),
294+
([{"xtime": ""}, {"PROF": ""}], ERR_MSG_TIME_KEY_MISSING),
295+
([{"time": ""}, {"xPROF": ""}], ERR_MSG_PROF_KEY_MISSING),
296+
([{"time": ""}, {"PROF": []}], ERR_MSG_PROFILE_LEN_MATCH),
297+
([{"time": []}, {"PROF": ""}], ERR_MSG_PROFILE_LEN_MATCH),
298+
([{"time": [1, 2]}, {"PROF": [1, 2, 3]}], ERR_MSG_PROFILE_LEN_MATCH),
299+
),
300+
)
301+
def test_throws_if_profile_not_of_proper_form(prof, data, msg):
302+
# arrange
303+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
304+
gas_data = ppmc.GasData(GAS_DATA_CTOR_ARG_MINIMAL)
305+
306+
data = copy.deepcopy(data)
307+
if len(data) == 2 and isinstance(data[1], dict) and len(data[1].keys()) == 1:
308+
key = tuple(data[1].keys())[0]
309+
new_key = key.replace("PROF", prof)
310+
if key != new_key:
311+
data[1][new_key] = data[1][key]
312+
del data[1][key]
313+
ctor_arg = copy.deepcopy(SCENARIO_CTOR_ARG_MINIMAL)
314+
ctor_arg[f"{prof}_profile"] = data
315+
316+
# act
317+
with pytest.raises(RuntimeError) as excinfo:
318+
_ = ppmc.Scenario(gas_data, aero_data, ctor_arg)
319+
320+
# assert
321+
assert str(excinfo.value) == msg.replace("PROF", f"{prof}")

0 commit comments

Comments
 (0)