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