1# 2# BitBake Test for lib/bb/parse/ 3# 4# Copyright (C) 2015 Richard Purdie 5# 6# SPDX-License-Identifier: GPL-2.0-only 7# 8 9import unittest 10import tempfile 11import logging 12import bb 13import os 14 15logger = logging.getLogger('BitBake.TestParse') 16 17import bb.parse 18import bb.data 19import bb.siggen 20 21class ParseTest(unittest.TestCase): 22 23 testfile = """ 24A = "1" 25B = "2" 26do_install() { 27 echo "hello" 28} 29 30C = "3" 31""" 32 33 def setUp(self): 34 self.origdir = os.getcwd() 35 self.d = bb.data.init() 36 bb.parse.siggen = bb.siggen.init(self.d) 37 38 def tearDown(self): 39 os.chdir(self.origdir) 40 41 def parsehelper(self, content, suffix = ".bb"): 42 43 f = tempfile.NamedTemporaryFile(suffix = suffix) 44 f.write(bytes(content, "utf-8")) 45 f.flush() 46 os.chdir(os.path.dirname(f.name)) 47 return f 48 49 def test_parse_simple(self): 50 f = self.parsehelper(self.testfile) 51 d = bb.parse.handle(f.name, self.d)[''] 52 self.assertEqual(d.getVar("A"), "1") 53 self.assertEqual(d.getVar("B"), "2") 54 self.assertEqual(d.getVar("C"), "3") 55 56 def test_parse_incomplete_function(self): 57 testfileB = self.testfile.replace("}", "") 58 f = self.parsehelper(testfileB) 59 with self.assertRaises(bb.parse.ParseError): 60 d = bb.parse.handle(f.name, self.d)[''] 61 62 unsettest = """ 63A = "1" 64B = "2" 65B[flag] = "3" 66 67unset A 68unset B[flag] 69""" 70 71 def test_parse_unset(self): 72 f = self.parsehelper(self.unsettest) 73 d = bb.parse.handle(f.name, self.d)[''] 74 self.assertEqual(d.getVar("A"), None) 75 self.assertEqual(d.getVarFlag("A","flag"), None) 76 self.assertEqual(d.getVar("B"), "2") 77 78 exporttest = """ 79A = "a" 80export B = "b" 81export C 82exportD = "d" 83""" 84 85 def test_parse_exports(self): 86 f = self.parsehelper(self.exporttest) 87 d = bb.parse.handle(f.name, self.d)[''] 88 self.assertEqual(d.getVar("A"), "a") 89 self.assertIsNone(d.getVarFlag("A", "export")) 90 self.assertEqual(d.getVar("B"), "b") 91 self.assertEqual(d.getVarFlag("B", "export"), 1) 92 self.assertIsNone(d.getVar("C")) 93 self.assertEqual(d.getVarFlag("C", "export"), 1) 94 self.assertIsNone(d.getVar("D")) 95 self.assertIsNone(d.getVarFlag("D", "export")) 96 self.assertEqual(d.getVar("exportD"), "d") 97 self.assertIsNone(d.getVarFlag("exportD", "export")) 98 99 100 overridetest = """ 101RRECOMMENDS:${PN} = "a" 102RRECOMMENDS:${PN}:libc = "b" 103OVERRIDES = "libc:${PN}" 104PN = "gtk+" 105""" 106 107 def test_parse_overrides(self): 108 f = self.parsehelper(self.overridetest) 109 d = bb.parse.handle(f.name, self.d)[''] 110 self.assertEqual(d.getVar("RRECOMMENDS"), "b") 111 bb.data.expandKeys(d) 112 self.assertEqual(d.getVar("RRECOMMENDS"), "b") 113 d.setVar("RRECOMMENDS:gtk+", "c") 114 self.assertEqual(d.getVar("RRECOMMENDS"), "c") 115 116 overridetest2 = """ 117EXTRA_OECONF = "" 118EXTRA_OECONF:class-target = "b" 119EXTRA_OECONF:append = " c" 120""" 121 122 def test_parse_overrides2(self): 123 f = self.parsehelper(self.overridetest2) 124 d = bb.parse.handle(f.name, self.d)[''] 125 d.appendVar("EXTRA_OECONF", " d") 126 d.setVar("OVERRIDES", "class-target") 127 self.assertEqual(d.getVar("EXTRA_OECONF"), "b c d") 128 129 overridetest3 = """ 130DESCRIPTION = "A" 131DESCRIPTION:${PN}-dev = "${DESCRIPTION} B" 132PN = "bc" 133""" 134 135 def test_parse_combinations(self): 136 f = self.parsehelper(self.overridetest3) 137 d = bb.parse.handle(f.name, self.d)[''] 138 bb.data.expandKeys(d) 139 self.assertEqual(d.getVar("DESCRIPTION:bc-dev"), "A B") 140 d.setVar("DESCRIPTION", "E") 141 d.setVar("DESCRIPTION:bc-dev", "C D") 142 d.setVar("OVERRIDES", "bc-dev") 143 self.assertEqual(d.getVar("DESCRIPTION"), "C D") 144 145 146 classextend = """ 147VAR_var:override1 = "B" 148EXTRA = ":override1" 149OVERRIDES = "nothing${EXTRA}" 150 151BBCLASSEXTEND = "###CLASS###" 152""" 153 classextend_bbclass = """ 154EXTRA = "" 155python () { 156 d.renameVar("VAR_var", "VAR_var2") 157} 158""" 159 160 # 161 # Test based upon a real world data corruption issue. One 162 # data store changing a variable poked through into a different data 163 # store. This test case replicates that issue where the value 'B' would 164 # become unset/disappear. 165 # 166 def test_parse_classextend_contamination(self): 167 cls = self.parsehelper(self.classextend_bbclass, suffix=".bbclass") 168 #clsname = os.path.basename(cls.name).replace(".bbclass", "") 169 self.classextend = self.classextend.replace("###CLASS###", cls.name) 170 f = self.parsehelper(self.classextend) 171 alldata = bb.parse.handle(f.name, self.d) 172 d1 = alldata[''] 173 d2 = alldata[cls.name] 174 self.assertEqual(d1.getVar("VAR_var"), "B") 175 self.assertEqual(d2.getVar("VAR_var"), None) 176 177 addtask_deltask = """ 178addtask do_patch after do_foo after do_unpack before do_configure before do_compile 179addtask do_fetch do_patch 180 181MYVAR = "do_patch" 182EMPTYVAR = "" 183deltask do_fetch ${MYVAR} ${EMPTYVAR} 184deltask ${EMPTYVAR} 185""" 186 def test_parse_addtask_deltask(self): 187 import sys 188 f = self.parsehelper(self.addtask_deltask) 189 d = bb.parse.handle(f.name, self.d)[''] 190 191 stdout = sys.stdout.getvalue() 192 self.assertTrue("addtask contained multiple 'before' keywords" in stdout) 193 self.assertTrue("addtask contained multiple 'after' keywords" in stdout) 194 self.assertTrue('addtask ignored: " do_patch"' in stdout) 195 #self.assertTrue('dependent task do_foo for do_patch does not exist' in stdout) 196 197 broken_multiline_comment = """ 198# First line of comment \\ 199# Second line of comment \\ 200 201""" 202 def test_parse_broken_multiline_comment(self): 203 f = self.parsehelper(self.broken_multiline_comment) 204 with self.assertRaises(bb.BBHandledException): 205 d = bb.parse.handle(f.name, self.d)[''] 206 207 208 comment_in_var = """ 209VAR = " \\ 210 SOMEVAL \\ 211# some comment \\ 212 SOMEOTHERVAL \\ 213" 214""" 215 def test_parse_comment_in_var(self): 216 f = self.parsehelper(self.comment_in_var) 217 with self.assertRaises(bb.BBHandledException): 218 d = bb.parse.handle(f.name, self.d)[''] 219 220