Skip to content

Commit a35f543

Browse files
authored
raise exception for non-unique mass_frac species in AeroMode and AeroDist ctors. closes #240 (#321)
1 parent 87839e8 commit a35f543

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

src/aero_dist.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ struct AeroDist {
5151
{
5252
if (!InputGimmick::unique_keys(json))
5353
throw std::runtime_error("Mode names must be unique");
54+
for (const auto &mode : json)
55+
AeroMode::check_mode_json(mode.begin().value());
5456

5557
GimmickGuard<InputGimmick> guard(json, "", "mode_name", 1);
5658
f_aero_dist_from_json(ptr.f_arg_non_const(), aero_data->ptr.f_arg_non_const());

src/aero_mode.hpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,25 @@ struct AeroMode {
128128
AeroMode(AeroData &aero_data, const nlohmann::json &json) :
129129
ptr(f_aero_mode_ctor, f_aero_mode_dtor)
130130
{
131-
if (json.size() != 1)
132-
throw std::runtime_error("Single element expected");
133-
131+
if (json.size() != 1 || !json.is_object() || !json.begin().value().is_object())
132+
throw std::runtime_error("Single-element dict expected with mode name as key and mode params dict as value");
133+
check_mode_json(json.begin().value());
134134
GimmickGuard<InputGimmick> guard(json, "", "mode_name");
135135
f_aero_mode_from_json(ptr.f_arg_non_const(), aero_data.ptr.f_arg_non_const());
136136
}
137137

138+
static void check_mode_json(const nlohmann::json &mode) {
139+
for (auto key : std::set<std::string>({"mass_frac", "mode_type"})) // TODO #320: more...
140+
if (mode.find(key) == mode.end())
141+
throw std::runtime_error("mode parameters dict must include key '" + key + "'");
142+
auto mass_frac = mode["mass_frac"];
143+
144+
if (!mass_frac.is_array()) // TODO #320: check if all are single-element dicts
145+
throw std::runtime_error("mass_frac value must be a list of single-element dicts");
146+
if (!InputGimmick::unique_keys(mass_frac))
147+
throw std::runtime_error("mass_frac keys must be unique");
148+
}
149+
138150
static auto get_num_conc(const AeroMode &self){
139151
double val;
140152
f_aero_mode_get_num_conc(self.ptr.f_arg(), &val);

tests/test_aero_dist.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,19 @@ def test_ctor_multimode_error_on_repeated_mode_names():
163163

164164
# assert
165165
assert str(exc_info.value) == "Mode names must be unique"
166+
167+
@staticmethod
168+
def test_ctor_error_on_repeated_massfrac_keys():
169+
# arrange
170+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
171+
fishy_ctor_arg = copy.deepcopy(AERO_DIST_CTOR_ARG_MINIMAL)
172+
fishy_ctor_arg[0]["test_mode"]["mass_frac"].append(
173+
fishy_ctor_arg[0]["test_mode"]["mass_frac"][0]
174+
)
175+
176+
# act
177+
with pytest.raises(Exception) as exc_info:
178+
ppmc.AeroDist(aero_data, fishy_ctor_arg)
179+
180+
# assert
181+
assert str(exc_info.value) == "mass_frac keys must be unique"

tests/test_aero_mode.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# Authors: https://github.com/open-atmos/PyPartMC/graphs/contributors #
55
####################################################################################################
66

7+
import copy
8+
79
import numpy as np
810
import pytest
911

@@ -242,30 +244,43 @@ def test_set_name():
242244
def test_ctor_fails_with_multiple_modes():
243245
# arrange
244246
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
245-
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
247+
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
246248
fishy_ctor_arg["xxx"] = fishy_ctor_arg["test_mode"]
247249

248250
# act
249251
with pytest.raises(Exception) as exc_info:
250252
ppmc.AeroMode(aero_data, fishy_ctor_arg)
251253

252254
# assert
253-
assert str(exc_info.value) == "Single element expected"
255+
assert (
256+
str(exc_info.value)
257+
== "Single-element dict expected with mode name as key and mode params dict as value"
258+
)
254259

255260
@staticmethod
256261
def test_ctor_fails_with_nonunique_mass_fracs():
257-
pytest.skip("TODO #240")
258-
259262
# arrange
260263
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
261-
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
264+
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
262265
fishy_ctor_arg["test_mode"]["mass_frac"].append(
263-
fishy_ctor_arg["test_mode"]["mass_frac"]
266+
AERO_MODE_CTOR_LOG_NORMAL["test_mode"]["mass_frac"][0]
264267
)
265268

266269
# act
267270
with pytest.raises(Exception) as exc_info:
268271
ppmc.AeroMode(aero_data, fishy_ctor_arg)
269272

270273
# assert
271-
assert str(exc_info.value) == ""
274+
assert str(exc_info.value) == "mass_frac keys must be unique"
275+
276+
@staticmethod
277+
def test_segfault_case(): # TODO #319
278+
pytest.skip()
279+
280+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
281+
fishy_ctor_arg = copy.deepcopy(AERO_MODE_CTOR_LOG_NORMAL)
282+
fishy_ctor_arg["test_mode"]["mass_frac"].append(
283+
fishy_ctor_arg["test_mode"]["mass_frac"]
284+
)
285+
print(fishy_ctor_arg)
286+
ppmc.AeroMode(aero_data, fishy_ctor_arg)

0 commit comments

Comments
 (0)