blob: bb820e40378d4db289e18e5930d926c99340491a [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# BitBake Test for codeparser.py
5#
6# Copyright (C) 2010 Chris Larson
7# Copyright (C) 2012 Richard Purdie
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License version 2 as
11# published by the Free Software Foundation.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License along
19# with this program; if not, write to the Free Software Foundation, Inc.,
20# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22
23import unittest
24import logging
25import bb
26
27logger = logging.getLogger('BitBake.TestCodeParser')
28
29# bb.data references bb.parse but can't directly import due to circular dependencies.
30# Hack around it for now :(
31import bb.parse
32import bb.data
33
34class ReferenceTest(unittest.TestCase):
35 def setUp(self):
36 self.d = bb.data.init()
37
38 def setEmptyVars(self, varlist):
39 for k in varlist:
40 self.d.setVar(k, "")
41
42 def setValues(self, values):
43 for k, v in values.items():
44 self.d.setVar(k, v)
45
46 def assertReferences(self, refs):
47 self.assertEqual(self.references, refs)
48
49 def assertExecs(self, execs):
50 self.assertEqual(self.execs, execs)
51
52class VariableReferenceTest(ReferenceTest):
53
54 def parseExpression(self, exp):
55 parsedvar = self.d.expandWithRefs(exp, None)
56 self.references = parsedvar.references
57
58 def test_simple_reference(self):
59 self.setEmptyVars(["FOO"])
60 self.parseExpression("${FOO}")
61 self.assertReferences(set(["FOO"]))
62
63 def test_nested_reference(self):
64 self.setEmptyVars(["BAR"])
65 self.d.setVar("FOO", "BAR")
66 self.parseExpression("${${FOO}}")
67 self.assertReferences(set(["FOO", "BAR"]))
68
69 def test_python_reference(self):
70 self.setEmptyVars(["BAR"])
71 self.parseExpression("${@bb.data.getVar('BAR', d, True) + 'foo'}")
72 self.assertReferences(set(["BAR"]))
73
74class ShellReferenceTest(ReferenceTest):
75
76 def parseExpression(self, exp):
77 parsedvar = self.d.expandWithRefs(exp, None)
78 parser = bb.codeparser.ShellParser("ParserTest", logger)
79 parser.parse_shell(parsedvar.value)
80
81 self.references = parsedvar.references
82 self.execs = parser.execs
83
84 def test_quotes_inside_assign(self):
85 self.parseExpression('foo=foo"bar"baz')
86 self.assertReferences(set([]))
87
88 def test_quotes_inside_arg(self):
89 self.parseExpression('sed s#"bar baz"#"alpha beta"#g')
90 self.assertExecs(set(["sed"]))
91
92 def test_arg_continuation(self):
93 self.parseExpression("sed -i -e s,foo,bar,g \\\n *.pc")
94 self.assertExecs(set(["sed"]))
95
96 def test_dollar_in_quoted(self):
97 self.parseExpression('sed -i -e "foo$" *.pc')
98 self.assertExecs(set(["sed"]))
99
100 def test_quotes_inside_arg_continuation(self):
101 self.setEmptyVars(["bindir", "D", "libdir"])
102 self.parseExpression("""
103sed -i -e s#"moc_location=.*$"#"moc_location=${bindir}/moc4"# \\
104-e s#"uic_location=.*$"#"uic_location=${bindir}/uic4"# \\
105${D}${libdir}/pkgconfig/*.pc
106""")
107 self.assertReferences(set(["bindir", "D", "libdir"]))
108
109 def test_assign_subshell_expansion(self):
110 self.parseExpression("foo=$(echo bar)")
111 self.assertExecs(set(["echo"]))
112
113 def test_shell_unexpanded(self):
114 self.setEmptyVars(["QT_BASE_NAME"])
115 self.parseExpression('echo "${QT_BASE_NAME}"')
116 self.assertExecs(set(["echo"]))
117 self.assertReferences(set(["QT_BASE_NAME"]))
118
119 def test_incomplete_varexp_single_quotes(self):
120 self.parseExpression("sed -i -e 's:IP{:I${:g' $pc")
121 self.assertExecs(set(["sed"]))
122
123
124 def test_until(self):
125 self.parseExpression("until false; do echo true; done")
126 self.assertExecs(set(["false", "echo"]))
127 self.assertReferences(set())
128
129 def test_case(self):
130 self.parseExpression("""
131case $foo in
132*)
133bar
134;;
135esac
136""")
137 self.assertExecs(set(["bar"]))
138 self.assertReferences(set())
139
140 def test_assign_exec(self):
141 self.parseExpression("a=b c='foo bar' alpha 1 2 3")
142 self.assertExecs(set(["alpha"]))
143
144 def test_redirect_to_file(self):
145 self.setEmptyVars(["foo"])
146 self.parseExpression("echo foo >${foo}/bar")
147 self.assertExecs(set(["echo"]))
148 self.assertReferences(set(["foo"]))
149
150 def test_heredoc(self):
151 self.setEmptyVars(["theta"])
152 self.parseExpression("""
153cat <<END
154alpha
155beta
156${theta}
157END
158""")
159 self.assertReferences(set(["theta"]))
160
161 def test_redirect_from_heredoc(self):
162 v = ["B", "SHADOW_MAILDIR", "SHADOW_MAILFILE", "SHADOW_UTMPDIR", "SHADOW_LOGDIR", "bindir"]
163 self.setEmptyVars(v)
164 self.parseExpression("""
165cat <<END >${B}/cachedpaths
166shadow_cv_maildir=${SHADOW_MAILDIR}
167shadow_cv_mailfile=${SHADOW_MAILFILE}
168shadow_cv_utmpdir=${SHADOW_UTMPDIR}
169shadow_cv_logdir=${SHADOW_LOGDIR}
170shadow_cv_passwd_dir=${bindir}
171END
172""")
173 self.assertReferences(set(v))
174 self.assertExecs(set(["cat"]))
175
176# def test_incomplete_command_expansion(self):
177# self.assertRaises(reftracker.ShellSyntaxError, reftracker.execs,
178# bbvalue.shparse("cp foo`", self.d), self.d)
179
180# def test_rogue_dollarsign(self):
181# self.setValues({"D" : "/tmp"})
182# self.parseExpression("install -d ${D}$")
183# self.assertReferences(set(["D"]))
184# self.assertExecs(set(["install"]))
185
186
187class PythonReferenceTest(ReferenceTest):
188
189 def setUp(self):
190 self.d = bb.data.init()
191 if hasattr(bb.utils, "_context"):
192 self.context = bb.utils._context
193 else:
194 import __builtin__
195 self.context = __builtin__.__dict__
196
197 def parseExpression(self, exp):
198 parsedvar = self.d.expandWithRefs(exp, None)
199 parser = bb.codeparser.PythonParser("ParserTest", logger)
200 parser.parse_python(parsedvar.value)
201
202 self.references = parsedvar.references | parser.references
203 self.execs = parser.execs
204
205 @staticmethod
206 def indent(value):
207 """Python Snippets have to be indented, python values don't have to
208be. These unit tests are testing snippets."""
209 return " " + value
210
211 def test_getvar_reference(self):
212 self.parseExpression("bb.data.getVar('foo', d, True)")
213 self.assertReferences(set(["foo"]))
214 self.assertExecs(set())
215
216 def test_getvar_computed_reference(self):
217 self.parseExpression("bb.data.getVar('f' + 'o' + 'o', d, True)")
218 self.assertReferences(set())
219 self.assertExecs(set())
220
221 def test_getvar_exec_reference(self):
222 self.parseExpression("eval('bb.data.getVar(\"foo\", d, True)')")
223 self.assertReferences(set())
224 self.assertExecs(set(["eval"]))
225
226 def test_var_reference(self):
227 self.context["foo"] = lambda x: x
228 self.setEmptyVars(["FOO"])
229 self.parseExpression("foo('${FOO}')")
230 self.assertReferences(set(["FOO"]))
231 self.assertExecs(set(["foo"]))
232 del self.context["foo"]
233
234 def test_var_exec(self):
235 for etype in ("func", "task"):
236 self.d.setVar("do_something", "echo 'hi mom! ${FOO}'")
237 self.d.setVarFlag("do_something", etype, True)
238 self.parseExpression("bb.build.exec_func('do_something', d)")
239 self.assertReferences(set([]))
240 self.assertExecs(set(["do_something"]))
241
242 def test_function_reference(self):
243 self.context["testfunc"] = lambda msg: bb.msg.note(1, None, msg)
244 self.d.setVar("FOO", "Hello, World!")
245 self.parseExpression("testfunc('${FOO}')")
246 self.assertReferences(set(["FOO"]))
247 self.assertExecs(set(["testfunc"]))
248 del self.context["testfunc"]
249
250 def test_qualified_function_reference(self):
251 self.parseExpression("time.time()")
252 self.assertExecs(set(["time.time"]))
253
254 def test_qualified_function_reference_2(self):
255 self.parseExpression("os.path.dirname('/foo/bar')")
256 self.assertExecs(set(["os.path.dirname"]))
257
258 def test_qualified_function_reference_nested(self):
259 self.parseExpression("time.strftime('%Y%m%d',time.gmtime())")
260 self.assertExecs(set(["time.strftime", "time.gmtime"]))
261
262 def test_function_reference_chained(self):
263 self.context["testget"] = lambda: "\tstrip me "
264 self.parseExpression("testget().strip()")
265 self.assertExecs(set(["testget"]))
266 del self.context["testget"]
267
268
269class DependencyReferenceTest(ReferenceTest):
270
271 pydata = """
272bb.data.getVar('somevar', d, True)
273def test(d):
274 foo = 'bar %s' % 'foo'
275def test2(d):
276 d.getVar(foo, True)
277 d.getVar('bar', False)
278 test2(d)
279
280def a():
281 \"\"\"some
282 stuff
283 \"\"\"
284 return "heh"
285
286test(d)
287
288bb.data.expand(bb.data.getVar("something", False, d), d)
289bb.data.expand("${inexpand} somethingelse", d)
290bb.data.getVar(a(), d, False)
291"""
292
293 def test_python(self):
294 self.d.setVar("FOO", self.pydata)
295 self.setEmptyVars(["inexpand", "a", "test2", "test"])
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500296 self.d.setVarFlags("FOO", {
297 "func": True,
298 "python": True,
299 "lineno": 1,
300 "filename": "example.bb",
301 })
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500302
303 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
304
305 self.assertEquals(deps, set(["somevar", "bar", "something", "inexpand", "test", "test2", "a"]))
306
307
308 shelldata = """
309foo () {
310bar
311}
312{
313echo baz
314$(heh)
315eval `moo`
316}
317a=b
318c=d
319(
320true && false
321test -f foo
322testval=something
323$testval
324) || aiee
325! inverted
326echo ${somevar}
327
328case foo in
329bar)
330echo bar
331;;
332baz)
333echo baz
334;;
335foo*)
336echo foo
337;;
338esac
339"""
340
341 def test_shell(self):
342 execs = ["bar", "echo", "heh", "moo", "true", "aiee"]
343 self.d.setVar("somevar", "heh")
344 self.d.setVar("inverted", "echo inverted...")
345 self.d.setVarFlag("inverted", "func", True)
346 self.d.setVar("FOO", self.shelldata)
347 self.d.setVarFlags("FOO", {"func": True})
348 self.setEmptyVars(execs)
349
350 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
351
352 self.assertEquals(deps, set(["somevar", "inverted"] + execs))
353
354
355 def test_vardeps(self):
356 self.d.setVar("oe_libinstall", "echo test")
357 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
358 self.d.setVarFlag("FOO", "vardeps", "oe_libinstall")
359
360 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
361
362 self.assertEquals(deps, set(["oe_libinstall"]))
363
364 def test_vardeps_expand(self):
365 self.d.setVar("oe_libinstall", "echo test")
366 self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
367 self.d.setVarFlag("FOO", "vardeps", "${@'oe_libinstall'}")
368
369 deps, values = bb.data.build_dependencies("FOO", set(self.d.keys()), set(), set(), self.d)
370
371 self.assertEquals(deps, set(["oe_libinstall"]))
372
373 #Currently no wildcard support
374 #def test_vardeps_wildcards(self):
375 # self.d.setVar("oe_libinstall", "echo test")
376 # self.d.setVar("FOO", "foo=oe_libinstall; eval $foo")
377 # self.d.setVarFlag("FOO", "vardeps", "oe_*")
378 # self.assertEquals(deps, set(["oe_libinstall"]))
379
380