4 This tool is for checking the security hardening options of the Linux kernel.
6 Author: Alexander Popov <alex.popov@linux.com>
8 This module performs unit-testing of the kernel-hardening-checker engine.
11 # pylint: disable=missing-function-docstring,line-too-long
18 from typing import Union, Optional, List, Dict, Tuple
19 from .engine import StrOrBool, ChecklistObjType, KconfigCheck, CmdlineCheck, SysctlCheck, VersionCheck, OR, AND
20 from .engine import populate_with_data, perform_checks, override_expected_value
23 ResultType = List[Union[Dict[str, StrOrBool], str]]
26 class TestEngine(unittest.TestCase):
28 Example test scenario:
30 # 1. prepare the checklist
31 config_checklist = [] # type: List[ChecklistObjType]
32 config_checklist += [KconfigCheck('reason_1', 'decision_1', 'KCONFIG_NAME', 'expected_1')]
33 config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'cmdline_name', 'expected_2')]
34 config_checklist += [SysctlCheck('reason_3', 'decision_3', 'sysctl_name', 'expected_3')]
36 # 2. prepare the parsed kconfig options
37 parsed_kconfig_options = {}
38 parsed_kconfig_options['CONFIG_KCONFIG_NAME'] = 'UNexpected_1'
40 # 3. prepare the parsed cmdline options
41 parsed_cmdline_options = {}
42 parsed_cmdline_options['cmdline_name'] = 'expected_2'
44 # 4. prepare the parsed sysctl options
45 parsed_sysctl_options = {}
46 parsed_sysctl_options['sysctl_name'] = 'expected_3'
48 # 5. prepare the kernel version
49 kernel_version = (42, 43, 44)
52 self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, kernel_version)
54 # 7. check that the results are correct
55 result = [] # type: ResultType
56 self.get_engine_result(config_checklist, result, 'json')
63 def run_engine(checklist: List[ChecklistObjType],
64 parsed_kconfig_options: Optional[Dict[str, str]],
65 parsed_cmdline_options: Optional[Dict[str, str]],
66 parsed_sysctl_options: Optional[Dict[str, str]],
67 kernel_version: Optional[Tuple[int, int, int]]) -> None:
68 # populate the checklist with data
69 if parsed_kconfig_options:
70 populate_with_data(checklist, parsed_kconfig_options, 'kconfig')
71 if parsed_cmdline_options:
72 populate_with_data(checklist, parsed_cmdline_options, 'cmdline')
73 if parsed_sysctl_options:
74 populate_with_data(checklist, parsed_sysctl_options, 'sysctl')
76 populate_with_data(checklist, kernel_version, 'version')
78 # now everything is ready, perform the checks
79 perform_checks(checklist)
81 # print the table with the results
82 print(f'\n{inspect.stack()[1].function}():')
85 opt.table_print('verbose', True) # verbose mode, with_results
89 # print the results in JSON
92 result.append(opt.json_dump(True)) # with_results
93 print(json.dumps(result))
97 def get_engine_result(checklist: List[ChecklistObjType], result: ResultType, result_type: str) -> None:
98 assert(result_type in ('json', 'stdout', 'stdout_verbose')), \
99 f'invalid result type "{result_type}"'
101 if result_type == 'json':
102 for opt in checklist:
103 result.append(opt.json_dump(True)) # with_results
106 captured_output = io.StringIO()
107 stdout_backup = sys.stdout
108 sys.stdout = captured_output
109 for opt in checklist:
110 if result_type == 'stdout_verbose':
111 opt.table_print('verbose', True) # verbose mode, with_results
113 opt.table_print(None, True) # normal mode, with_results
114 sys.stdout = stdout_backup
115 result.append(captured_output.getvalue())
117 def test_simple_kconfig(self) -> None:
118 # 1. prepare the checklist
119 config_checklist = [] # type: List[ChecklistObjType]
120 config_checklist += [KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1')]
121 config_checklist += [KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2')]
122 config_checklist += [KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3')]
123 config_checklist += [KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'is not set')]
124 config_checklist += [KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'is present')]
125 config_checklist += [KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'is present')]
126 config_checklist += [KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'is not off')]
127 config_checklist += [KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is not off')]
128 config_checklist += [KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'is not off')]
129 config_checklist += [KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is not off')]
131 # 2. prepare the parsed kconfig options
132 parsed_kconfig_options = {}
133 parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
134 parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
135 parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
136 parsed_kconfig_options['CONFIG_NAME_7'] = 'really_not_off'
137 parsed_kconfig_options['CONFIG_NAME_8'] = 'off'
138 parsed_kconfig_options['CONFIG_NAME_9'] = '0'
141 self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
143 # 4. check that the results are correct
144 result = [] # type: ResultType
145 self.get_engine_result(config_checklist, result, 'json')
148 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
149 {'option_name': 'CONFIG_NAME_2', 'type': 'kconfig', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: "UNexpected_2"', 'check_result_bool': False},
150 {'option_name': 'CONFIG_NAME_3', 'type': 'kconfig', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: is not found', 'check_result_bool': False},
151 {'option_name': 'CONFIG_NAME_4', 'type': 'kconfig', 'desired_val': 'is not set', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'OK: is not found', 'check_result_bool': True},
152 {'option_name': 'CONFIG_NAME_5', 'type': 'kconfig', 'desired_val': 'is present', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'OK: is present', 'check_result_bool': True},
153 {'option_name': 'CONFIG_NAME_6', 'type': 'kconfig', 'desired_val': 'is present', 'decision': 'decision_6', 'reason': 'reason_6', 'check_result': 'FAIL: is not present', 'check_result_bool': False},
154 {'option_name': 'CONFIG_NAME_7', 'type': 'kconfig', 'desired_val': 'is not off', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'OK: is not off, "really_not_off"', 'check_result_bool': True},
155 {'option_name': 'CONFIG_NAME_8', 'type': 'kconfig', 'desired_val': 'is not off', 'decision': 'decision_8', 'reason': 'reason_8', 'check_result': 'FAIL: is off', 'check_result_bool': False},
156 {'option_name': 'CONFIG_NAME_9', 'type': 'kconfig', 'desired_val': 'is not off', 'decision': 'decision_9', 'reason': 'reason_9', 'check_result': 'FAIL: is off, "0"', 'check_result_bool': False},
157 {'option_name': 'CONFIG_NAME_10', 'type': 'kconfig', 'desired_val': 'is not off', 'decision': 'decision_10', 'reason': 'reason_10', 'check_result': 'FAIL: is off, not found', 'check_result_bool': False}]
160 def test_simple_cmdline(self) -> None:
161 # 1. prepare the checklist
162 config_checklist = [] # type: List[ChecklistObjType]
163 config_checklist += [CmdlineCheck('reason_1', 'decision_1', 'name_1', 'expected_1')]
164 config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
165 config_checklist += [CmdlineCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
166 config_checklist += [CmdlineCheck('reason_4', 'decision_4', 'name_4', 'is not set')]
167 config_checklist += [CmdlineCheck('reason_5', 'decision_5', 'name_5', 'is present')]
168 config_checklist += [CmdlineCheck('reason_6', 'decision_6', 'name_6', 'is present')]
169 config_checklist += [CmdlineCheck('reason_7', 'decision_7', 'name_7', 'is not off')]
170 config_checklist += [CmdlineCheck('reason_8', 'decision_8', 'name_8', 'is not off')]
171 config_checklist += [CmdlineCheck('reason_9', 'decision_9', 'name_9', 'is not off')]
172 config_checklist += [CmdlineCheck('reason_10', 'decision_10', 'name_10', 'is not off')]
174 # 2. prepare the parsed cmdline options
175 parsed_cmdline_options = {}
176 parsed_cmdline_options['name_1'] = 'expected_1'
177 parsed_cmdline_options['name_2'] = 'UNexpected_2'
178 parsed_cmdline_options['name_5'] = ''
179 parsed_cmdline_options['name_7'] = ''
180 parsed_cmdline_options['name_8'] = 'off'
181 parsed_cmdline_options['name_9'] = '0'
184 self.run_engine(config_checklist, None, parsed_cmdline_options, None, None)
186 # 4. check that the results are correct
187 result = [] # type: ResultType
188 self.get_engine_result(config_checklist, result, 'json')
191 [{'option_name': 'name_1', 'type': 'cmdline', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
192 {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: "UNexpected_2"', 'check_result_bool': False},
193 {'option_name': 'name_3', 'type': 'cmdline', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: is not found', 'check_result_bool': False},
194 {'option_name': 'name_4', 'type': 'cmdline', 'desired_val': 'is not set', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'OK: is not found', 'check_result_bool': True},
195 {'option_name': 'name_5', 'type': 'cmdline', 'desired_val': 'is present', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'OK: is present', 'check_result_bool': True},
196 {'option_name': 'name_6', 'type': 'cmdline', 'desired_val': 'is present', 'decision': 'decision_6', 'reason': 'reason_6', 'check_result': 'FAIL: is not present', 'check_result_bool': False},
197 {'option_name': 'name_7', 'type': 'cmdline', 'desired_val': 'is not off', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'OK: is not off, ""', 'check_result_bool': True},
198 {'option_name': 'name_8', 'type': 'cmdline', 'desired_val': 'is not off', 'decision': 'decision_8', 'reason': 'reason_8', 'check_result': 'FAIL: is off', 'check_result_bool': False},
199 {'option_name': 'name_9', 'type': 'cmdline', 'desired_val': 'is not off', 'decision': 'decision_9', 'reason': 'reason_9', 'check_result': 'FAIL: is off, "0"', 'check_result_bool': False},
200 {'option_name': 'name_10', 'type': 'cmdline', 'desired_val': 'is not off', 'decision': 'decision_10', 'reason': 'reason_10', 'check_result': 'FAIL: is off, not found', 'check_result_bool': False}]
203 def test_simple_sysctl(self) -> None:
204 # 1. prepare the checklist
205 config_checklist = [] # type: List[ChecklistObjType]
206 config_checklist += [SysctlCheck('reason_1', 'decision_1', 'name_1', 'expected_1')]
207 config_checklist += [SysctlCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
208 config_checklist += [SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
209 config_checklist += [SysctlCheck('reason_4', 'decision_4', 'name_4', 'is not set')]
210 config_checklist += [SysctlCheck('reason_5', 'decision_5', 'name_5', 'is present')]
211 config_checklist += [SysctlCheck('reason_6', 'decision_6', 'name_6', 'is present')]
212 config_checklist += [SysctlCheck('reason_7', 'decision_7', 'name_7', 'is not off')]
213 config_checklist += [SysctlCheck('reason_8', 'decision_8', 'name_8', 'is not off')]
214 config_checklist += [SysctlCheck('reason_9', 'decision_9', 'name_9', 'is not off')]
215 config_checklist += [SysctlCheck('reason_10', 'decision_10', 'name_10', 'is not off')]
217 # 2. prepare the parsed sysctl options
218 parsed_sysctl_options = {}
219 parsed_sysctl_options['name_1'] = 'expected_1'
220 parsed_sysctl_options['name_2'] = 'UNexpected_2'
221 parsed_sysctl_options['name_5'] = ''
222 parsed_sysctl_options['name_7'] = ''
223 parsed_sysctl_options['name_8'] = 'off'
224 parsed_sysctl_options['name_9'] = '0'
227 self.run_engine(config_checklist, None, None, parsed_sysctl_options, None)
229 # 4. check that the results are correct
230 result = [] # type: ResultType
231 self.get_engine_result(config_checklist, result, 'json')
234 [{'option_name': 'name_1', 'type': 'sysctl', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
235 {'option_name': 'name_2', 'type': 'sysctl', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: "UNexpected_2"', 'check_result_bool': False},
236 {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: is not found', 'check_result_bool': False},
237 {'option_name': 'name_4', 'type': 'sysctl', 'desired_val': 'is not set', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'OK: is not found', 'check_result_bool': True},
238 {'option_name': 'name_5', 'type': 'sysctl', 'desired_val': 'is present', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'OK: is present', 'check_result_bool': True},
239 {'option_name': 'name_6', 'type': 'sysctl', 'desired_val': 'is present', 'decision': 'decision_6', 'reason': 'reason_6', 'check_result': 'FAIL: is not present', 'check_result_bool': False},
240 {'option_name': 'name_7', 'type': 'sysctl', 'desired_val': 'is not off', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'OK: is not off, ""', 'check_result_bool': True},
241 {'option_name': 'name_8', 'type': 'sysctl', 'desired_val': 'is not off', 'decision': 'decision_8', 'reason': 'reason_8', 'check_result': 'FAIL: is off', 'check_result_bool': False},
242 {'option_name': 'name_9', 'type': 'sysctl', 'desired_val': 'is not off', 'decision': 'decision_9', 'reason': 'reason_9', 'check_result': 'FAIL: is off, "0"', 'check_result_bool': False},
243 {'option_name': 'name_10', 'type': 'sysctl', 'desired_val': 'is not off', 'decision': 'decision_10', 'reason': 'reason_10', 'check_result': 'FAIL: is off, not found', 'check_result_bool': False}]
246 def test_complex_or(self) -> None:
247 # 1. prepare the checklist
248 config_checklist = [] # type: List[ChecklistObjType]
249 config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
250 KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'))]
251 config_checklist += [OR(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
252 KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'))]
253 config_checklist += [OR(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
254 KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'))]
255 config_checklist += [OR(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
256 KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is not set'))]
257 config_checklist += [OR(KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9'),
258 KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is present'))]
259 config_checklist += [OR(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
260 KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'is not off'))]
262 # 2. prepare the parsed kconfig options
263 parsed_kconfig_options = {}
264 parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
265 parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
266 parsed_kconfig_options['CONFIG_NAME_3'] = 'UNexpected_3'
267 parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
268 parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
269 parsed_kconfig_options['CONFIG_NAME_6'] = 'UNexpected_6'
270 parsed_kconfig_options['CONFIG_NAME_10'] = 'UNexpected_10'
271 parsed_kconfig_options['CONFIG_NAME_12'] = 'really_not_off'
274 self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
276 # 4. check that the results are correct
277 result = [] # type: ResultType
278 self.get_engine_result(config_checklist, result, 'json')
281 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
282 {'option_name': 'CONFIG_NAME_3', 'type': 'kconfig', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'OK: CONFIG_NAME_4 is "expected_4"', 'check_result_bool': True},
283 {'option_name': 'CONFIG_NAME_5', 'type': 'kconfig', 'desired_val': 'expected_5', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'FAIL: "UNexpected_5"', 'check_result_bool': False},
284 {'option_name': 'CONFIG_NAME_7', 'type': 'kconfig', 'desired_val': 'expected_7', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'OK: CONFIG_NAME_8 is not found', 'check_result_bool': True},
285 {'option_name': 'CONFIG_NAME_9', 'type': 'kconfig', 'desired_val': 'expected_9', 'decision': 'decision_9', 'reason': 'reason_9', 'check_result': 'OK: CONFIG_NAME_10 is present', 'check_result_bool': True},
286 {'option_name': 'CONFIG_NAME_11', 'type': 'kconfig', 'desired_val': 'expected_11', 'decision': 'decision_11', 'reason': 'reason_11', 'check_result': 'OK: CONFIG_NAME_12 is not off', 'check_result_bool': True}]
289 def test_complex_and(self) -> None:
290 # 1. prepare the checklist
291 config_checklist = [] # type: List[ChecklistObjType]
292 config_checklist += [AND(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
293 KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'))]
294 config_checklist += [AND(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
295 KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'))]
296 config_checklist += [AND(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
297 KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'))]
298 config_checklist += [AND(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
299 KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is present'))]
300 config_checklist += [AND(KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9'),
301 KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is not off'))]
302 config_checklist += [AND(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
303 KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'is not off'))]
305 # 2. prepare the parsed kconfig options
306 parsed_kconfig_options = {}
307 parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
308 parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
309 parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
310 parsed_kconfig_options['CONFIG_NAME_4'] = 'UNexpected_4'
311 parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
312 parsed_kconfig_options['CONFIG_NAME_6'] = 'expected_6'
313 parsed_kconfig_options['CONFIG_NAME_7'] = 'expected_7'
314 parsed_kconfig_options['CONFIG_NAME_9'] = 'expected_9'
315 parsed_kconfig_options['CONFIG_NAME_10'] = '0'
316 parsed_kconfig_options['CONFIG_NAME_11'] = 'expected_11'
319 self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
321 # 4. check that the results are correct
322 result = [] # type: ResultType
323 self.get_engine_result(config_checklist, result, 'json')
326 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
327 {'option_name': 'CONFIG_NAME_3', 'type': 'kconfig', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: CONFIG_NAME_4 is not "expected_4"', 'check_result_bool': False},
328 {'option_name': 'CONFIG_NAME_5', 'type': 'kconfig', 'desired_val': 'expected_5', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'FAIL: "UNexpected_5"', 'check_result_bool': False},
329 {'option_name': 'CONFIG_NAME_7', 'type': 'kconfig', 'desired_val': 'expected_7', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'FAIL: CONFIG_NAME_8 is not present', 'check_result_bool': False},
330 {'option_name': 'CONFIG_NAME_9', 'type': 'kconfig', 'desired_val': 'expected_9', 'decision': 'decision_9', 'reason': 'reason_9', 'check_result': 'FAIL: CONFIG_NAME_10 is off', 'check_result_bool': False},
331 {'option_name': 'CONFIG_NAME_11', 'type': 'kconfig', 'desired_val': 'expected_11', 'decision': 'decision_11', 'reason': 'reason_11', 'check_result': 'FAIL: CONFIG_NAME_12 is off, not found', 'check_result_bool': False}]
334 def test_complex_nested(self) -> None:
335 # 1. prepare the checklist
336 config_checklist = [] # type: List[ChecklistObjType]
337 config_checklist += [AND(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
338 OR(KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'),
339 KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3')))]
340 config_checklist += [AND(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
341 OR(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
342 KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6')))]
343 config_checklist += [OR(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
344 AND(KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'expected_8'),
345 KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9')))]
346 config_checklist += [OR(KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'expected_10'),
347 AND(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
348 KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'expected_12')))]
350 # 2. prepare the parsed kconfig options
351 parsed_kconfig_options = {}
352 parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
353 parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
354 parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
355 parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
356 parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
357 parsed_kconfig_options['CONFIG_NAME_6'] = 'UNexpected_6'
358 parsed_kconfig_options['CONFIG_NAME_7'] = 'UNexpected_7'
359 parsed_kconfig_options['CONFIG_NAME_8'] = 'expected_8'
360 parsed_kconfig_options['CONFIG_NAME_9'] = 'expected_9'
361 parsed_kconfig_options['CONFIG_NAME_10'] = 'UNexpected_10'
362 parsed_kconfig_options['CONFIG_NAME_11'] = 'UNexpected_11'
363 parsed_kconfig_options['CONFIG_NAME_12'] = 'expected_12'
366 self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
368 # 4. check that the results are correct
369 result = [] # type: ResultType
370 self.get_engine_result(config_checklist, result, 'json')
373 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
374 {'option_name': 'CONFIG_NAME_4', 'type': 'kconfig', 'desired_val': 'expected_4', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'FAIL: CONFIG_NAME_5 is not "expected_5"', 'check_result_bool': False},
375 {'option_name': 'CONFIG_NAME_7', 'type': 'kconfig', 'desired_val': 'expected_7', 'decision': 'decision_7', 'reason': 'reason_7', 'check_result': 'OK: CONFIG_NAME_8 is "expected_8"', 'check_result_bool': True},
376 {'option_name': 'CONFIG_NAME_10', 'type': 'kconfig', 'desired_val': 'expected_10', 'decision': 'decision_10', 'reason': 'reason_10', 'check_result': 'FAIL: "UNexpected_10"', 'check_result_bool': False}]
379 def test_version(self) -> None:
380 # 1. prepare the checklist
381 config_checklist = [] # type: List[ChecklistObjType]
382 config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
383 VersionCheck((41, 101, 0)))]
384 config_checklist += [AND(KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'),
385 VersionCheck((43, 1, 0)))]
386 config_checklist += [OR(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
387 VersionCheck((42, 42, 101)))]
388 config_checklist += [AND(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
389 VersionCheck((42, 44, 1)))]
390 config_checklist += [OR(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
391 VersionCheck((42, 43, 44)))]
392 config_checklist += [AND(KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'),
393 VersionCheck((42, 43, 45)))]
395 # 2. prepare the parsed kconfig options
396 parsed_kconfig_options = {}
397 parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
398 parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
399 parsed_kconfig_options['CONFIG_NAME_6'] = 'expected_6'
401 # 3. prepare the kernel version
402 kernel_version = (42, 43, 44)
405 self.run_engine(config_checklist, parsed_kconfig_options, None, None, kernel_version)
407 # 5. check that the results are correct
408 result = [] # type: ResultType
409 self.get_engine_result(config_checklist, result, 'json')
412 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK: version >= (41, 101, 0)', 'check_result_bool': True},
413 {'option_name': 'CONFIG_NAME_2', 'type': 'kconfig', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: version < (43, 1, 0)', 'check_result_bool': False},
414 {'option_name': 'CONFIG_NAME_3', 'type': 'kconfig', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'OK: version >= (42, 42, 101)', 'check_result_bool': True},
415 {'option_name': 'CONFIG_NAME_4', 'type': 'kconfig', 'desired_val': 'expected_4', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'FAIL: version < (42, 44, 1)', 'check_result_bool': False},
416 {'option_name': 'CONFIG_NAME_5', 'type': 'kconfig', 'desired_val': 'expected_5', 'decision': 'decision_5', 'reason': 'reason_5', 'check_result': 'OK: version >= (42, 43, 44)', 'check_result_bool': True},
417 {'option_name': 'CONFIG_NAME_6', 'type': 'kconfig', 'desired_val': 'expected_6', 'decision': 'decision_6', 'reason': 'reason_6', 'check_result': 'FAIL: version < (42, 43, 45)', 'check_result_bool': False}]
420 def test_stdout(self) -> None:
421 # 1. prepare the checklist
422 config_checklist = [] # type: List[ChecklistObjType]
423 config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
424 CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2'),
425 SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3'))]
426 config_checklist += [AND(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
427 CmdlineCheck('reason_5', 'decision_5', 'name_5', 'expected_5'),
428 SysctlCheck('reason_6', 'decision_6', 'name_6', 'expected_6'))]
430 # 2. prepare the parsed kconfig options
431 parsed_kconfig_options = {}
432 parsed_kconfig_options['CONFIG_NAME_1'] = 'UNexpected_1'
434 # 3. prepare the parsed cmdline options
435 parsed_cmdline_options = {}
436 parsed_cmdline_options['name_2'] = 'expected_2'
437 parsed_cmdline_options['name_5'] = 'UNexpected_5'
439 # 4. prepare the parsed sysctl options
440 parsed_sysctl_options = {}
441 parsed_sysctl_options['name_6'] = 'expected_6'
444 self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, None)
446 # 6. check that the results are correct
447 json_result = [] # type: ResultType
448 self.get_engine_result(config_checklist, json_result, 'json')
451 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK: name_2 is "expected_2"', 'check_result_bool': True},
452 {'option_name': 'CONFIG_NAME_4', 'type': 'kconfig', 'desired_val': 'expected_4', 'decision': 'decision_4', 'reason': 'reason_4', 'check_result': 'FAIL: name_5 is not "expected_5"', 'check_result_bool': False}]
455 stdout_result = [] # type: ResultType
456 self.get_engine_result(config_checklist, stdout_result, 'stdout')
461 CONFIG_NAME_1 |kconfig| expected_1 |decision_1| reason_1 | OK: name_2 is "expected_2"\
462 CONFIG_NAME_4 |kconfig| expected_4 |decision_4| reason_4 | FAIL: name_5 is not "expected_5"\
467 self.get_engine_result(config_checklist, stdout_result, 'stdout_verbose')
472 <<< OR >>> | OK: name_2 is "expected_2"\n\
473 CONFIG_NAME_1 |kconfig| expected_1 |decision_1| reason_1 | FAIL: "UNexpected_1"\n\
474 name_2 |cmdline| expected_2 |decision_2| reason_2 | OK\n\
475 name_3 |sysctl | expected_3 |decision_3| reason_3 | None\
478 <<< AND >>> | FAIL: name_5 is not "expected_5"\n\
479 CONFIG_NAME_4 |kconfig| expected_4 |decision_4| reason_4 | None\n\
480 name_5 |cmdline| expected_5 |decision_5| reason_5 | FAIL: "UNexpected_5"\n\
481 name_6 |sysctl | expected_6 |decision_6| reason_6 | OK\
485 def test_value_overriding(self) -> None:
486 # 1. prepare the checklist
487 config_checklist = [] # type: List[ChecklistObjType]
488 config_checklist += [KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1')]
489 config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
490 config_checklist += [SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
492 # 2. prepare the parsed kconfig options
493 parsed_kconfig_options = {}
494 parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1_new'
496 # 3. prepare the parsed cmdline options
497 parsed_cmdline_options = {}
498 parsed_cmdline_options['name_2'] = 'expected_2_new'
500 # 4. prepare the parsed sysctl options
501 parsed_sysctl_options = {}
502 parsed_sysctl_options['name_3'] = 'expected_3_new'
505 self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, None)
507 # 6. check that the results are correct
508 result = [] # type: ResultType
509 self.get_engine_result(config_checklist, result, 'json')
512 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'FAIL: "expected_1_new"', 'check_result_bool': False},
513 {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: "expected_2_new"', 'check_result_bool': False},
514 {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: "expected_3_new"', 'check_result_bool': False}]
517 # 7. override expected value and perform the checks again
518 override_expected_value(config_checklist, 'CONFIG_NAME_1', 'expected_1_new')
519 perform_checks(config_checklist)
521 # 8. check that the results are correct
523 self.get_engine_result(config_checklist, result, 'json')
526 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1_new', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
527 {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'FAIL: "expected_2_new"', 'check_result_bool': False},
528 {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: "expected_3_new"', 'check_result_bool': False}]
531 # 9. override expected value and perform the checks again
532 override_expected_value(config_checklist, 'name_2', 'expected_2_new')
533 perform_checks(config_checklist)
535 # 10. check that the results are correct
537 self.get_engine_result(config_checklist, result, 'json')
540 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1_new', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
541 {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2_new', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'OK', 'check_result_bool': True},
542 {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'FAIL: "expected_3_new"', 'check_result_bool': False}]
545 # 11. override expected value and perform the checks again
546 override_expected_value(config_checklist, 'name_3', 'expected_3_new')
547 perform_checks(config_checklist)
549 # 12. check that the results are correct
551 self.get_engine_result(config_checklist, result, 'json')
554 [{'option_name': 'CONFIG_NAME_1', 'type': 'kconfig', 'desired_val': 'expected_1_new', 'decision': 'decision_1', 'reason': 'reason_1', 'check_result': 'OK', 'check_result_bool': True},
555 {'option_name': 'name_2', 'type': 'cmdline', 'desired_val': 'expected_2_new', 'decision': 'decision_2', 'reason': 'reason_2', 'check_result': 'OK', 'check_result_bool': True},
556 {'option_name': 'name_3', 'type': 'sysctl', 'desired_val': 'expected_3_new', 'decision': 'decision_3', 'reason': 'reason_3', 'check_result': 'OK', 'check_result_bool': True}]