ccb1b18d618a6c51af97411a900f89d927e9c3c1
[kconfig-hardened-check.git] / kernel_hardening_checker / test_engine.py
1 #!/usr/bin/env python3
2
3 """
4 This tool is for checking the security hardening options of the Linux kernel.
5
6 Author: Alexander Popov <alex.popov@linux.com>
7
8 This module performs unit-testing of the kernel-hardening-checker engine.
9 """
10
11 # pylint: disable=missing-function-docstring,line-too-long
12
13 import unittest
14 import io
15 import sys
16 from collections import OrderedDict
17 import json
18 import inspect
19 from .engine import KconfigCheck, CmdlineCheck, SysctlCheck, VersionCheck, OR, AND, populate_with_data, perform_checks, override_expected_value
20
21
22 class TestEngine(unittest.TestCase):
23     """
24     Example test scenario:
25
26         # 1. prepare the checklist
27         config_checklist = []
28         config_checklist += [KconfigCheck('reason_1', 'decision_1', 'KCONFIG_NAME', 'expected_1')]
29         config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'cmdline_name', 'expected_2')]
30         config_checklist += [SysctlCheck('reason_3', 'decision_3', 'sysctl_name', 'expected_3')]
31
32         # 2. prepare the parsed kconfig options
33         parsed_kconfig_options = OrderedDict()
34         parsed_kconfig_options['CONFIG_KCONFIG_NAME'] = 'UNexpected_1'
35
36         # 3. prepare the parsed cmdline options
37         parsed_cmdline_options = OrderedDict()
38         parsed_cmdline_options['cmdline_name'] = 'expected_2'
39
40         # 4. prepare the parsed sysctl options
41         parsed_sysctl_options = OrderedDict()
42         parsed_sysctl_options['sysctl_name'] = 'expected_3'
43
44         # 5. prepare the kernel version
45         kernel_version = (42, 43)
46
47         # 6. run the engine
48         self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, kernel_version)
49
50         # 7. check that the results are correct
51         result = []
52         self.get_engine_result(config_checklist, result, 'json')
53         self.assertEqual(...
54     """
55
56     maxDiff = None
57
58     @staticmethod
59     def run_engine(checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, kernel_version):
60         # populate the checklist with data
61         if parsed_kconfig_options:
62             populate_with_data(checklist, parsed_kconfig_options, 'kconfig')
63         if parsed_cmdline_options:
64             populate_with_data(checklist, parsed_cmdline_options, 'cmdline')
65         if parsed_sysctl_options:
66             populate_with_data(checklist, parsed_sysctl_options, 'sysctl')
67         if kernel_version:
68             populate_with_data(checklist, kernel_version, 'version')
69
70         # now everything is ready, perform the checks
71         perform_checks(checklist)
72
73         # print the table with the results
74         print(f'\n{inspect.stack()[1].function}():')
75         print('=' * 121)
76         for opt in checklist:
77             opt.table_print('verbose', True) # verbose mode, with_results
78             print()
79             print('=' * 121)
80
81         # print the results in JSON
82         result = []
83         for opt in checklist:
84             result.append(opt.json_dump(True)) # with_results
85         print(json.dumps(result))
86         print()
87
88     @staticmethod
89     def get_engine_result(checklist, result, result_type):
90         assert(result_type in ('json', 'stdout', 'stdout_verbose')), \
91                f'invalid result type "{result_type}"'
92
93         if result_type == 'json':
94             for opt in checklist:
95                 result.append(opt.json_dump(True)) # with_results
96             return
97
98         captured_output = io.StringIO()
99         stdout_backup = sys.stdout
100         sys.stdout = captured_output
101         for opt in checklist:
102             if result_type == 'stdout_verbose':
103                 opt.table_print('verbose', True) # verbose mode, with_results
104             else:
105                 opt.table_print(None, True) # normal mode, with_results
106         sys.stdout = stdout_backup
107         result.append(captured_output.getvalue())
108
109     def test_simple_kconfig(self):
110         # 1. prepare the checklist
111         config_checklist = []
112         config_checklist += [KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1')]
113         config_checklist += [KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2')]
114         config_checklist += [KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3')]
115         config_checklist += [KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'is not set')]
116         config_checklist += [KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'is present')]
117         config_checklist += [KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'is present')]
118         config_checklist += [KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'is not off')]
119         config_checklist += [KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is not off')]
120         config_checklist += [KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'is not off')]
121         config_checklist += [KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is not off')]
122
123         # 2. prepare the parsed kconfig options
124         parsed_kconfig_options = OrderedDict()
125         parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
126         parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
127         parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
128         parsed_kconfig_options['CONFIG_NAME_7'] = 'really_not_off'
129         parsed_kconfig_options['CONFIG_NAME_8'] = 'off'
130         parsed_kconfig_options['CONFIG_NAME_9'] = '0'
131
132         # 3. run the engine
133         self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
134
135         # 4. check that the results are correct
136         result = []
137         self.get_engine_result(config_checklist, result, 'json')
138         self.assertEqual(
139                 result,
140                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK'],
141                  ['CONFIG_NAME_2', 'kconfig', 'expected_2', 'decision_2', 'reason_2', 'FAIL: "UNexpected_2"'],
142                  ['CONFIG_NAME_3', 'kconfig', 'expected_3', 'decision_3', 'reason_3', 'FAIL: is not found'],
143                  ['CONFIG_NAME_4', 'kconfig', 'is not set', 'decision_4', 'reason_4', 'OK: is not found'],
144                  ['CONFIG_NAME_5', 'kconfig', 'is present', 'decision_5', 'reason_5', 'OK: is present'],
145                  ['CONFIG_NAME_6', 'kconfig', 'is present', 'decision_6', 'reason_6', 'FAIL: is not present'],
146                  ['CONFIG_NAME_7', 'kconfig', 'is not off', 'decision_7', 'reason_7', 'OK: is not off, "really_not_off"'],
147                  ['CONFIG_NAME_8', 'kconfig', 'is not off', 'decision_8', 'reason_8', 'FAIL: is off'],
148                  ['CONFIG_NAME_9', 'kconfig', 'is not off', 'decision_9', 'reason_9', 'FAIL: is off, "0"'],
149                  ['CONFIG_NAME_10', 'kconfig', 'is not off', 'decision_10', 'reason_10', 'FAIL: is off, not found']]
150         )
151
152     def test_simple_cmdline(self):
153         # 1. prepare the checklist
154         config_checklist = []
155         config_checklist += [CmdlineCheck('reason_1', 'decision_1', 'name_1', 'expected_1')]
156         config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
157         config_checklist += [CmdlineCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
158         config_checklist += [CmdlineCheck('reason_4', 'decision_4', 'name_4', 'is not set')]
159         config_checklist += [CmdlineCheck('reason_5', 'decision_5', 'name_5', 'is present')]
160         config_checklist += [CmdlineCheck('reason_6', 'decision_6', 'name_6', 'is present')]
161         config_checklist += [CmdlineCheck('reason_7', 'decision_7', 'name_7', 'is not off')]
162         config_checklist += [CmdlineCheck('reason_8', 'decision_8', 'name_8', 'is not off')]
163         config_checklist += [CmdlineCheck('reason_9', 'decision_9', 'name_9', 'is not off')]
164         config_checklist += [CmdlineCheck('reason_10', 'decision_10', 'name_10', 'is not off')]
165
166         # 2. prepare the parsed cmdline options
167         parsed_cmdline_options = OrderedDict()
168         parsed_cmdline_options['name_1'] = 'expected_1'
169         parsed_cmdline_options['name_2'] = 'UNexpected_2'
170         parsed_cmdline_options['name_5'] = ''
171         parsed_cmdline_options['name_7'] = ''
172         parsed_cmdline_options['name_8'] = 'off'
173         parsed_cmdline_options['name_9'] = '0'
174
175         # 3. run the engine
176         self.run_engine(config_checklist, None, parsed_cmdline_options, None, None)
177
178         # 4. check that the results are correct
179         result = []
180         self.get_engine_result(config_checklist, result, 'json')
181         self.assertEqual(
182                 result,
183                 [['name_1', 'cmdline', 'expected_1', 'decision_1', 'reason_1', 'OK'],
184                  ['name_2', 'cmdline', 'expected_2', 'decision_2', 'reason_2', 'FAIL: "UNexpected_2"'],
185                  ['name_3', 'cmdline', 'expected_3', 'decision_3', 'reason_3', 'FAIL: is not found'],
186                  ['name_4', 'cmdline', 'is not set', 'decision_4', 'reason_4', 'OK: is not found'],
187                  ['name_5', 'cmdline', 'is present', 'decision_5', 'reason_5', 'OK: is present'],
188                  ['name_6', 'cmdline', 'is present', 'decision_6', 'reason_6', 'FAIL: is not present'],
189                  ['name_7', 'cmdline', 'is not off', 'decision_7', 'reason_7', 'OK: is not off, ""'],
190                  ['name_8', 'cmdline', 'is not off', 'decision_8', 'reason_8', 'FAIL: is off'],
191                  ['name_9', 'cmdline', 'is not off', 'decision_9', 'reason_9', 'FAIL: is off, "0"'],
192                  ['name_10', 'cmdline', 'is not off', 'decision_10', 'reason_10', 'FAIL: is off, not found']]
193         )
194
195     def test_simple_sysctl(self):
196         # 1. prepare the checklist
197         config_checklist = []
198         config_checklist += [SysctlCheck('reason_1', 'decision_1', 'name_1', 'expected_1')]
199         config_checklist += [SysctlCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
200         config_checklist += [SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
201         config_checklist += [SysctlCheck('reason_4', 'decision_4', 'name_4', 'is not set')]
202         config_checklist += [SysctlCheck('reason_5', 'decision_5', 'name_5', 'is present')]
203         config_checklist += [SysctlCheck('reason_6', 'decision_6', 'name_6', 'is present')]
204         config_checklist += [SysctlCheck('reason_7', 'decision_7', 'name_7', 'is not off')]
205         config_checklist += [SysctlCheck('reason_8', 'decision_8', 'name_8', 'is not off')]
206         config_checklist += [SysctlCheck('reason_9', 'decision_9', 'name_9', 'is not off')]
207         config_checklist += [SysctlCheck('reason_10', 'decision_10', 'name_10', 'is not off')]
208
209         # 2. prepare the parsed sysctl options
210         parsed_sysctl_options = OrderedDict()
211         parsed_sysctl_options['name_1'] = 'expected_1'
212         parsed_sysctl_options['name_2'] = 'UNexpected_2'
213         parsed_sysctl_options['name_5'] = ''
214         parsed_sysctl_options['name_7'] = ''
215         parsed_sysctl_options['name_8'] = 'off'
216         parsed_sysctl_options['name_9'] = '0'
217
218         # 3. run the engine
219         self.run_engine(config_checklist, None, None, parsed_sysctl_options, None)
220
221         # 4. check that the results are correct
222         result = []
223         self.get_engine_result(config_checklist, result, 'json')
224         self.assertEqual(
225                 result,
226                 [['name_1', 'sysctl', 'expected_1', 'decision_1', 'reason_1', 'OK'],
227                  ['name_2', 'sysctl', 'expected_2', 'decision_2', 'reason_2', 'FAIL: "UNexpected_2"'],
228                  ['name_3', 'sysctl', 'expected_3', 'decision_3', 'reason_3', 'FAIL: is not found'],
229                  ['name_4', 'sysctl', 'is not set', 'decision_4', 'reason_4', 'OK: is not found'],
230                  ['name_5', 'sysctl', 'is present', 'decision_5', 'reason_5', 'OK: is present'],
231                  ['name_6', 'sysctl', 'is present', 'decision_6', 'reason_6', 'FAIL: is not present'],
232                  ['name_7', 'sysctl', 'is not off', 'decision_7', 'reason_7', 'OK: is not off, ""'],
233                  ['name_8', 'sysctl', 'is not off', 'decision_8', 'reason_8', 'FAIL: is off'],
234                  ['name_9', 'sysctl', 'is not off', 'decision_9', 'reason_9', 'FAIL: is off, "0"'],
235                  ['name_10', 'sysctl', 'is not off', 'decision_10', 'reason_10', 'FAIL: is off, not found']]
236         )
237
238     def test_complex_or(self):
239         # 1. prepare the checklist
240         config_checklist = []
241         config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
242                                 KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'))]
243         config_checklist += [OR(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
244                                 KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'))]
245         config_checklist += [OR(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
246                                 KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'))]
247         config_checklist += [OR(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
248                                 KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is not set'))]
249         config_checklist += [OR(KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9'),
250                                 KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is present'))]
251         config_checklist += [OR(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
252                                 KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'is not off'))]
253
254         # 2. prepare the parsed kconfig options
255         parsed_kconfig_options = OrderedDict()
256         parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
257         parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
258         parsed_kconfig_options['CONFIG_NAME_3'] = 'UNexpected_3'
259         parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
260         parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
261         parsed_kconfig_options['CONFIG_NAME_6'] = 'UNexpected_6'
262         parsed_kconfig_options['CONFIG_NAME_10'] = 'UNexpected_10'
263         parsed_kconfig_options['CONFIG_NAME_12'] = 'really_not_off'
264
265         # 3. run the engine
266         self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
267
268         # 4. check that the results are correct
269         result = []
270         self.get_engine_result(config_checklist, result, 'json')
271         self.assertEqual(
272                 result,
273                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK'],
274                  ['CONFIG_NAME_3', 'kconfig', 'expected_3', 'decision_3', 'reason_3', 'OK: CONFIG_NAME_4 is "expected_4"'],
275                  ['CONFIG_NAME_5', 'kconfig', 'expected_5', 'decision_5', 'reason_5', 'FAIL: "UNexpected_5"'],
276                  ['CONFIG_NAME_7', 'kconfig', 'expected_7', 'decision_7', 'reason_7', 'OK: CONFIG_NAME_8 is not found'],
277                  ['CONFIG_NAME_9', 'kconfig', 'expected_9', 'decision_9', 'reason_9', 'OK: CONFIG_NAME_10 is present'],
278                  ['CONFIG_NAME_11', 'kconfig', 'expected_11', 'decision_11', 'reason_11', 'OK: CONFIG_NAME_12 is not off']]
279         )
280
281     def test_complex_and(self):
282         # 1. prepare the checklist
283         config_checklist = []
284         config_checklist += [AND(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
285                                  KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'))]
286         config_checklist += [AND(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
287                                  KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'))]
288         config_checklist += [AND(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
289                                  KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6'))]
290         config_checklist += [AND(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
291                                  KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'is present'))]
292         config_checklist += [AND(KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9'),
293                                  KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'is not off'))]
294         config_checklist += [AND(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
295                                  KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'is not off'))]
296
297         # 2. prepare the parsed kconfig options
298         parsed_kconfig_options = OrderedDict()
299         parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
300         parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
301         parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
302         parsed_kconfig_options['CONFIG_NAME_4'] = 'UNexpected_4'
303         parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
304         parsed_kconfig_options['CONFIG_NAME_6'] = 'expected_6'
305         parsed_kconfig_options['CONFIG_NAME_7'] = 'expected_7'
306         parsed_kconfig_options['CONFIG_NAME_9'] = 'expected_9'
307         parsed_kconfig_options['CONFIG_NAME_10'] = '0'
308         parsed_kconfig_options['CONFIG_NAME_11'] = 'expected_11'
309
310         # 3. run the engine
311         self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
312
313         # 4. check that the results are correct
314         result = []
315         self.get_engine_result(config_checklist, result, 'json')
316         self.assertEqual(
317                 result,
318                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK'],
319                  ['CONFIG_NAME_3', 'kconfig', 'expected_3', 'decision_3', 'reason_3', 'FAIL: CONFIG_NAME_4 is not "expected_4"'],
320                  ['CONFIG_NAME_5', 'kconfig', 'expected_5', 'decision_5', 'reason_5', 'FAIL: "UNexpected_5"'],
321                  ['CONFIG_NAME_7', 'kconfig', 'expected_7', 'decision_7', 'reason_7', 'FAIL: CONFIG_NAME_8 is not present'],
322                  ['CONFIG_NAME_9', 'kconfig', 'expected_9', 'decision_9', 'reason_9', 'FAIL: CONFIG_NAME_10 is off'],
323                  ['CONFIG_NAME_11', 'kconfig', 'expected_11', 'decision_11', 'reason_11', 'FAIL: CONFIG_NAME_12 is off, not found']]
324         )
325
326     def test_complex_nested(self):
327         # 1. prepare the checklist
328         config_checklist = []
329         config_checklist += [AND(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
330                                  OR(KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'),
331                                     KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3')))]
332         config_checklist += [AND(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
333                                  OR(KconfigCheck('reason_5', 'decision_5', 'NAME_5', 'expected_5'),
334                                     KconfigCheck('reason_6', 'decision_6', 'NAME_6', 'expected_6')))]
335         config_checklist += [OR(KconfigCheck('reason_7', 'decision_7', 'NAME_7', 'expected_7'),
336                                  AND(KconfigCheck('reason_8', 'decision_8', 'NAME_8', 'expected_8'),
337                                      KconfigCheck('reason_9', 'decision_9', 'NAME_9', 'expected_9')))]
338         config_checklist += [OR(KconfigCheck('reason_10', 'decision_10', 'NAME_10', 'expected_10'),
339                                  AND(KconfigCheck('reason_11', 'decision_11', 'NAME_11', 'expected_11'),
340                                      KconfigCheck('reason_12', 'decision_12', 'NAME_12', 'expected_12')))]
341
342         # 2. prepare the parsed kconfig options
343         parsed_kconfig_options = OrderedDict()
344         parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1'
345         parsed_kconfig_options['CONFIG_NAME_2'] = 'UNexpected_2'
346         parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
347         parsed_kconfig_options['CONFIG_NAME_4'] = 'expected_4'
348         parsed_kconfig_options['CONFIG_NAME_5'] = 'UNexpected_5'
349         parsed_kconfig_options['CONFIG_NAME_6'] = 'UNexpected_6'
350         parsed_kconfig_options['CONFIG_NAME_7'] = 'UNexpected_7'
351         parsed_kconfig_options['CONFIG_NAME_8'] = 'expected_8'
352         parsed_kconfig_options['CONFIG_NAME_9'] = 'expected_9'
353         parsed_kconfig_options['CONFIG_NAME_10'] = 'UNexpected_10'
354         parsed_kconfig_options['CONFIG_NAME_11'] = 'UNexpected_11'
355         parsed_kconfig_options['CONFIG_NAME_12'] = 'expected_12'
356
357         # 3. run the engine
358         self.run_engine(config_checklist, parsed_kconfig_options, None, None, None)
359
360         # 4. check that the results are correct
361         result = []
362         self.get_engine_result(config_checklist, result, 'json')
363         self.assertEqual(
364                 result,
365                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK'],
366                  ['CONFIG_NAME_4', 'kconfig', 'expected_4', 'decision_4', 'reason_4', 'FAIL: CONFIG_NAME_5 is not "expected_5"'],
367                  ['CONFIG_NAME_7', 'kconfig', 'expected_7', 'decision_7', 'reason_7', 'OK: CONFIG_NAME_8 is "expected_8"'],
368                  ['CONFIG_NAME_10', 'kconfig', 'expected_10', 'decision_10', 'reason_10', 'FAIL: "UNexpected_10"']]
369         )
370
371     def test_version(self):
372         # 1. prepare the checklist
373         config_checklist = []
374         config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
375                                 VersionCheck((41, 101, 0)))]
376         config_checklist += [AND(KconfigCheck('reason_2', 'decision_2', 'NAME_2', 'expected_2'),
377                                 VersionCheck((44, 1, 0)))]
378         config_checklist += [AND(KconfigCheck('reason_3', 'decision_3', 'NAME_3', 'expected_3'),
379                                 VersionCheck((42, 44, 0)))]
380         config_checklist += [OR(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
381                                 VersionCheck((42, 43, 0)))]
382
383         # 2. prepare the parsed kconfig options
384         parsed_kconfig_options = OrderedDict()
385         parsed_kconfig_options['CONFIG_NAME_2'] = 'expected_2'
386         parsed_kconfig_options['CONFIG_NAME_3'] = 'expected_3'
387
388         # 3. prepare the kernel version
389         kernel_version = (42, 43, 0)
390
391         # 4. run the engine
392         self.run_engine(config_checklist, parsed_kconfig_options, None, None, kernel_version)
393
394         # 5. check that the results are correct
395         result = []
396         self.get_engine_result(config_checklist, result, 'json')
397         self.assertEqual(
398                 result,
399                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK: version >= 41.101'],
400                  ['CONFIG_NAME_2', 'kconfig', 'expected_2', 'decision_2', 'reason_2', 'FAIL: version < 44.1'],
401                  ['CONFIG_NAME_3', 'kconfig', 'expected_3', 'decision_3', 'reason_3', 'FAIL: version < 42.44'],
402                  ['CONFIG_NAME_4', 'kconfig', 'expected_4', 'decision_4', 'reason_4', 'OK: version >= 42.43']]
403         )
404
405     def test_stdout(self):
406         # 1. prepare the checklist
407         config_checklist = []
408         config_checklist += [OR(KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1'),
409                                 CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2'),
410                                 SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3'))]
411         config_checklist += [AND(KconfigCheck('reason_4', 'decision_4', 'NAME_4', 'expected_4'),
412                                  CmdlineCheck('reason_5', 'decision_5', 'name_5', 'expected_5'),
413                                  SysctlCheck('reason_6', 'decision_6', 'name_6', 'expected_6'))]
414
415         # 2. prepare the parsed kconfig options
416         parsed_kconfig_options = OrderedDict()
417         parsed_kconfig_options['CONFIG_NAME_1'] = 'UNexpected_1'
418
419         # 3. prepare the parsed cmdline options
420         parsed_cmdline_options = OrderedDict()
421         parsed_cmdline_options['name_2'] = 'expected_2'
422         parsed_cmdline_options['name_5'] = 'UNexpected_5'
423
424         # 4. prepare the parsed sysctl options
425         parsed_sysctl_options = OrderedDict()
426         parsed_sysctl_options['name_6'] = 'expected_6'
427
428         # 5. run the engine
429         self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, None)
430
431         # 6. check that the results are correct
432         json_result = []
433         self.get_engine_result(config_checklist, json_result, 'json')
434         self.assertEqual(
435                 json_result,
436                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'OK: name_2 is "expected_2"'],
437                  ['CONFIG_NAME_4', 'kconfig', 'expected_4', 'decision_4', 'reason_4', 'FAIL: name_5 is not "expected_5"']]
438         )
439
440         stdout_result = []
441         self.get_engine_result(config_checklist, stdout_result, 'stdout')
442         self.assertEqual(
443                 stdout_result,
444                 [
445 '\
446 CONFIG_NAME_1                           |kconfig| expected_1 |decision_1|     reason_1     | \x1b[32mOK: name_2 is "expected_2"\x1b[0m\
447 CONFIG_NAME_4                           |kconfig| expected_4 |decision_4|     reason_4     | \x1b[31mFAIL: name_5 is not "expected_5"\x1b[0m\
448 '               ]
449         )
450
451         stdout_result = []
452         self.get_engine_result(config_checklist, stdout_result, 'stdout_verbose')
453         self.assertEqual(
454                 stdout_result,
455                 [
456 '\
457     <<< OR >>>                                                                             | \x1b[32mOK: name_2 is "expected_2"\x1b[0m\n\
458 CONFIG_NAME_1                           |kconfig| expected_1 |decision_1|     reason_1     | \x1b[31mFAIL: "UNexpected_1"\x1b[0m\n\
459 name_2                                  |cmdline| expected_2 |decision_2|     reason_2     | \x1b[32mOK\x1b[0m\n\
460 name_3                                  |sysctl | expected_3 |decision_3|     reason_3     | None\
461 '\
462 '\
463     <<< AND >>>                                                                            | \x1b[31mFAIL: name_5 is not "expected_5"\x1b[0m\n\
464 CONFIG_NAME_4                           |kconfig| expected_4 |decision_4|     reason_4     | None\n\
465 name_5                                  |cmdline| expected_5 |decision_5|     reason_5     | \x1b[31mFAIL: "UNexpected_5"\x1b[0m\n\
466 name_6                                  |sysctl | expected_6 |decision_6|     reason_6     | \x1b[32mOK\x1b[0m\
467 '               ]
468         )
469
470     def test_value_overriding(self):
471         # 1. prepare the checklist
472         config_checklist = []
473         config_checklist += [KconfigCheck('reason_1', 'decision_1', 'NAME_1', 'expected_1')]
474         config_checklist += [CmdlineCheck('reason_2', 'decision_2', 'name_2', 'expected_2')]
475         config_checklist += [SysctlCheck('reason_3', 'decision_3', 'name_3', 'expected_3')]
476
477         # 2. prepare the parsed kconfig options
478         parsed_kconfig_options = OrderedDict()
479         parsed_kconfig_options['CONFIG_NAME_1'] = 'expected_1_new'
480
481         # 3. prepare the parsed cmdline options
482         parsed_cmdline_options = OrderedDict()
483         parsed_cmdline_options['name_2'] = 'expected_2_new'
484
485         # 4. prepare the parsed sysctl options
486         parsed_sysctl_options = OrderedDict()
487         parsed_sysctl_options['name_3'] = 'expected_3_new'
488
489         # 5. run the engine
490         self.run_engine(config_checklist, parsed_kconfig_options, parsed_cmdline_options, parsed_sysctl_options, None)
491
492         # 6. check that the results are correct
493         result = []
494         self.get_engine_result(config_checklist, result, 'json')
495         self.assertEqual(
496                 result,
497                 [['CONFIG_NAME_1', 'kconfig', 'expected_1', 'decision_1', 'reason_1', 'FAIL: "expected_1_new"'],
498                  ['name_2', 'cmdline', 'expected_2', 'decision_2', 'reason_2', 'FAIL: "expected_2_new"'],
499                  ['name_3', 'sysctl', 'expected_3', 'decision_3', 'reason_3', 'FAIL: "expected_3_new"']]
500         )
501
502         # 7. override expected value and perform the checks again
503         override_expected_value(config_checklist, 'CONFIG_NAME_1', 'expected_1_new')
504         perform_checks(config_checklist)
505
506         # 8. check that the results are correct
507         result = []
508         self.get_engine_result(config_checklist, result, 'json')
509         self.assertEqual(
510                 result,
511                 [['CONFIG_NAME_1', 'kconfig', 'expected_1_new', 'decision_1', 'reason_1', 'OK'],
512                  ['name_2', 'cmdline', 'expected_2', 'decision_2', 'reason_2', 'FAIL: "expected_2_new"'],
513                  ['name_3', 'sysctl', 'expected_3', 'decision_3', 'reason_3', 'FAIL: "expected_3_new"']]
514         )
515
516         # 9. override expected value and perform the checks again
517         override_expected_value(config_checklist, 'name_2', 'expected_2_new')
518         perform_checks(config_checklist)
519
520         # 10. check that the results are correct
521         result = []
522         self.get_engine_result(config_checklist, result, 'json')
523         self.assertEqual(
524                 result,
525                 [['CONFIG_NAME_1', 'kconfig', 'expected_1_new', 'decision_1', 'reason_1', 'OK'],
526                  ['name_2', 'cmdline', 'expected_2_new', 'decision_2', 'reason_2', 'OK'],
527                  ['name_3', 'sysctl', 'expected_3', 'decision_3', 'reason_3', 'FAIL: "expected_3_new"']]
528         )
529
530         # 11. override expected value and perform the checks again
531         override_expected_value(config_checklist, 'name_3', 'expected_3_new')
532         perform_checks(config_checklist)
533
534         # 12. check that the results are correct
535         result = []
536         self.get_engine_result(config_checklist, result, 'json')
537         self.assertEqual(
538                 result,
539                 [['CONFIG_NAME_1', 'kconfig', 'expected_1_new', 'decision_1', 'reason_1', 'OK'],
540                  ['name_2', 'cmdline', 'expected_2_new', 'decision_2', 'reason_2', 'OK'],
541                  ['name_3', 'sysctl', 'expected_3_new', 'decision_3', 'reason_3', 'OK']]
542         )