1#!/usr/bin/env python3
2# Copyright (c) 2023 Intel Corporation
3#
4# SPDX-License-Identifier: Apache-2.0
5"""
6Tests for cmakecache.py classes' methods
7"""
8
9import mock
10import pytest
11
12from contextlib import nullcontext
13
14from twisterlib.cmakecache import CMakeCacheEntry, CMakeCache
15
16
17TESTDATA_1 = [
18    ('ON', True),
19    ('YES', True),
20    ('TRUE', True),
21    ('Y', True),
22    ('OFF', False),
23    ('NO', False),
24    ('FALSE', False),
25    ('N', False),
26    ('IGNORE', False),
27    ('NOTFOUND', False),
28    ('', False),
29    ('DUMMY-NOTFOUND', False),
30    ('1', True),
31    ('0', False),
32    ('I AM NOT A PROPER VALUE', ValueError),
33]
34
35
36@pytest.mark.parametrize(
37    'cmake_bool, expected_bool',
38    TESTDATA_1,
39    ids=[t[0] for t in TESTDATA_1]
40)
41def test_cmakecacheentry_to_bool(cmake_bool, expected_bool):
42    with pytest.raises(expected_bool) if \
43     not isinstance(expected_bool, bool) else nullcontext() as exception:
44        b = CMakeCacheEntry._to_bool(cmake_bool)
45
46    if exception is None:
47        assert b == expected_bool
48    else:
49        assert str(exception.value) == f'invalid bool {cmake_bool}'
50
51
52TESTDATA_2 = [
53    (
54        '// I am a comment',
55        None
56    ),
57    (
58        '# I am a comment too',
59        None
60    ),
61    (
62        '  	\r\n	     ',
63        None
64    ),
65    (
66        'DUMMY:WRONG_TYPE=???',
67        None
68    ),
69    (
70        'DUMMY_VALUE_NAME1:STRING=I am a dummy string',
71        CMakeCacheEntry('DUMMY_VALUE_NAME1', 'I am a dummy string')
72    ),
73    (
74       'DUMMY_VALUE_NAME2:STRING=list_el1;list_el2;list_el3',
75        CMakeCacheEntry(
76            'DUMMY_VALUE_NAME2',
77            ['list_el1', 'list_el2', 'list_el3']
78        )
79    ),
80    (
81        'DUMMY_VALUE_NAME3:INTERNAL=I am a dummy internal string',
82        CMakeCacheEntry('DUMMY_VALUE_NAME3', 'I am a dummy internal string')),
83    (
84        'DUMMY_VALUE_NAME4:INTERNAL=list_el1;list_el2',
85        CMakeCacheEntry('DUMMY_VALUE_NAME4', ['list_el1', 'list_el2'])
86    ),
87    (
88        'DUMMY_VALUE_NAME5:FILEPATH=/path/to/dir',
89        CMakeCacheEntry('DUMMY_VALUE_NAME5', '/path/to/dir')
90    ),
91    (
92        'DUMMY_VALUE_NAME6:PATH=/path/to/dir/file.txt',
93        CMakeCacheEntry('DUMMY_VALUE_NAME6', '/path/to/dir/file.txt')),
94    (
95        'DUMMY_VALUE_NAME7:BOOL=Y',
96        CMakeCacheEntry('DUMMY_VALUE_NAME7', True)
97    ),
98    (
99        'DUMMY_VALUE_NAME8:BOOL=FALSE',
100        CMakeCacheEntry('DUMMY_VALUE_NAME8', False)
101    ),
102    (
103        'DUMMY_VALUE_NAME9:BOOL=NOT_A_BOOL',
104        ValueError(
105          (
106              'invalid bool NOT_A_BOOL',
107              'on line 7: DUMMY_VALUE_NAME9:BOOL=NOT_A_BOOL'
108          )
109        )
110    ),
111]
112
113
114@pytest.mark.parametrize(
115    'cmake_line, expected',
116    TESTDATA_2,
117    ids=[
118        '// comment',
119        '# comment',
120        'whitespace',
121        'unrecognised type',
122        'string',
123        'string list',
124        'internal string',
125        'internal list',
126        'filepath',
127        'path',
128        'true bool',
129        'false bool',
130        'not a bool'
131    ]
132)
133def test_cmakecacheentry_from_line(cmake_line, expected):
134    cmake_line_number = 7
135
136    with pytest.raises(type(expected)) if \
137     isinstance(expected, Exception) else nullcontext() as exception:
138        entry = CMakeCacheEntry.from_line(cmake_line, cmake_line_number)
139
140    if exception is not None:
141        assert repr(exception.value) == repr(expected)
142        return
143
144    if expected is None:
145        assert entry is None
146        return
147
148    assert entry.name == expected.name
149    assert entry.value == expected.value
150
151
152TESTDATA_3 = [
153    (
154        CMakeCacheEntry('DUMMY_NAME1', 'dummy value'),
155        'CMakeCacheEntry(name=DUMMY_NAME1, value=dummy value)'
156    ),
157    (
158        CMakeCacheEntry('DUMMY_NAME2', False),
159        'CMakeCacheEntry(name=DUMMY_NAME2, value=False)'
160    )
161]
162
163
164@pytest.mark.parametrize(
165    'cmake_cache_entry, expected',
166    TESTDATA_3,
167    ids=['string value', 'bool value']
168)
169def test_cmakecacheentry_str(cmake_cache_entry, expected):
170    assert str(cmake_cache_entry) == expected
171
172
173def test_cmakecache_load():
174    file_data = (
175        'DUMMY_NAME1:STRING=First line\n'
176        '//Comment on the second line\n'
177        'DUMMY_NAME2:STRING=Third line\n'
178        'DUMMY_NAME3:STRING=Fourth line\n'
179    )
180
181    with mock.patch('builtins.open', mock.mock_open(read_data=file_data)):
182        cache = CMakeCache.from_file('dummy/path/CMakeCache.txt')
183
184    assert cache.cache_file == 'dummy/path/CMakeCache.txt'
185
186    expected = [
187        ('DUMMY_NAME1', 'First line'),
188        ('DUMMY_NAME2', 'Third line'),
189        ('DUMMY_NAME3', 'Fourth line')
190    ]
191
192    for expect in reversed(expected):
193        item = cache._entries.popitem()
194
195        assert item[0] == expect[0]
196        assert item[1].name == expect[0]
197        assert item[1].value == expect[1]
198
199
200def test_cmakecache_get():
201    file_data = 'DUMMY_NAME:STRING=Dummy value'
202
203    with mock.patch('builtins.open', mock.mock_open(read_data=file_data)):
204        cache = CMakeCache.from_file('dummy/path/CMakeCache.txt')
205
206    good_val = cache.get('DUMMY_NAME')
207
208    assert good_val == 'Dummy value'
209
210    bad_val = cache.get('ANOTHER_NAME')
211
212    assert bad_val is None
213
214    bad_val = cache.get('ANOTHER_NAME', default='No such value')
215
216    assert bad_val == 'No such value'
217
218
219TESTDATA_4 = [
220    ('STRING=el1;el2;el3;el4', True, ['el1', 'el2', 'el3', 'el4']),
221    ('STRING=dummy value', True, ['dummy value']),
222    ('STRING=', True, []),
223    ('BOOL=True', True, RuntimeError),
224    ('STRING=dummy value', False, []),
225]
226
227
228@pytest.mark.parametrize(
229    'value, correct_get, expected',
230    TESTDATA_4,
231    ids=['list', 'single value', 'empty', 'exception', 'get failure']
232)
233def test_cmakecache_get_list(value, correct_get, expected):
234    file_data = f'DUMMY_NAME:{value}'
235
236    with mock.patch('builtins.open', mock.mock_open(read_data=file_data)):
237        cache = CMakeCache.from_file('dummy/path/CMakeCache.txt')
238
239    with pytest.raises(expected) if \
240     isinstance(expected, type) and issubclass(expected, Exception) else \
241     nullcontext() as exception:
242        res = cache.get_list('DUMMY_NAME') if \
243         correct_get else cache.get_list('ANOTHER_NAME')
244
245    if exception is None:
246        assert res == expected
247
248
249def test_cmakecache_dunders():
250    file_data = f'DUMMY_NAME:STRING=dummy value'
251
252    with mock.patch('builtins.open', mock.mock_open(read_data=file_data)):
253        cache = CMakeCache.from_file('dummy/path/CMakeCache.txt')
254
255    assert len(list(cache.__iter__())) == 1
256
257    cache.__setitem__(
258        'ANOTHER_NAME',
259        CMakeCacheEntry('ANOTHER_NAME', 'another value')
260    )
261
262    assert cache.__contains__('ANOTHER_NAME')
263    assert cache.__getitem__('ANOTHER_NAME') == 'another value'
264
265    cache.__delitem__('ANOTHER_NAME')
266
267    assert not cache.__contains__('ANOTHER_NAME')
268
269    assert len(list(cache.__iter__())) == 1
270
271    with pytest.raises(TypeError):
272        cache.__setitem__('WRONG_TYPE', 'Yet Another Dummy Value')
273