Skip to content

Commit 0ce2c18

Browse files
authored
add checks for uniqeness of mode and species names (#246)
1 parent 99c7046 commit 0ce2c18

File tree

7 files changed

+76
-7
lines changed

7 files changed

+76
-7
lines changed

src/aero_data.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct AeroData {
3232
AeroData(const nlohmann::json &json) :
3333
ptr(f_aero_data_ctor, f_aero_data_dtor)
3434
{
35+
if (!InputGimmick::unique_keys(json))
36+
throw std::runtime_error("Species names must be unique");
3537
gimmick_ptr() = std::make_unique<InputGimmick>(json);
3638
f_aero_data_from_json(this->ptr.f_arg());
3739
gimmick_ptr().reset(); // TODO #117: guard

src/aero_dist.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct AeroDist {
4949
ptr(f_aero_dist_ctor, f_aero_dist_dtor),
5050
aero_data(aero_data)
5151
{
52+
if (!InputGimmick::unique_keys(json))
53+
throw std::runtime_error("Mode names must be unique");
5254
gimmick_ptr() = std::make_unique<InputGimmick>(json, "", "mode_name", 1);
5355
f_aero_dist_from_json(ptr.f_arg_non_const(), aero_data->ptr.f_arg_non_const());
5456
gimmick_ptr().reset();

src/aero_mode.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ 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");
131133
gimmick_ptr() = std::make_unique<InputGimmick>(json, "", "mode_name");
132134
f_aero_mode_from_json(ptr.f_arg_non_const(), aero_data.ptr.f_arg_non_const());
133135
gimmick_ptr().reset();

src/gimmicks.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,18 @@ struct InputGimmick: Gimmick {
272272
}
273273
return 1;
274274
}
275+
276+
static bool unique_keys(const nlohmann::json &json) {
277+
std::set<std::string> keys;
278+
for (auto i=0u; i<json.size(); ++i) {
279+
for (auto &entry : json.at(i).items()) {
280+
if (keys.find(entry.key()) != keys.end())
281+
return false;
282+
keys.insert(entry.key());
283+
}
284+
}
285+
return true;
286+
}
275287
};
276288

277289
struct OutputGimmick: Gimmick {

tests/test_aero_data.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,3 +353,12 @@ def test_aero_data_density():
353353

354354
# assert
355355
assert density == val[0]
356+
357+
@staticmethod
358+
def test_ctor_error_on_nonunique_keys():
359+
# act
360+
with pytest.raises(Exception) as exc_info:
361+
ppmc.AeroData([AERO_DATA_CTOR_ARG_MINIMAL[0]] * 2)
362+
363+
# assert
364+
assert str(exc_info.value) == "Species names must be unique"

tests/test_aero_dist.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
]
3333

3434

35-
@pytest.fixture
36-
def sut_minimal():
35+
@pytest.fixture(name="sut_minimal")
36+
def sut_minimal_fixture():
3737
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
3838
sut = ppmc.AeroDist(aero_data, AERO_DIST_CTOR_ARG_MINIMAL)
3939
aero_data = None
@@ -113,9 +113,7 @@ def test_ctor_modes_in_order(n_modes=4):
113113

114114
@staticmethod
115115
@pytest.mark.parametrize("idx", (-1, 500))
116-
def test_get_mode_out_of_range(
117-
sut_minimal, idx
118-
): # pylint: disable=redefined-outer-name
116+
def test_get_mode_out_of_range(sut_minimal, idx):
119117
# act
120118
try:
121119
_ = sut_minimal.mode(idx)
@@ -128,7 +126,7 @@ def test_get_mode_out_of_range(
128126
@staticmethod
129127
def test_get_mode_result_lifetime(
130128
sut_minimal,
131-
): # pylint: disable=redefined-outer-name
129+
):
132130
# arrange
133131
mode = sut_minimal.mode(0)
134132
mode_type = mode.type
@@ -141,7 +139,7 @@ def test_get_mode_result_lifetime(
141139
assert mode.type == mode_type
142140

143141
@staticmethod
144-
def test_get_mode_is_a_copy(sut_minimal): # pylint: disable=redefined-outer-name
142+
def test_get_mode_is_a_copy(sut_minimal):
145143
# arrange
146144
new_type = "mono"
147145
mode_idx = 0
@@ -153,3 +151,15 @@ def test_get_mode_is_a_copy(sut_minimal): # pylint: disable=redefined-outer-nam
153151

154152
# assert
155153
assert sut_minimal.mode(mode_idx).type != new_type
154+
155+
@staticmethod
156+
def test_ctor_multimode_error_on_repeated_mode_names():
157+
# arrange
158+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
159+
160+
# act
161+
with pytest.raises(Exception) as exc_info:
162+
ppmc.AeroDist(aero_data, AERO_DIST_CTOR_ARG_MINIMAL * 2)
163+
164+
# assert
165+
assert str(exc_info.value) == "Mode names must be unique"

tests/test_aero_mode.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,35 @@ def test_set_name():
237237

238238
# assert
239239
assert sut.name == val
240+
241+
@staticmethod
242+
def test_ctor_fails_with_multiple_modes():
243+
# arrange
244+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
245+
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
246+
fishy_ctor_arg["xxx"] = fishy_ctor_arg["test_mode"]
247+
248+
# act
249+
with pytest.raises(Exception) as exc_info:
250+
ppmc.AeroMode(aero_data, fishy_ctor_arg)
251+
252+
# assert
253+
assert str(exc_info.value) == "Single element expected"
254+
255+
@staticmethod
256+
def test_ctor_fails_with_nonunique_mass_fracs():
257+
pytest.skip("TODO #240")
258+
259+
# arrange
260+
aero_data = ppmc.AeroData(AERO_DATA_CTOR_ARG_MINIMAL)
261+
fishy_ctor_arg = AERO_MODE_CTOR_LOG_NORMAL
262+
fishy_ctor_arg["test_mode"]["mass_frac"].append(
263+
fishy_ctor_arg["test_mode"]["mass_frac"]
264+
)
265+
266+
# act
267+
with pytest.raises(Exception) as exc_info:
268+
ppmc.AeroMode(aero_data, fishy_ctor_arg)
269+
270+
# assert
271+
assert str(exc_info.value) == ""

0 commit comments

Comments
 (0)