utils.py 16 KB

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