blob: c363f62d7d03b89ba69ae3c3660273f11247bb11 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#
2# BitBake Tests for utils.py
3#
4# Copyright (C) 2012 Richard Purdie
5#
Brad Bishopc342db32019-05-15 21:57:59 -04006# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007#
8
9import unittest
10import bb
11import os
12import tempfile
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050013import re
Patrick Williamsc124f4f2015-09-15 14:41:29 -050014
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)
Brad Bishopf3fd2882019-06-21 08:06:37 -040032 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)
Brad Bishop19323692019-04-05 15:28:33 -040036 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)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040
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
Brad Bishop6dbb3162019-11-25 09:41:34 -0500106class Checksum(unittest.TestCase):
107 filler = b"Shiver me timbers square-rigged spike Gold Road galleon bilge water boatswain wherry jack pirate. Mizzenmast rum lad Privateer jack salmagundi hang the jib piracy Pieces of Eight Corsair. Parrel marooned black spot yawl provost quarterdeck cable no prey, no pay spirits lateen sail."
108
109 def test_md5(self):
110 import hashlib
111 with tempfile.NamedTemporaryFile() as f:
112 f.write(self.filler)
113 f.flush()
114 checksum = bb.utils.md5_file(f.name)
115 self.assertEqual(checksum, "bd572cd5de30a785f4efcb6eaf5089e3")
116
117 def test_sha1(self):
118 import hashlib
119 with tempfile.NamedTemporaryFile() as f:
120 f.write(self.filler)
121 f.flush()
122 checksum = bb.utils.sha1_file(f.name)
123 self.assertEqual(checksum, "249eb8fd654732ea836d5e702d7aa567898eca71")
124
125 def test_sha256(self):
126 import hashlib
127 with tempfile.NamedTemporaryFile() as f:
128 f.write(self.filler)
129 f.flush()
130 checksum = bb.utils.sha256_file(f.name)
131 self.assertEqual(checksum, "fcfbae8bf6b721dbb9d2dc6a9334a58f2031a9a9b302999243f99da4d7f12d0f")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500132
133class EditMetadataFile(unittest.TestCase):
134 _origfile = """
135# A comment
136HELLO = "oldvalue"
137
138THIS = "that"
139
140# Another comment
141NOCHANGE = "samevalue"
142OTHER = 'anothervalue'
143
144MULTILINE = "a1 \\
145 a2 \\
146 a3"
147
148MULTILINE2 := " \\
149 b1 \\
150 b2 \\
151 b3 \\
152 "
153
154
155MULTILINE3 = " \\
156 c1 \\
157 c2 \\
158 c3 \\
159"
160
161do_functionname() {
162 command1 ${VAL1} ${VAL2}
163 command2 ${VAL3} ${VAL4}
164}
165"""
166 def _testeditfile(self, varvalues, compareto, dummyvars=None):
167 if dummyvars is None:
168 dummyvars = []
169 with tempfile.NamedTemporaryFile('w', delete=False) as tf:
170 tf.write(self._origfile)
171 tf.close()
172 try:
173 varcalls = []
174 def handle_file(varname, origvalue, op, newlines):
175 self.assertIn(varname, varvalues, 'Callback called for variable %s not in the list!' % varname)
176 self.assertNotIn(varname, dummyvars, 'Callback called for variable %s in dummy list!' % varname)
177 varcalls.append(varname)
178 return varvalues[varname]
179
180 bb.utils.edit_metadata_file(tf.name, varvalues.keys(), handle_file)
181 with open(tf.name) as f:
182 modfile = f.readlines()
183 # Ensure the output matches the expected output
184 self.assertEqual(compareto.splitlines(True), modfile)
185 # Ensure the callback function was called for every variable we asked for
186 # (plus allow testing behaviour when a requested variable is not present)
187 self.assertEqual(sorted(varvalues.keys()), sorted(varcalls + dummyvars))
188 finally:
189 os.remove(tf.name)
190
191
192 def test_edit_metadata_file_nochange(self):
193 # Test file doesn't get modified with nothing to do
194 self._testeditfile({}, self._origfile)
195 # Test file doesn't get modified with only dummy variables
196 self._testeditfile({'DUMMY1': ('should_not_set', None, 0, True),
197 'DUMMY2': ('should_not_set_again', None, 0, True)}, self._origfile, dummyvars=['DUMMY1', 'DUMMY2'])
198 # Test file doesn't get modified with some the same values
199 self._testeditfile({'THIS': ('that', None, 0, True),
200 'OTHER': ('anothervalue', None, 0, True),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500201 'MULTILINE3': (' c1 c2 c3 ', None, 4, False)}, self._origfile)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500202
203 def test_edit_metadata_file_1(self):
204
205 newfile1 = """
206# A comment
207HELLO = "newvalue"
208
209THIS = "that"
210
211# Another comment
212NOCHANGE = "samevalue"
213OTHER = 'anothervalue'
214
215MULTILINE = "a1 \\
216 a2 \\
217 a3"
218
219MULTILINE2 := " \\
220 b1 \\
221 b2 \\
222 b3 \\
223 "
224
225
226MULTILINE3 = " \\
227 c1 \\
228 c2 \\
229 c3 \\
230"
231
232do_functionname() {
233 command1 ${VAL1} ${VAL2}
234 command2 ${VAL3} ${VAL4}
235}
236"""
237 self._testeditfile({'HELLO': ('newvalue', None, 4, True)}, newfile1)
238
239
240 def test_edit_metadata_file_2(self):
241
242 newfile2 = """
243# A comment
244HELLO = "oldvalue"
245
246THIS = "that"
247
248# Another comment
249NOCHANGE = "samevalue"
250OTHER = 'anothervalue'
251
252MULTILINE = " \\
253 d1 \\
254 d2 \\
255 d3 \\
256 "
257
258MULTILINE2 := " \\
259 b1 \\
260 b2 \\
261 b3 \\
262 "
263
264
265MULTILINE3 = "nowsingle"
266
267do_functionname() {
268 command1 ${VAL1} ${VAL2}
269 command2 ${VAL3} ${VAL4}
270}
271"""
272 self._testeditfile({'MULTILINE': (['d1','d2','d3'], None, 4, False),
273 'MULTILINE3': ('nowsingle', None, 4, True),
274 'NOTPRESENT': (['a', 'b'], None, 4, False)}, newfile2, dummyvars=['NOTPRESENT'])
275
276
277 def test_edit_metadata_file_3(self):
278
279 newfile3 = """
280# A comment
281HELLO = "oldvalue"
282
283# Another comment
284NOCHANGE = "samevalue"
285OTHER = "yetanothervalue"
286
287MULTILINE = "e1 \\
288 e2 \\
289 e3 \\
290 "
291
292MULTILINE2 := "f1 \\
293\tf2 \\
294\t"
295
296
297MULTILINE3 = " \\
298 c1 \\
299 c2 \\
300 c3 \\
301"
302
303do_functionname() {
304 othercommand_one a b c
305 othercommand_two d e f
306}
307"""
308
309 self._testeditfile({'do_functionname()': (['othercommand_one a b c', 'othercommand_two d e f'], None, 4, False),
310 'MULTILINE2': (['f1', 'f2'], None, '\t', True),
311 'MULTILINE': (['e1', 'e2', 'e3'], None, -1, True),
312 'THIS': (None, None, 0, False),
313 'OTHER': ('yetanothervalue', None, 0, True)}, newfile3)
314
315
316 def test_edit_metadata_file_4(self):
317
318 newfile4 = """
319# A comment
320HELLO = "oldvalue"
321
322THIS = "that"
323
324# Another comment
325OTHER = 'anothervalue'
326
327MULTILINE = "a1 \\
328 a2 \\
329 a3"
330
331MULTILINE2 := " \\
332 b1 \\
333 b2 \\
334 b3 \\
335 "
336
337
338"""
339
340 self._testeditfile({'NOCHANGE': (None, None, 0, False),
341 'MULTILINE3': (None, None, 0, False),
342 'THIS': ('that', None, 0, False),
343 'do_functionname()': (None, None, 0, False)}, newfile4)
344
345
346 def test_edit_metadata(self):
347 newfile5 = """
348# A comment
349HELLO = "hithere"
350
351# A new comment
352THIS += "that"
353
354# Another comment
355NOCHANGE = "samevalue"
356OTHER = 'anothervalue'
357
358MULTILINE = "a1 \\
359 a2 \\
360 a3"
361
362MULTILINE2 := " \\
363 b1 \\
364 b2 \\
365 b3 \\
366 "
367
368
369MULTILINE3 = " \\
370 c1 \\
371 c2 \\
372 c3 \\
373"
374
375NEWVAR = "value"
376
377do_functionname() {
378 command1 ${VAL1} ${VAL2}
379 command2 ${VAL3} ${VAL4}
380}
381"""
382
383
384 def handle_var(varname, origvalue, op, newlines):
385 if varname == 'THIS':
386 newlines.append('# A new comment\n')
387 elif varname == 'do_functionname()':
388 newlines.append('NEWVAR = "value"\n')
389 newlines.append('\n')
390 valueitem = varvalues.get(varname, None)
391 if valueitem:
392 return valueitem
393 else:
394 return (origvalue, op, 0, True)
395
396 varvalues = {'HELLO': ('hithere', None, 0, True), 'THIS': ('that', '+=', 0, True)}
397 varlist = ['HELLO', 'THIS', 'do_functionname()']
398 (updated, newlines) = bb.utils.edit_metadata(self._origfile.splitlines(True), varlist, handle_var)
399 self.assertTrue(updated, 'List should be updated but isn\'t')
400 self.assertEqual(newlines, newfile5.splitlines(True))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500401
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500402 # Make sure the orig value matches what we expect it to be
403 def test_edit_metadata_origvalue(self):
404 origfile = """
405MULTILINE = " stuff \\
406 morestuff"
407"""
408 expected_value = "stuff morestuff"
409 global value_in_callback
410 value_in_callback = ""
411
412 def handle_var(varname, origvalue, op, newlines):
413 global value_in_callback
414 value_in_callback = origvalue
415 return (origvalue, op, -1, False)
416
417 bb.utils.edit_metadata(origfile.splitlines(True),
418 ['MULTILINE'],
419 handle_var)
420
Andrew Geisslereff27472021-10-29 15:35:00 -0500421 testvalue = re.sub(r'\s+', ' ', value_in_callback.strip())
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500422 self.assertEqual(expected_value, testvalue)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500423
424class EditBbLayersConf(unittest.TestCase):
425
426 def _test_bblayers_edit(self, before, after, add, remove, notadded, notremoved):
427 with tempfile.NamedTemporaryFile('w', delete=False) as tf:
428 tf.write(before)
429 tf.close()
430 try:
431 actual_notadded, actual_notremoved = bb.utils.edit_bblayers_conf(tf.name, add, remove)
432 with open(tf.name) as f:
433 actual_after = f.readlines()
434 self.assertEqual(after.splitlines(True), actual_after)
435 self.assertEqual(notadded, actual_notadded)
436 self.assertEqual(notremoved, actual_notremoved)
437 finally:
438 os.remove(tf.name)
439
440
441 def test_bblayers_remove(self):
442 before = r"""
443# A comment
444
445BBPATH = "${TOPDIR}"
446BBFILES ?= ""
447BBLAYERS = " \
448 /home/user/path/layer1 \
449 /home/user/path/layer2 \
450 /home/user/path/subpath/layer3 \
451 /home/user/path/layer4 \
452 "
453"""
454 after = r"""
455# A comment
456
457BBPATH = "${TOPDIR}"
458BBFILES ?= ""
459BBLAYERS = " \
460 /home/user/path/layer1 \
461 /home/user/path/subpath/layer3 \
462 /home/user/path/layer4 \
463 "
464"""
465 self._test_bblayers_edit(before, after,
466 None,
467 '/home/user/path/layer2',
468 [],
469 [])
470
471
472 def test_bblayers_add(self):
473 before = r"""
474# A comment
475
476BBPATH = "${TOPDIR}"
477BBFILES ?= ""
478BBLAYERS = " \
479 /home/user/path/layer1 \
480 /home/user/path/layer2 \
481 /home/user/path/subpath/layer3 \
482 /home/user/path/layer4 \
483 "
484"""
485 after = r"""
486# A comment
487
488BBPATH = "${TOPDIR}"
489BBFILES ?= ""
490BBLAYERS = " \
491 /home/user/path/layer1 \
492 /home/user/path/layer2 \
493 /home/user/path/subpath/layer3 \
494 /home/user/path/layer4 \
495 /other/path/to/layer5 \
496 "
497"""
498 self._test_bblayers_edit(before, after,
499 '/other/path/to/layer5/',
500 None,
501 [],
502 [])
503
504
505 def test_bblayers_add_remove(self):
506 before = r"""
507# A comment
508
509BBPATH = "${TOPDIR}"
510BBFILES ?= ""
511BBLAYERS = " \
512 /home/user/path/layer1 \
513 /home/user/path/layer2 \
514 /home/user/path/subpath/layer3 \
515 /home/user/path/layer4 \
516 "
517"""
518 after = r"""
519# A comment
520
521BBPATH = "${TOPDIR}"
522BBFILES ?= ""
523BBLAYERS = " \
524 /home/user/path/layer1 \
525 /home/user/path/layer2 \
526 /home/user/path/layer4 \
527 /other/path/to/layer5 \
528 "
529"""
530 self._test_bblayers_edit(before, after,
531 ['/other/path/to/layer5', '/home/user/path/layer2/'], '/home/user/path/subpath/layer3/',
532 ['/home/user/path/layer2'],
533 [])
534
535
536 def test_bblayers_add_remove_home(self):
537 before = r"""
538# A comment
539
540BBPATH = "${TOPDIR}"
541BBFILES ?= ""
542BBLAYERS = " \
543 ~/path/layer1 \
544 ~/path/layer2 \
545 ~/otherpath/layer3 \
546 ~/path/layer4 \
547 "
548"""
549 after = r"""
550# A comment
551
552BBPATH = "${TOPDIR}"
553BBFILES ?= ""
554BBLAYERS = " \
555 ~/path/layer2 \
556 ~/path/layer4 \
557 ~/path2/layer5 \
558 "
559"""
560 self._test_bblayers_edit(before, after,
561 [os.environ['HOME'] + '/path/layer4', '~/path2/layer5'],
562 [os.environ['HOME'] + '/otherpath/layer3', '~/path/layer1', '~/path/notinlist'],
563 [os.environ['HOME'] + '/path/layer4'],
564 ['~/path/notinlist'])
565
566
567 def test_bblayers_add_remove_plusequals(self):
568 before = r"""
569# A comment
570
571BBPATH = "${TOPDIR}"
572BBFILES ?= ""
573BBLAYERS += " \
574 /home/user/path/layer1 \
575 /home/user/path/layer2 \
576 "
577"""
578 after = r"""
579# A comment
580
581BBPATH = "${TOPDIR}"
582BBFILES ?= ""
583BBLAYERS += " \
584 /home/user/path/layer2 \
585 /home/user/path/layer3 \
586 "
587"""
588 self._test_bblayers_edit(before, after,
589 '/home/user/path/layer3',
590 '/home/user/path/layer1',
591 [],
592 [])
593
594
595 def test_bblayers_add_remove_plusequals2(self):
596 before = r"""
597# A comment
598
599BBPATH = "${TOPDIR}"
600BBFILES ?= ""
601BBLAYERS += " \
602 /home/user/path/layer1 \
603 /home/user/path/layer2 \
604 /home/user/path/layer3 \
605 "
606BBLAYERS += "/home/user/path/layer4"
607BBLAYERS += "/home/user/path/layer5"
608"""
609 after = r"""
610# A comment
611
612BBPATH = "${TOPDIR}"
613BBFILES ?= ""
614BBLAYERS += " \
615 /home/user/path/layer2 \
616 /home/user/path/layer3 \
617 "
618BBLAYERS += "/home/user/path/layer5"
619BBLAYERS += "/home/user/otherpath/layer6"
620"""
621 self._test_bblayers_edit(before, after,
622 ['/home/user/otherpath/layer6', '/home/user/path/layer3'], ['/home/user/path/layer1', '/home/user/path/layer4', '/home/user/path/layer7'],
623 ['/home/user/path/layer3'],
624 ['/home/user/path/layer7'])
Andrew Geissler82c905d2020-04-13 13:39:40 -0500625
626
627class GetReferencedVars(unittest.TestCase):
628 def setUp(self):
629 self.d = bb.data.init()
630
631 def check_referenced(self, expression, expected_layers):
632 vars = bb.utils.get_referenced_vars(expression, self.d)
633
634 # Do the easy check first - is every variable accounted for?
635 expected_vars = set.union(set(), *expected_layers)
636 got_vars = set(vars)
637 self.assertSetEqual(got_vars, expected_vars)
638
639 # Now test the order of the layers
640 start = 0
641 for i, expected_layer in enumerate(expected_layers):
642 got_layer = set(vars[start:len(expected_layer)+start])
643 start += len(expected_layer)
644 self.assertSetEqual(got_layer, expected_layer)
645
646 def test_no_vars(self):
647 self.check_referenced("", [])
648 self.check_referenced(" ", [])
649 self.check_referenced(" no vars here! ", [])
650
651 def test_single_layer(self):
652 self.check_referenced("${VAR}", [{"VAR"}])
653 self.check_referenced("${VAR} ${VAR}", [{"VAR"}])
654
655 def test_two_layer(self):
656 self.d.setVar("VAR", "${B}")
657 self.check_referenced("${VAR}", [{"VAR"}, {"B"}])
658 self.check_referenced("${@d.getVar('VAR')}", [{"VAR"}, {"B"}])
659
660 def test_more_complicated(self):
661 self.d["SRC_URI"] = "${QT_GIT}/${QT_MODULE}.git;name=${QT_MODULE};${QT_MODULE_BRANCH_PARAM};protocol=${QT_GIT_PROTOCOL}"
662 self.d["QT_GIT"] = "git://code.qt.io/${QT_GIT_PROJECT}"
663 self.d["QT_MODULE_BRANCH_PARAM"] = "branch=${QT_MODULE_BRANCH}"
664 self.d["QT_MODULE"] = "${BPN}"
665 self.d["BPN"] = "something to do with ${PN} and ${SPECIAL_PKGSUFFIX}"
666
667 layers = [{"SRC_URI"}, {"QT_GIT", "QT_MODULE", "QT_MODULE_BRANCH_PARAM", "QT_GIT_PROTOCOL"}, {"QT_GIT_PROJECT", "QT_MODULE_BRANCH", "BPN"}, {"PN", "SPECIAL_PKGSUFFIX"}]
668 self.check_referenced("${SRC_URI}", layers)
Patrick Williams0ca19cc2021-08-16 14:03:13 -0500669
670
671class EnvironmentTests(unittest.TestCase):
672 def test_environment(self):
673 os.environ["A"] = "this is A"
674 self.assertIn("A", os.environ)
675 self.assertEqual(os.environ["A"], "this is A")
676 self.assertNotIn("B", os.environ)
677
678 with bb.utils.environment(B="this is B"):
679 self.assertIn("A", os.environ)
680 self.assertEqual(os.environ["A"], "this is A")
681 self.assertIn("B", os.environ)
682 self.assertEqual(os.environ["B"], "this is B")
683
684 self.assertIn("A", os.environ)
685 self.assertEqual(os.environ["A"], "this is A")
686 self.assertNotIn("B", os.environ)