xref: /openbmc/openbmc/poky/bitbake/lib/bb/tests/utils.py (revision 96ff1984)
1#
2# BitBake Tests for utils.py
3#
4# Copyright (C) 2012 Richard Purdie
5#
6# SPDX-License-Identifier: GPL-2.0-only
7#
8
9import unittest
10import bb
11import os
12import tempfile
13import re
14
15class VerCmpString(unittest.TestCase):
16
17    def test_vercmpstring(self):
18        result = bb.utils.vercmp_string('1', '2')
19        self.assertTrue(result < 0)
20        result = bb.utils.vercmp_string('2', '1')
21        self.assertTrue(result > 0)
22        result = bb.utils.vercmp_string('1', '1.0')
23        self.assertTrue(result < 0)
24        result = bb.utils.vercmp_string('1', '1.1')
25        self.assertTrue(result < 0)
26        result = bb.utils.vercmp_string('1.1', '1_p2')
27        self.assertTrue(result < 0)
28        result = bb.utils.vercmp_string('1.0', '1.0+1.1-beta1')
29        self.assertTrue(result < 0)
30        result = bb.utils.vercmp_string('1.1', '1.0+1.1-beta1')
31        self.assertTrue(result > 0)
32        result = bb.utils.vercmp_string('1a', '1a1')
33        self.assertTrue(result < 0)
34        result = bb.utils.vercmp_string('1a1', '1a')
35        self.assertTrue(result > 0)
36        result = bb.utils.vercmp_string('1.', '1.1')
37        self.assertTrue(result < 0)
38        result = bb.utils.vercmp_string('1.1', '1.')
39        self.assertTrue(result > 0)
40
41    def test_explode_dep_versions(self):
42        correctresult = {"foo" : ["= 1.10"]}
43        result = bb.utils.explode_dep_versions2("foo (= 1.10)")
44        self.assertEqual(result, correctresult)
45        result = bb.utils.explode_dep_versions2("foo (=1.10)")
46        self.assertEqual(result, correctresult)
47        result = bb.utils.explode_dep_versions2("foo ( = 1.10)")
48        self.assertEqual(result, correctresult)
49        result = bb.utils.explode_dep_versions2("foo ( =1.10)")
50        self.assertEqual(result, correctresult)
51        result = bb.utils.explode_dep_versions2("foo ( = 1.10 )")
52        self.assertEqual(result, correctresult)
53        result = bb.utils.explode_dep_versions2("foo ( =1.10 )")
54        self.assertEqual(result, correctresult)
55
56    def test_vercmp_string_op(self):
57        compareops = [('1', '1', '=', True),
58                      ('1', '1', '==', True),
59                      ('1', '1', '!=', False),
60                      ('1', '1', '>', False),
61                      ('1', '1', '<', False),
62                      ('1', '1', '>=', True),
63                      ('1', '1', '<=', True),
64                      ('1', '0', '=', False),
65                      ('1', '0', '==', False),
66                      ('1', '0', '!=', True),
67                      ('1', '0', '>', True),
68                      ('1', '0', '<', False),
69                      ('1', '0', '>>', True),
70                      ('1', '0', '<<', False),
71                      ('1', '0', '>=', True),
72                      ('1', '0', '<=', False),
73                      ('0', '1', '=', False),
74                      ('0', '1', '==', False),
75                      ('0', '1', '!=', True),
76                      ('0', '1', '>', False),
77                      ('0', '1', '<', True),
78                      ('0', '1', '>>', False),
79                      ('0', '1', '<<', True),
80                      ('0', '1', '>=', False),
81                      ('0', '1', '<=', True)]
82
83        for arg1, arg2, op, correctresult in compareops:
84            result = bb.utils.vercmp_string_op(arg1, arg2, op)
85            self.assertEqual(result, correctresult, 'vercmp_string_op("%s", "%s", "%s") != %s' % (arg1, arg2, op, correctresult))
86
87        # Check that clearly invalid operator raises an exception
88        self.assertRaises(bb.utils.VersionStringException, bb.utils.vercmp_string_op, '0', '0', '$')
89
90
91class Path(unittest.TestCase):
92    def test_unsafe_delete_path(self):
93        checkitems = [('/', True),
94                      ('//', True),
95                      ('///', True),
96                      (os.getcwd().count(os.sep) * ('..' + os.sep), True),
97                      (os.environ.get('HOME', '/home/test'), True),
98                      ('/home/someone', True),
99                      ('/home/other/', True),
100                      ('/home/other/subdir', False),
101                      ('', False)]
102        for arg1, correctresult in checkitems:
103            result = bb.utils._check_unsafe_delete_path(arg1)
104            self.assertEqual(result, correctresult, '_check_unsafe_delete_path("%s") != %s' % (arg1, correctresult))
105
106
107class EditMetadataFile(unittest.TestCase):
108    _origfile = """
109# A comment
110HELLO = "oldvalue"
111
112THIS = "that"
113
114# Another comment
115NOCHANGE = "samevalue"
116OTHER = 'anothervalue'
117
118MULTILINE = "a1 \\
119             a2 \\
120             a3"
121
122MULTILINE2 := " \\
123               b1 \\
124               b2 \\
125               b3 \\
126               "
127
128
129MULTILINE3 = " \\
130              c1 \\
131              c2 \\
132              c3 \\
133"
134
135do_functionname() {
136    command1 ${VAL1} ${VAL2}
137    command2 ${VAL3} ${VAL4}
138}
139"""
140    def _testeditfile(self, varvalues, compareto, dummyvars=None):
141        if dummyvars is None:
142            dummyvars = []
143        with tempfile.NamedTemporaryFile('w', delete=False) as tf:
144            tf.write(self._origfile)
145            tf.close()
146            try:
147                varcalls = []
148                def handle_file(varname, origvalue, op, newlines):
149                    self.assertIn(varname, varvalues, 'Callback called for variable %s not in the list!' % varname)
150                    self.assertNotIn(varname, dummyvars, 'Callback called for variable %s in dummy list!' % varname)
151                    varcalls.append(varname)
152                    return varvalues[varname]
153
154                bb.utils.edit_metadata_file(tf.name, varvalues.keys(), handle_file)
155                with open(tf.name) as f:
156                    modfile = f.readlines()
157                # Ensure the output matches the expected output
158                self.assertEqual(compareto.splitlines(True), modfile)
159                # Ensure the callback function was called for every variable we asked for
160                # (plus allow testing behaviour when a requested variable is not present)
161                self.assertEqual(sorted(varvalues.keys()), sorted(varcalls + dummyvars))
162            finally:
163                os.remove(tf.name)
164
165
166    def test_edit_metadata_file_nochange(self):
167        # Test file doesn't get modified with nothing to do
168        self._testeditfile({}, self._origfile)
169        # Test file doesn't get modified with only dummy variables
170        self._testeditfile({'DUMMY1': ('should_not_set', None, 0, True),
171                        'DUMMY2': ('should_not_set_again', None, 0, True)}, self._origfile, dummyvars=['DUMMY1', 'DUMMY2'])
172        # Test file doesn't get modified with some the same values
173        self._testeditfile({'THIS': ('that', None, 0, True),
174                        'OTHER': ('anothervalue', None, 0, True),
175                        'MULTILINE3': ('               c1               c2               c3 ', None, 4, False)}, self._origfile)
176
177    def test_edit_metadata_file_1(self):
178
179        newfile1 = """
180# A comment
181HELLO = "newvalue"
182
183THIS = "that"
184
185# Another comment
186NOCHANGE = "samevalue"
187OTHER = 'anothervalue'
188
189MULTILINE = "a1 \\
190             a2 \\
191             a3"
192
193MULTILINE2 := " \\
194               b1 \\
195               b2 \\
196               b3 \\
197               "
198
199
200MULTILINE3 = " \\
201              c1 \\
202              c2 \\
203              c3 \\
204"
205
206do_functionname() {
207    command1 ${VAL1} ${VAL2}
208    command2 ${VAL3} ${VAL4}
209}
210"""
211        self._testeditfile({'HELLO': ('newvalue', None, 4, True)}, newfile1)
212
213
214    def test_edit_metadata_file_2(self):
215
216        newfile2 = """
217# A comment
218HELLO = "oldvalue"
219
220THIS = "that"
221
222# Another comment
223NOCHANGE = "samevalue"
224OTHER = 'anothervalue'
225
226MULTILINE = " \\
227    d1 \\
228    d2 \\
229    d3 \\
230    "
231
232MULTILINE2 := " \\
233               b1 \\
234               b2 \\
235               b3 \\
236               "
237
238
239MULTILINE3 = "nowsingle"
240
241do_functionname() {
242    command1 ${VAL1} ${VAL2}
243    command2 ${VAL3} ${VAL4}
244}
245"""
246        self._testeditfile({'MULTILINE': (['d1','d2','d3'], None, 4, False),
247                        'MULTILINE3': ('nowsingle', None, 4, True),
248                        'NOTPRESENT': (['a', 'b'], None, 4, False)}, newfile2, dummyvars=['NOTPRESENT'])
249
250
251    def test_edit_metadata_file_3(self):
252
253        newfile3 = """
254# A comment
255HELLO = "oldvalue"
256
257# Another comment
258NOCHANGE = "samevalue"
259OTHER = "yetanothervalue"
260
261MULTILINE = "e1 \\
262             e2 \\
263             e3 \\
264             "
265
266MULTILINE2 := "f1 \\
267\tf2 \\
268\t"
269
270
271MULTILINE3 = " \\
272              c1 \\
273              c2 \\
274              c3 \\
275"
276
277do_functionname() {
278    othercommand_one a b c
279    othercommand_two d e f
280}
281"""
282
283        self._testeditfile({'do_functionname()': (['othercommand_one a b c', 'othercommand_two d e f'], None, 4, False),
284                        'MULTILINE2': (['f1', 'f2'], None, '\t', True),
285                        'MULTILINE': (['e1', 'e2', 'e3'], None, -1, True),
286                        'THIS': (None, None, 0, False),
287                        'OTHER': ('yetanothervalue', None, 0, True)}, newfile3)
288
289
290    def test_edit_metadata_file_4(self):
291
292        newfile4 = """
293# A comment
294HELLO = "oldvalue"
295
296THIS = "that"
297
298# Another comment
299OTHER = 'anothervalue'
300
301MULTILINE = "a1 \\
302             a2 \\
303             a3"
304
305MULTILINE2 := " \\
306               b1 \\
307               b2 \\
308               b3 \\
309               "
310
311
312"""
313
314        self._testeditfile({'NOCHANGE': (None, None, 0, False),
315                        'MULTILINE3': (None, None, 0, False),
316                        'THIS': ('that', None, 0, False),
317                        'do_functionname()': (None, None, 0, False)}, newfile4)
318
319
320    def test_edit_metadata(self):
321        newfile5 = """
322# A comment
323HELLO = "hithere"
324
325# A new comment
326THIS += "that"
327
328# Another comment
329NOCHANGE = "samevalue"
330OTHER = 'anothervalue'
331
332MULTILINE = "a1 \\
333             a2 \\
334             a3"
335
336MULTILINE2 := " \\
337               b1 \\
338               b2 \\
339               b3 \\
340               "
341
342
343MULTILINE3 = " \\
344              c1 \\
345              c2 \\
346              c3 \\
347"
348
349NEWVAR = "value"
350
351do_functionname() {
352    command1 ${VAL1} ${VAL2}
353    command2 ${VAL3} ${VAL4}
354}
355"""
356
357
358        def handle_var(varname, origvalue, op, newlines):
359            if varname == 'THIS':
360                newlines.append('# A new comment\n')
361            elif varname == 'do_functionname()':
362                newlines.append('NEWVAR = "value"\n')
363                newlines.append('\n')
364            valueitem = varvalues.get(varname, None)
365            if valueitem:
366                return valueitem
367            else:
368                return (origvalue, op, 0, True)
369
370        varvalues = {'HELLO': ('hithere', None, 0, True), 'THIS': ('that', '+=', 0, True)}
371        varlist = ['HELLO', 'THIS', 'do_functionname()']
372        (updated, newlines) = bb.utils.edit_metadata(self._origfile.splitlines(True), varlist, handle_var)
373        self.assertTrue(updated, 'List should be updated but isn\'t')
374        self.assertEqual(newlines, newfile5.splitlines(True))
375
376    # Make sure the orig value matches what we expect it to be
377    def test_edit_metadata_origvalue(self):
378        origfile = """
379MULTILINE = "  stuff \\
380    morestuff"
381"""
382        expected_value = "stuff morestuff"
383        global value_in_callback
384        value_in_callback = ""
385
386        def handle_var(varname, origvalue, op, newlines):
387            global value_in_callback
388            value_in_callback = origvalue
389            return (origvalue, op, -1, False)
390
391        bb.utils.edit_metadata(origfile.splitlines(True),
392                               ['MULTILINE'],
393                               handle_var)
394
395        testvalue = re.sub('\s+', ' ', value_in_callback.strip())
396        self.assertEqual(expected_value, testvalue)
397
398class EditBbLayersConf(unittest.TestCase):
399
400    def _test_bblayers_edit(self, before, after, add, remove, notadded, notremoved):
401        with tempfile.NamedTemporaryFile('w', delete=False) as tf:
402            tf.write(before)
403            tf.close()
404            try:
405                actual_notadded, actual_notremoved = bb.utils.edit_bblayers_conf(tf.name, add, remove)
406                with open(tf.name) as f:
407                    actual_after = f.readlines()
408                self.assertEqual(after.splitlines(True), actual_after)
409                self.assertEqual(notadded, actual_notadded)
410                self.assertEqual(notremoved, actual_notremoved)
411            finally:
412                os.remove(tf.name)
413
414
415    def test_bblayers_remove(self):
416        before = r"""
417# A comment
418
419BBPATH = "${TOPDIR}"
420BBFILES ?= ""
421BBLAYERS = " \
422  /home/user/path/layer1 \
423  /home/user/path/layer2 \
424  /home/user/path/subpath/layer3 \
425  /home/user/path/layer4 \
426  "
427"""
428        after = r"""
429# A comment
430
431BBPATH = "${TOPDIR}"
432BBFILES ?= ""
433BBLAYERS = " \
434  /home/user/path/layer1 \
435  /home/user/path/subpath/layer3 \
436  /home/user/path/layer4 \
437  "
438"""
439        self._test_bblayers_edit(before, after,
440                                 None,
441                                 '/home/user/path/layer2',
442                                 [],
443                                 [])
444
445
446    def test_bblayers_add(self):
447        before = r"""
448# A comment
449
450BBPATH = "${TOPDIR}"
451BBFILES ?= ""
452BBLAYERS = " \
453  /home/user/path/layer1 \
454  /home/user/path/layer2 \
455  /home/user/path/subpath/layer3 \
456  /home/user/path/layer4 \
457  "
458"""
459        after = r"""
460# A comment
461
462BBPATH = "${TOPDIR}"
463BBFILES ?= ""
464BBLAYERS = " \
465  /home/user/path/layer1 \
466  /home/user/path/layer2 \
467  /home/user/path/subpath/layer3 \
468  /home/user/path/layer4 \
469  /other/path/to/layer5 \
470  "
471"""
472        self._test_bblayers_edit(before, after,
473                                 '/other/path/to/layer5/',
474                                 None,
475                                 [],
476                                 [])
477
478
479    def test_bblayers_add_remove(self):
480        before = r"""
481# A comment
482
483BBPATH = "${TOPDIR}"
484BBFILES ?= ""
485BBLAYERS = " \
486  /home/user/path/layer1 \
487  /home/user/path/layer2 \
488  /home/user/path/subpath/layer3 \
489  /home/user/path/layer4 \
490  "
491"""
492        after = r"""
493# A comment
494
495BBPATH = "${TOPDIR}"
496BBFILES ?= ""
497BBLAYERS = " \
498  /home/user/path/layer1 \
499  /home/user/path/layer2 \
500  /home/user/path/layer4 \
501  /other/path/to/layer5 \
502  "
503"""
504        self._test_bblayers_edit(before, after,
505                                 ['/other/path/to/layer5', '/home/user/path/layer2/'], '/home/user/path/subpath/layer3/',
506                                 ['/home/user/path/layer2'],
507                                 [])
508
509
510    def test_bblayers_add_remove_home(self):
511        before = r"""
512# A comment
513
514BBPATH = "${TOPDIR}"
515BBFILES ?= ""
516BBLAYERS = " \
517  ~/path/layer1 \
518  ~/path/layer2 \
519  ~/otherpath/layer3 \
520  ~/path/layer4 \
521  "
522"""
523        after = r"""
524# A comment
525
526BBPATH = "${TOPDIR}"
527BBFILES ?= ""
528BBLAYERS = " \
529  ~/path/layer2 \
530  ~/path/layer4 \
531  ~/path2/layer5 \
532  "
533"""
534        self._test_bblayers_edit(before, after,
535                                 [os.environ['HOME'] + '/path/layer4', '~/path2/layer5'],
536                                 [os.environ['HOME'] + '/otherpath/layer3', '~/path/layer1', '~/path/notinlist'],
537                                 [os.environ['HOME'] + '/path/layer4'],
538                                 ['~/path/notinlist'])
539
540
541    def test_bblayers_add_remove_plusequals(self):
542        before = r"""
543# A comment
544
545BBPATH = "${TOPDIR}"
546BBFILES ?= ""
547BBLAYERS += " \
548  /home/user/path/layer1 \
549  /home/user/path/layer2 \
550  "
551"""
552        after = r"""
553# A comment
554
555BBPATH = "${TOPDIR}"
556BBFILES ?= ""
557BBLAYERS += " \
558  /home/user/path/layer2 \
559  /home/user/path/layer3 \
560  "
561"""
562        self._test_bblayers_edit(before, after,
563                                 '/home/user/path/layer3',
564                                 '/home/user/path/layer1',
565                                 [],
566                                 [])
567
568
569    def test_bblayers_add_remove_plusequals2(self):
570        before = r"""
571# A comment
572
573BBPATH = "${TOPDIR}"
574BBFILES ?= ""
575BBLAYERS += " \
576  /home/user/path/layer1 \
577  /home/user/path/layer2 \
578  /home/user/path/layer3 \
579  "
580BBLAYERS += "/home/user/path/layer4"
581BBLAYERS += "/home/user/path/layer5"
582"""
583        after = r"""
584# A comment
585
586BBPATH = "${TOPDIR}"
587BBFILES ?= ""
588BBLAYERS += " \
589  /home/user/path/layer2 \
590  /home/user/path/layer3 \
591  "
592BBLAYERS += "/home/user/path/layer5"
593BBLAYERS += "/home/user/otherpath/layer6"
594"""
595        self._test_bblayers_edit(before, after,
596                                 ['/home/user/otherpath/layer6', '/home/user/path/layer3'], ['/home/user/path/layer1', '/home/user/path/layer4', '/home/user/path/layer7'],
597                                 ['/home/user/path/layer3'],
598                                 ['/home/user/path/layer7'])
599