# ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- # # BitBake Tests for utils.py # # Copyright (C) 2012 Richard Purdie # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # import unittest import bb import os import tempfile class VerCmpString(unittest.TestCase): def test_vercmpstring(self): result = bb.utils.vercmp_string('1', '2') self.assertTrue(result < 0) result = bb.utils.vercmp_string('2', '1') self.assertTrue(result > 0) result = bb.utils.vercmp_string('1', '1.0') self.assertTrue(result < 0) result = bb.utils.vercmp_string('1', '1.1') self.assertTrue(result < 0) result = bb.utils.vercmp_string('1.1', '1_p2') self.assertTrue(result < 0) result = bb.utils.vercmp_string('1.0', '1.0+1.1-beta1') self.assertTrue(result < 0) result = bb.utils.vercmp_string('1.1', '1.0+1.1-beta1') self.assertTrue(result > 0) def test_explode_dep_versions(self): correctresult = {"foo" : ["= 1.10"]} result = bb.utils.explode_dep_versions2("foo (= 1.10)") self.assertEqual(result, correctresult) result = bb.utils.explode_dep_versions2("foo (=1.10)") self.assertEqual(result, correctresult) result = bb.utils.explode_dep_versions2("foo ( = 1.10)") self.assertEqual(result, correctresult) result = bb.utils.explode_dep_versions2("foo ( =1.10)") self.assertEqual(result, correctresult) result = bb.utils.explode_dep_versions2("foo ( = 1.10 )") self.assertEqual(result, correctresult) result = bb.utils.explode_dep_versions2("foo ( =1.10 )") self.assertEqual(result, correctresult) def test_vercmp_string_op(self): compareops = [('1', '1', '=', True), ('1', '1', '==', True), ('1', '1', '!=', False), ('1', '1', '>', False), ('1', '1', '<', False), ('1', '1', '>=', True), ('1', '1', '<=', True), ('1', '0', '=', False), ('1', '0', '==', False), ('1', '0', '!=', True), ('1', '0', '>', True), ('1', '0', '<', False), ('1', '0', '>>', True), ('1', '0', '<<', False), ('1', '0', '>=', True), ('1', '0', '<=', False), ('0', '1', '=', False), ('0', '1', '==', False), ('0', '1', '!=', True), ('0', '1', '>', False), ('0', '1', '<', True), ('0', '1', '>>', False), ('0', '1', '<<', True), ('0', '1', '>=', False), ('0', '1', '<=', True)] for arg1, arg2, op, correctresult in compareops: result = bb.utils.vercmp_string_op(arg1, arg2, op) self.assertEqual(result, correctresult, 'vercmp_string_op("%s", "%s", "%s") != %s' % (arg1, arg2, op, correctresult)) # Check that clearly invalid operator raises an exception self.assertRaises(bb.utils.VersionStringException, bb.utils.vercmp_string_op, '0', '0', '$') class Path(unittest.TestCase): def test_unsafe_delete_path(self): checkitems = [('/', True), ('//', True), ('///', True), (os.getcwd().count(os.sep) * ('..' + os.sep), True), (os.environ.get('HOME', '/home/test'), True), ('/home/someone', True), ('/home/other/', True), ('/home/other/subdir', False), ('', False)] for arg1, correctresult in checkitems: result = bb.utils._check_unsafe_delete_path(arg1) self.assertEqual(result, correctresult, '_check_unsafe_delete_path("%s") != %s' % (arg1, correctresult)) class EditMetadataFile(unittest.TestCase): _origfile = """ # A comment HELLO = "oldvalue" THIS = "that" # Another comment NOCHANGE = "samevalue" OTHER = 'anothervalue' MULTILINE = "a1 \\ a2 \\ a3" MULTILINE2 := " \\ b1 \\ b2 \\ b3 \\ " MULTILINE3 = " \\ c1 \\ c2 \\ c3 \\ " do_functionname() { command1 ${VAL1} ${VAL2} command2 ${VAL3} ${VAL4} } """ def _testeditfile(self, varvalues, compareto, dummyvars=None): if dummyvars is None: dummyvars = [] with tempfile.NamedTemporaryFile('w', delete=False) as tf: tf.write(self._origfile) tf.close() try: varcalls = [] def handle_file(varname, origvalue, op, newlines): self.assertIn(varname, varvalues, 'Callback called for variable %s not in the list!' % varname) self.assertNotIn(varname, dummyvars, 'Callback called for variable %s in dummy list!' % varname) varcalls.append(varname) return varvalues[varname] bb.utils.edit_metadata_file(tf.name, varvalues.keys(), handle_file) with open(tf.name) as f: modfile = f.readlines() # Ensure the output matches the expected output self.assertEqual(compareto.splitlines(True), modfile) # Ensure the callback function was called for every variable we asked for # (plus allow testing behaviour when a requested variable is not present) self.assertEqual(sorted(varvalues.keys()), sorted(varcalls + dummyvars)) finally: os.remove(tf.name) def test_edit_metadata_file_nochange(self): # Test file doesn't get modified with nothing to do self._testeditfile({}, self._origfile) # Test file doesn't get modified with only dummy variables self._testeditfile({'DUMMY1': ('should_not_set', None, 0, True), 'DUMMY2': ('should_not_set_again', None, 0, True)}, self._origfile, dummyvars=['DUMMY1', 'DUMMY2']) # Test file doesn't get modified with some the same values self._testeditfile({'THIS': ('that', None, 0, True), 'OTHER': ('anothervalue', None, 0, True), 'MULTILINE3': (' c1 c2 c3', None, 4, False)}, self._origfile) def test_edit_metadata_file_1(self): newfile1 = """ # A comment HELLO = "newvalue" THIS = "that" # Another comment NOCHANGE = "samevalue" OTHER = 'anothervalue' MULTILINE = "a1 \\ a2 \\ a3" MULTILINE2 := " \\ b1 \\ b2 \\ b3 \\ " MULTILINE3 = " \\ c1 \\ c2 \\ c3 \\ " do_functionname() { command1 ${VAL1} ${VAL2} command2 ${VAL3} ${VAL4} } """ self._testeditfile({'HELLO': ('newvalue', None, 4, True)}, newfile1) def test_edit_metadata_file_2(self): newfile2 = """ # A comment HELLO = "oldvalue" THIS = "that" # Another comment NOCHANGE = "samevalue" OTHER = 'anothervalue' MULTILINE = " \\ d1 \\ d2 \\ d3 \\ " MULTILINE2 := " \\ b1 \\ b2 \\ b3 \\ " MULTILINE3 = "nowsingle" do_functionname() { command1 ${VAL1} ${VAL2} command2 ${VAL3} ${VAL4} } """ self._testeditfile({'MULTILINE': (['d1','d2','d3'], None, 4, False), 'MULTILINE3': ('nowsingle', None, 4, True), 'NOTPRESENT': (['a', 'b'], None, 4, False)}, newfile2, dummyvars=['NOTPRESENT']) def test_edit_metadata_file_3(self): newfile3 = """ # A comment HELLO = "oldvalue" # Another comment NOCHANGE = "samevalue" OTHER = "yetanothervalue" MULTILINE = "e1 \\ e2 \\ e3 \\ " MULTILINE2 := "f1 \\ \tf2 \\ \t" MULTILINE3 = " \\ c1 \\ c2 \\ c3 \\ " do_functionname() { othercommand_one a b c othercommand_two d e f } """ self._testeditfile({'do_functionname()': (['othercommand_one a b c', 'othercommand_two d e f'], None, 4, False), 'MULTILINE2': (['f1', 'f2'], None, '\t', True), 'MULTILINE': (['e1', 'e2', 'e3'], None, -1, True), 'THIS': (None, None, 0, False), 'OTHER': ('yetanothervalue', None, 0, True)}, newfile3) def test_edit_metadata_file_4(self): newfile4 = """ # A comment HELLO = "oldvalue" THIS = "that" # Another comment OTHER = 'anothervalue' MULTILINE = "a1 \\ a2 \\ a3" MULTILINE2 := " \\ b1 \\ b2 \\ b3 \\ " """ self._testeditfile({'NOCHANGE': (None, None, 0, False), 'MULTILINE3': (None, None, 0, False), 'THIS': ('that', None, 0, False), 'do_functionname()': (None, None, 0, False)}, newfile4) def test_edit_metadata(self): newfile5 = """ # A comment HELLO = "hithere" # A new comment THIS += "that" # Another comment NOCHANGE = "samevalue" OTHER = 'anothervalue' MULTILINE = "a1 \\ a2 \\ a3" MULTILINE2 := " \\ b1 \\ b2 \\ b3 \\ " MULTILINE3 = " \\ c1 \\ c2 \\ c3 \\ " NEWVAR = "value" do_functionname() { command1 ${VAL1} ${VAL2} command2 ${VAL3} ${VAL4} } """ def handle_var(varname, origvalue, op, newlines): if varname == 'THIS': newlines.append('# A new comment\n') elif varname == 'do_functionname()': newlines.append('NEWVAR = "value"\n') newlines.append('\n') valueitem = varvalues.get(varname, None) if valueitem: return valueitem else: return (origvalue, op, 0, True) varvalues = {'HELLO': ('hithere', None, 0, True), 'THIS': ('that', '+=', 0, True)} varlist = ['HELLO', 'THIS', 'do_functionname()'] (updated, newlines) = bb.utils.edit_metadata(self._origfile.splitlines(True), varlist, handle_var) self.assertTrue(updated, 'List should be updated but isn\'t') self.assertEqual(newlines, newfile5.splitlines(True)) class EditBbLayersConf(unittest.TestCase): def _test_bblayers_edit(self, before, after, add, remove, notadded, notremoved): with tempfile.NamedTemporaryFile('w', delete=False) as tf: tf.write(before) tf.close() try: actual_notadded, actual_notremoved = bb.utils.edit_bblayers_conf(tf.name, add, remove) with open(tf.name) as f: actual_after = f.readlines() self.assertEqual(after.splitlines(True), actual_after) self.assertEqual(notadded, actual_notadded) self.assertEqual(notremoved, actual_notremoved) finally: os.remove(tf.name) def test_bblayers_remove(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/subpath/layer3 \ /home/user/path/layer4 \ " """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/subpath/layer3 \ /home/user/path/layer4 \ " """ self._test_bblayers_edit(before, after, None, '/home/user/path/layer2', [], []) def test_bblayers_add(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/subpath/layer3 \ /home/user/path/layer4 \ " """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/subpath/layer3 \ /home/user/path/layer4 \ /other/path/to/layer5 \ " """ self._test_bblayers_edit(before, after, '/other/path/to/layer5/', None, [], []) def test_bblayers_add_remove(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/subpath/layer3 \ /home/user/path/layer4 \ " """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/layer4 \ /other/path/to/layer5 \ " """ self._test_bblayers_edit(before, after, ['/other/path/to/layer5', '/home/user/path/layer2/'], '/home/user/path/subpath/layer3/', ['/home/user/path/layer2'], []) def test_bblayers_add_remove_home(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ ~/path/layer1 \ ~/path/layer2 \ ~/otherpath/layer3 \ ~/path/layer4 \ " """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS = " \ ~/path/layer2 \ ~/path/layer4 \ ~/path2/layer5 \ " """ self._test_bblayers_edit(before, after, [os.environ['HOME'] + '/path/layer4', '~/path2/layer5'], [os.environ['HOME'] + '/otherpath/layer3', '~/path/layer1', '~/path/notinlist'], [os.environ['HOME'] + '/path/layer4'], ['~/path/notinlist']) def test_bblayers_add_remove_plusequals(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS += " \ /home/user/path/layer1 \ /home/user/path/layer2 \ " """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS += " \ /home/user/path/layer2 \ /home/user/path/layer3 \ " """ self._test_bblayers_edit(before, after, '/home/user/path/layer3', '/home/user/path/layer1', [], []) def test_bblayers_add_remove_plusequals2(self): before = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS += " \ /home/user/path/layer1 \ /home/user/path/layer2 \ /home/user/path/layer3 \ " BBLAYERS += "/home/user/path/layer4" BBLAYERS += "/home/user/path/layer5" """ after = r""" # A comment BBPATH = "${TOPDIR}" BBFILES ?= "" BBLAYERS += " \ /home/user/path/layer2 \ /home/user/path/layer3 \ " BBLAYERS += "/home/user/path/layer5" BBLAYERS += "/home/user/otherpath/layer6" """ self._test_bblayers_edit(before, after, ['/home/user/otherpath/layer6', '/home/user/path/layer3'], ['/home/user/path/layer1', '/home/user/path/layer4', '/home/user/path/layer7'], ['/home/user/path/layer3'], ['/home/user/path/layer7'])