1#
2# BitBake Tests for runqueue task processing
3#
4# Copyright (C) 2019 Richard Purdie
5#
6# SPDX-License-Identifier: GPL-2.0-only
7#
8
9import unittest
10import bb
11import os
12import tempfile
13import subprocess
14import sys
15import time
16
17#
18# TODO:
19# Add tests on task ordering (X happens before Y after Z)
20#
21
22class RunQueueTests(unittest.TestCase):
23
24    alltasks = ['package', 'fetch', 'unpack', 'patch', 'prepare_recipe_sysroot', 'configure',
25                'compile', 'install', 'packagedata', 'package_qa', 'package_write_rpm', 'package_write_ipk',
26                'populate_sysroot', 'build']
27    a1_sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_package_write_rpm a1:do_populate_lic a1:do_populate_sysroot"
28    b1_sstatevalid = "b1:do_package b1:do_package_qa b1:do_packagedata b1:do_package_write_ipk b1:do_package_write_rpm b1:do_populate_lic b1:do_populate_sysroot"
29
30    def run_bitbakecmd(self, cmd, builddir, sstatevalid="", slowtasks="", extraenv=None, cleanup=False):
31        env = os.environ.copy()
32        env["BBPATH"] = os.path.realpath(os.path.join(os.path.dirname(__file__), "runqueue-tests"))
33        env["BB_ENV_EXTRAWHITE"] = "SSTATEVALID SLOWTASKS"
34        env["SSTATEVALID"] = sstatevalid
35        env["SLOWTASKS"] = slowtasks
36        if extraenv:
37            for k in extraenv:
38                env[k] = extraenv[k]
39                env["BB_ENV_EXTRAWHITE"] = env["BB_ENV_EXTRAWHITE"] + " " + k
40        try:
41            output = subprocess.check_output(cmd, env=env, stderr=subprocess.STDOUT,universal_newlines=True, cwd=builddir)
42            print(output)
43        except subprocess.CalledProcessError as e:
44            self.fail("Command %s failed with %s" % (cmd, e.output))
45        tasks = []
46        tasklog = builddir + "/task.log"
47        if os.path.exists(tasklog):
48            with open(tasklog, "r") as f:
49                tasks = [line.rstrip() for line in f]
50            if cleanup:
51                os.remove(tasklog)
52        return tasks
53
54    def test_no_setscenevalid(self):
55        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
56            cmd = ["bitbake", "a1"]
57            sstatevalid = ""
58            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
59            expected = ['a1:' + x for x in self.alltasks]
60            self.assertEqual(set(tasks), set(expected))
61
62    def test_single_setscenevalid(self):
63        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
64            cmd = ["bitbake", "a1"]
65            sstatevalid = "a1:do_package"
66            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
67            expected = ['a1:package_setscene', 'a1:fetch', 'a1:unpack', 'a1:patch', 'a1:prepare_recipe_sysroot', 'a1:configure',
68                        'a1:compile', 'a1:install', 'a1:packagedata', 'a1:package_qa', 'a1:package_write_rpm', 'a1:package_write_ipk',
69                        'a1:populate_sysroot', 'a1:build']
70            self.assertEqual(set(tasks), set(expected))
71
72    def test_intermediate_setscenevalid(self):
73        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
74            cmd = ["bitbake", "a1"]
75            sstatevalid = "a1:do_package a1:do_populate_sysroot"
76            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
77            expected = ['a1:package_setscene', 'a1:packagedata', 'a1:package_qa', 'a1:package_write_rpm', 'a1:package_write_ipk',
78                        'a1:populate_sysroot_setscene', 'a1:build']
79            self.assertEqual(set(tasks), set(expected))
80
81    def test_intermediate_notcovered(self):
82        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
83            cmd = ["bitbake", "a1"]
84            sstatevalid = "a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_package_write_rpm a1:do_populate_lic a1:do_populate_sysroot"
85            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
86            expected = ['a1:package_write_ipk_setscene', 'a1:package_write_rpm_setscene', 'a1:packagedata_setscene',
87                        'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
88            self.assertEqual(set(tasks), set(expected))
89
90    def test_all_setscenevalid(self):
91        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
92            cmd = ["bitbake", "a1"]
93            sstatevalid = self.a1_sstatevalid
94            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
95            expected = ['a1:package_write_ipk_setscene', 'a1:package_write_rpm_setscene', 'a1:packagedata_setscene',
96                        'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
97            self.assertEqual(set(tasks), set(expected))
98
99    def test_no_settasks(self):
100        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
101            cmd = ["bitbake", "a1", "-c", "patch"]
102            sstatevalid = self.a1_sstatevalid
103            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
104            expected = ['a1:fetch', 'a1:unpack', 'a1:patch']
105            self.assertEqual(set(tasks), set(expected))
106
107    def test_mix_covered_notcovered(self):
108        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
109            cmd = ["bitbake", "a1:do_patch", "a1:do_populate_sysroot"]
110            sstatevalid = self.a1_sstatevalid
111            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
112            expected = ['a1:fetch', 'a1:unpack', 'a1:patch', 'a1:populate_sysroot_setscene']
113            self.assertEqual(set(tasks), set(expected))
114
115
116    # Test targets with intermediate setscene tasks alongside a target with no intermediate setscene tasks
117    def test_mixed_direct_tasks_setscene_tasks(self):
118        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
119            cmd = ["bitbake", "c1:do_patch", "a1"]
120            sstatevalid = self.a1_sstatevalid
121            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
122            expected = ['c1:fetch', 'c1:unpack', 'c1:patch', 'a1:package_write_ipk_setscene', 'a1:package_write_rpm_setscene', 'a1:packagedata_setscene',
123                        'a1:package_qa_setscene', 'a1:build', 'a1:populate_sysroot_setscene']
124            self.assertEqual(set(tasks), set(expected))
125
126    # This test slows down the execution of do_package_setscene until after other real tasks have
127    # started running which tests for a bug where tasks were being lost from the buildable list of real
128    # tasks if they weren't in tasks_covered or tasks_notcovered
129    def test_slow_setscene(self):
130        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
131            cmd = ["bitbake", "a1"]
132            sstatevalid = "a1:do_package"
133            slowtasks = "a1:package_setscene"
134            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, slowtasks)
135            expected = ['a1:package_setscene', 'a1:fetch', 'a1:unpack', 'a1:patch', 'a1:prepare_recipe_sysroot', 'a1:configure',
136                        'a1:compile', 'a1:install', 'a1:packagedata', 'a1:package_qa', 'a1:package_write_rpm', 'a1:package_write_ipk',
137                        'a1:populate_sysroot', 'a1:build']
138            self.assertEqual(set(tasks), set(expected))
139
140    def test_setscenewhitelist(self):
141        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
142            cmd = ["bitbake", "a1"]
143            extraenv = {
144                "BB_SETSCENE_ENFORCE" : "1",
145                "BB_SETSCENE_ENFORCE_WHITELIST" : "a1:do_package_write_rpm a1:do_build"
146            }
147            sstatevalid = "a1:do_package a1:do_package_qa a1:do_packagedata a1:do_package_write_ipk a1:do_populate_lic a1:do_populate_sysroot"
148            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv)
149            expected = ['a1:packagedata_setscene', 'a1:package_qa_setscene', 'a1:package_write_ipk_setscene',
150                        'a1:populate_sysroot_setscene', 'a1:package_setscene']
151            self.assertEqual(set(tasks), set(expected))
152
153    # Tests for problems with dependencies between setscene tasks
154    def test_no_setscenevalid_harddeps(self):
155        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
156            cmd = ["bitbake", "d1"]
157            sstatevalid = ""
158            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
159            expected = ['a1:package', 'a1:fetch', 'a1:unpack', 'a1:patch', 'a1:prepare_recipe_sysroot', 'a1:configure',
160                        'a1:compile', 'a1:install', 'a1:packagedata', 'a1:package_write_rpm', 'a1:package_write_ipk',
161                        'a1:populate_sysroot', 'd1:package', 'd1:fetch', 'd1:unpack', 'd1:patch', 'd1:prepare_recipe_sysroot', 'd1:configure',
162                        'd1:compile', 'd1:install', 'd1:packagedata', 'd1:package_qa', 'd1:package_write_rpm', 'd1:package_write_ipk',
163                        'd1:populate_sysroot', 'd1:build']
164            self.assertEqual(set(tasks), set(expected))
165
166    def test_no_setscenevalid_withdeps(self):
167        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
168            cmd = ["bitbake", "b1"]
169            sstatevalid = ""
170            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
171            expected = ['a1:' + x for x in self.alltasks] + ['b1:' + x for x in self.alltasks]
172            expected.remove('a1:build')
173            expected.remove('a1:package_qa')
174            self.assertEqual(set(tasks), set(expected))
175
176    def test_single_a1_setscenevalid_withdeps(self):
177        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
178            cmd = ["bitbake", "b1"]
179            sstatevalid = "a1:do_package"
180            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
181            expected = ['a1:package_setscene', 'a1:fetch', 'a1:unpack', 'a1:patch', 'a1:prepare_recipe_sysroot', 'a1:configure',
182                        'a1:compile', 'a1:install', 'a1:packagedata', 'a1:package_write_rpm', 'a1:package_write_ipk',
183                        'a1:populate_sysroot'] + ['b1:' + x for x in self.alltasks]
184            self.assertEqual(set(tasks), set(expected))
185
186    def test_single_b1_setscenevalid_withdeps(self):
187        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
188            cmd = ["bitbake", "b1"]
189            sstatevalid = "b1:do_package"
190            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
191            expected = ['a1:package', 'a1:fetch', 'a1:unpack', 'a1:patch', 'a1:prepare_recipe_sysroot', 'a1:configure',
192                        'a1:compile', 'a1:install', 'a1:packagedata', 'a1:package_write_rpm', 'a1:package_write_ipk',
193                        'a1:populate_sysroot', 'b1:package_setscene'] + ['b1:' + x for x in self.alltasks]
194            expected.remove('b1:package')
195            self.assertEqual(set(tasks), set(expected))
196
197    def test_intermediate_setscenevalid_withdeps(self):
198        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
199            cmd = ["bitbake", "b1"]
200            sstatevalid = "a1:do_package a1:do_populate_sysroot b1:do_package"
201            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
202            expected = ['a1:package_setscene', 'a1:packagedata', 'a1:package_write_rpm', 'a1:package_write_ipk',
203                        'a1:populate_sysroot_setscene', 'b1:package_setscene'] + ['b1:' + x for x in self.alltasks]
204            expected.remove('b1:package')
205            self.assertEqual(set(tasks), set(expected))
206
207    def test_all_setscenevalid_withdeps(self):
208        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
209            cmd = ["bitbake", "b1"]
210            sstatevalid = self.a1_sstatevalid + " " + self.b1_sstatevalid
211            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid)
212            expected = ['a1:package_write_ipk_setscene', 'a1:package_write_rpm_setscene', 'a1:packagedata_setscene',
213                        'b1:build', 'a1:populate_sysroot_setscene', 'b1:package_write_ipk_setscene', 'b1:package_write_rpm_setscene',
214                        'b1:packagedata_setscene', 'b1:package_qa_setscene', 'b1:populate_sysroot_setscene']
215            self.assertEqual(set(tasks), set(expected))
216
217    def test_multiconfig_setscene_optimise(self):
218        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
219            extraenv = {
220                "BBMULTICONFIG" : "mc1 mc2",
221                "BB_SIGNATURE_HANDLER" : "basic"
222            }
223            cmd = ["bitbake", "b1", "mc:mc1:b1", "mc:mc2:b1"]
224            setscenetasks = ['package_write_ipk_setscene', 'package_write_rpm_setscene', 'packagedata_setscene',
225                             'populate_sysroot_setscene', 'package_qa_setscene']
226            sstatevalid = ""
227            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv)
228            expected = ['a1:' + x for x in self.alltasks] + ['b1:' + x for x in self.alltasks] + \
229                       ['mc1:b1:' + x for x in setscenetasks] + ['mc1:a1:' + x for x in setscenetasks] + \
230                       ['mc2:b1:' + x for x in setscenetasks] + ['mc2:a1:' + x for x in setscenetasks] + \
231                       ['mc1:b1:build', 'mc2:b1:build']
232            for x in ['mc1:a1:package_qa_setscene', 'mc2:a1:package_qa_setscene', 'a1:build', 'a1:package_qa']:
233                expected.remove(x)
234            self.assertEqual(set(tasks), set(expected))
235
236
237    @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required')
238    def test_hashserv_single(self):
239        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
240            extraenv = {
241                "BB_HASHSERVE" : "auto",
242                "BB_SIGNATURE_HANDLER" : "TestEquivHash"
243            }
244            cmd = ["bitbake", "a1", "b1"]
245            setscenetasks = ['package_write_ipk_setscene', 'package_write_rpm_setscene', 'packagedata_setscene',
246                             'populate_sysroot_setscene', 'package_qa_setscene']
247            sstatevalid = ""
248            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
249            expected = ['a1:' + x for x in self.alltasks] + ['b1:' + x for x in self.alltasks]
250            self.assertEqual(set(tasks), set(expected))
251            cmd = ["bitbake", "a1", "-c", "install", "-f"]
252            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
253            expected = ['a1:install']
254            self.assertEqual(set(tasks), set(expected))
255            cmd = ["bitbake", "a1", "b1"]
256            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
257            expected = ['a1:populate_sysroot', 'a1:package', 'a1:package_write_rpm_setscene', 'a1:packagedata_setscene',
258                        'a1:package_write_ipk_setscene', 'a1:package_qa_setscene']
259            self.assertEqual(set(tasks), set(expected))
260
261            self.shutdown(tempdir)
262
263    @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required')
264    def test_hashserv_double(self):
265        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
266            extraenv = {
267                "BB_HASHSERVE" : "auto",
268                "BB_SIGNATURE_HANDLER" : "TestEquivHash"
269            }
270            cmd = ["bitbake", "a1", "b1", "e1"]
271            setscenetasks = ['package_write_ipk_setscene', 'package_write_rpm_setscene', 'packagedata_setscene',
272                             'populate_sysroot_setscene', 'package_qa_setscene']
273            sstatevalid = ""
274            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
275            expected = ['a1:' + x for x in self.alltasks] + ['b1:' + x for x in self.alltasks] + ['e1:' + x for x in self.alltasks]
276            self.assertEqual(set(tasks), set(expected))
277            cmd = ["bitbake", "a1", "b1", "-c", "install", "-fn"]
278            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
279            cmd = ["bitbake", "e1"]
280            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
281            expected = ['a1:package', 'a1:install', 'b1:package', 'b1:install', 'a1:populate_sysroot', 'b1:populate_sysroot',
282                        'a1:package_write_ipk_setscene', 'b1:packagedata_setscene', 'b1:package_write_rpm_setscene',
283                        'a1:package_write_rpm_setscene', 'b1:package_write_ipk_setscene', 'a1:packagedata_setscene']
284            self.assertEqual(set(tasks), set(expected))
285
286            self.shutdown(tempdir)
287
288    @unittest.skipIf(sys.version_info < (3, 5, 0), 'Python 3.5 or later required')
289    def test_hashserv_multiple_setscene(self):
290        # Runs e1:do_package_setscene twice
291        with tempfile.TemporaryDirectory(prefix="runqueuetest") as tempdir:
292            extraenv = {
293                "BB_HASHSERVE" : "auto",
294                "BB_SIGNATURE_HANDLER" : "TestEquivHash"
295            }
296            cmd = ["bitbake", "a1", "b1", "e1"]
297            setscenetasks = ['package_write_ipk_setscene', 'package_write_rpm_setscene', 'packagedata_setscene',
298                             'populate_sysroot_setscene', 'package_qa_setscene']
299            sstatevalid = ""
300            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
301            expected = ['a1:' + x for x in self.alltasks] + ['b1:' + x for x in self.alltasks] + ['e1:' + x for x in self.alltasks]
302            self.assertEqual(set(tasks), set(expected))
303            cmd = ["bitbake", "a1", "b1", "-c", "install", "-fn"]
304            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True)
305            cmd = ["bitbake", "e1"]
306            sstatevalid = "e1:do_package"
307            tasks = self.run_bitbakecmd(cmd, tempdir, sstatevalid, extraenv=extraenv, cleanup=True, slowtasks="a1:populate_sysroot b1:populate_sysroot")
308            expected = ['a1:package', 'a1:install', 'b1:package', 'b1:install', 'a1:populate_sysroot', 'b1:populate_sysroot',
309                        'a1:package_write_ipk_setscene', 'b1:packagedata_setscene', 'b1:package_write_rpm_setscene',
310                        'a1:package_write_rpm_setscene', 'b1:package_write_ipk_setscene', 'a1:packagedata_setscene',
311                        'e1:package_setscene']
312            self.assertEqual(set(tasks), set(expected))
313            for i in expected:
314                self.assertEqual(tasks.count(i), 1, "%s not in task list once" % i)
315
316            self.shutdown(tempdir)
317
318    def shutdown(self, tempdir):
319        # Wait for the hashserve socket to disappear else we'll see races with the tempdir cleanup
320        while os.path.exists(tempdir + "/hashserve.sock"):
321            time.sleep(0.5)
322
323
324