Skip to content

Commit 42c3aeb

Browse files
authored
AddonInfo: const-ified loading and improved errorhandling (#5834)
1 parent c7cd091 commit 42c3aeb

File tree

2 files changed

+113
-51
lines changed

2 files changed

+113
-51
lines changed

lib/addoninfo.cpp

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -52,47 +52,73 @@ static std::string parseAddonInfo(AddonInfo& addoninfo, const picojson::value &j
5252
return "Loading " + fileName + " failed. " + json_error;
5353
}
5454
if (!json.is<picojson::object>())
55-
return "Loading " + fileName + " failed. Bad json.";
56-
picojson::object obj = json.get<picojson::object>();
57-
if (obj.count("args")) {
58-
if (!obj["args"].is<picojson::array>())
59-
return "Loading " + fileName + " failed. args must be array.";
60-
for (const picojson::value &v : obj["args"].get<picojson::array>())
61-
addoninfo.args += " " + v.get<std::string>();
62-
}
55+
return "Loading " + fileName + " failed. JSON is not an object.";
6356

64-
if (obj.count("ctu")) {
65-
// ctu is specified in the config file
66-
if (!obj["ctu"].is<bool>())
67-
return "Loading " + fileName + " failed. ctu must be boolean.";
68-
addoninfo.ctu = obj["ctu"].get<bool>();
69-
} else {
70-
addoninfo.ctu = false;
57+
// TODO: remove/complete default value handling for missing fields
58+
const picojson::object& obj = json.get<picojson::object>();
59+
{
60+
const auto it = obj.find("args");
61+
if (it != obj.cend()) {
62+
const auto& val = it->second;
63+
if (!val.is<picojson::array>())
64+
return "Loading " + fileName + " failed. 'args' must be an array.";
65+
for (const picojson::value &v : val.get<picojson::array>()) {
66+
if (!v.is<std::string>())
67+
return "Loading " + fileName + " failed. 'args' entry is not a string.";
68+
addoninfo.args += " " + v.get<std::string>();
69+
}
70+
}
7171
}
7272

73-
if (obj.count("python")) {
74-
// Python was defined in the config file
75-
if (obj["python"].is<picojson::array>()) {
76-
return "Loading " + fileName +" failed. python must not be an array.";
73+
{
74+
const auto it = obj.find("ctu");
75+
if (it != obj.cend()) {
76+
const auto& val = it->second;
77+
// ctu is specified in the config file
78+
if (!val.is<bool>())
79+
return "Loading " + fileName + " failed. 'ctu' must be a boolean.";
80+
addoninfo.ctu = val.get<bool>();
81+
}
82+
else {
83+
addoninfo.ctu = false;
7784
}
78-
addoninfo.python = obj["python"].get<std::string>();
79-
} else {
80-
addoninfo.python = "";
8185
}
8286

83-
if (obj.count("executable")) {
84-
if (!obj["executable"].is<std::string>())
85-
return "Loading " + fileName + " failed. executable must be a string.";
86-
addoninfo.executable = getFullPath(obj["executable"].get<std::string>(), fileName);
87-
return "";
87+
{
88+
const auto it = obj.find("python");
89+
if (it != obj.cend()) {
90+
const auto& val = it->second;
91+
// Python was defined in the config file
92+
if (!val.is<std::string>()) {
93+
return "Loading " + fileName +" failed. 'python' must be a string.";
94+
}
95+
addoninfo.python = val.get<std::string>();
96+
}
97+
else {
98+
addoninfo.python = "";
99+
}
88100
}
89101

90-
if (!obj.count("script") || !obj["script"].is<std::string>())
91102
{
92-
return "Loading " + fileName + " failed. script must be set to a string value.";
103+
const auto it = obj.find("executable");
104+
if (it != obj.cend()) {
105+
const auto& val = it->second;
106+
if (!val.is<std::string>())
107+
return "Loading " + fileName + " failed. 'executable' must be a string.";
108+
addoninfo.executable = getFullPath(val.get<std::string>(), fileName);
109+
return ""; // TODO: why bail out?
110+
}
93111
}
94112

95-
return addoninfo.getAddonInfo(obj["script"].get<std::string>(), exename);
113+
const auto it = obj.find("script");
114+
if (it == obj.cend())
115+
return "Loading " + fileName + " failed. 'script' is missing.";
116+
117+
const auto& val = it->second;
118+
if (!val.is<std::string>())
119+
return "Loading " + fileName + " failed. 'script' must be a string.";
120+
121+
return addoninfo.getAddonInfo(val.get<std::string>(), exename);
96122
}
97123

98124
std::string AddonInfo::getAddonInfo(const std::string &fileName, const std::string &exename) {

test/cli/test-other.py

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import sys
66
import pytest
7+
import json
78

89
from testutils import cppcheck, assert_cppcheck
910

@@ -278,22 +279,6 @@ def test_addon_threadsafety(tmpdir):
278279
assert stderr == '{}:4:12: warning: strerror is MT-unsafe [threadsafety-unsafe-call]\n'.format(test_file)
279280

280281

281-
def test_addon_invalidjson(tmpdir):
282-
addon_file = os.path.join(tmpdir, 'invalid.json')
283-
with open(addon_file, 'wt') as f:
284-
f.write("""
285-
{
286-
"Script": "addons/something.py"
287-
}
288-
""")
289-
290-
args = ['--addon={}'.format(addon_file), '--enable=all', 'nonexistent.cpp']
291-
292-
exitcode, stdout, stderr = cppcheck(args)
293-
assert exitcode != 0
294-
assert stdout == 'Loading {} failed. script must be set to a string value.\n'.format(addon_file)
295-
296-
297282
def test_addon_naming(tmpdir):
298283
# the addon does nothing without a config
299284
addon_file = os.path.join(tmpdir, 'naming1.json')
@@ -644,12 +629,10 @@ def test_invalid_addon_json(tmpdir):
644629
""")
645630

646631
test_file = os.path.join(tmpdir, 'file.cpp')
647-
with open(test_file, 'wt') as f:
648-
f.write("""
649-
typedef int MISRA_5_6_VIOLATION;
650-
""")
632+
with open(test_file, 'wt'):
633+
pass
651634

652-
args = ['--addon={}'.format(addon_file), '--enable=all', test_file]
635+
args = ['--addon={}'.format(addon_file), test_file]
653636

654637
exitcode, stdout, stderr = cppcheck(args)
655638
assert exitcode == 1
@@ -1124,3 +1107,56 @@ def test_build_dir_j_memleak(tmpdir): #12111
11241107
]
11251108

11261109
assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines)
1110+
1111+
1112+
def __test_addon_json_invalid(tmpdir, addon_json, expected):
1113+
addon_file = os.path.join(tmpdir, 'invalid.json')
1114+
with open(addon_file, 'wt') as f:
1115+
f.write(addon_json)
1116+
1117+
test_file = os.path.join(tmpdir, 'file.cpp')
1118+
with open(test_file, 'wt'):
1119+
pass
1120+
1121+
args = ['--addon={}'.format(addon_file), test_file]
1122+
1123+
exitcode, stdout, stderr = cppcheck(args)
1124+
assert exitcode == 1
1125+
lines = stdout.splitlines()
1126+
assert len(lines) == 1
1127+
assert lines == [
1128+
'Loading {} failed. {}'.format(addon_file, expected)
1129+
]
1130+
assert stderr == ''
1131+
1132+
1133+
def test_addon_json_invalid_no_obj(tmpdir):
1134+
__test_addon_json_invalid(tmpdir, json.dumps([]), "JSON is not an object.")
1135+
1136+
1137+
def test_addon_json_invalid_args_1(tmpdir):
1138+
__test_addon_json_invalid(tmpdir, json.dumps({'args':0}), "'args' must be an array.")
1139+
1140+
1141+
def test_addon_json_invalid_args_2(tmpdir):
1142+
__test_addon_json_invalid(tmpdir, json.dumps({'args':[0]}), "'args' entry is not a string.")
1143+
1144+
1145+
def test_addon_json_invalid_ctu(tmpdir):
1146+
__test_addon_json_invalid(tmpdir, json.dumps({'ctu':0}), "'ctu' must be a boolean.")
1147+
1148+
1149+
def test_addon_json_invalid_python(tmpdir):
1150+
__test_addon_json_invalid(tmpdir, json.dumps({'python':0}), "'python' must be a string.")
1151+
1152+
1153+
def test_addon_json_invalid_executable(tmpdir):
1154+
__test_addon_json_invalid(tmpdir, json.dumps({'executable':0}), "'executable' must be a string.")
1155+
1156+
1157+
def test_addon_json_invalid_script_1(tmpdir):
1158+
__test_addon_json_invalid(tmpdir, json.dumps({'Script':''}), "'script' is missing.")
1159+
1160+
1161+
def test_addon_json_invalid_script_2(tmpdir):
1162+
__test_addon_json_invalid(tmpdir, json.dumps({'script':0}), "'script' must be a string.")

0 commit comments

Comments
 (0)