blob: 4a791ff40e0906680e1100fd7334c9e0dbae96b9 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: MIT
3#
4
Brad Bishopd7bf8c12018-02-25 22:55:05 -05005import os
6import re
7import shutil
8import tempfile
9import glob
10import fnmatch
11
12import oeqa.utils.ftools as ftools
13from oeqa.selftest.case import OESelftestTestCase
14from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
15from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer
Brad Bishopd7bf8c12018-02-25 22:55:05 -050016
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080017oldmetapath = None
18
19def setUpModule():
20 import bb.utils
21
22 global templayerdir
23 templayerdir = tempfile.mkdtemp(prefix='devtoolqa')
24 corecopydir = os.path.join(templayerdir, 'core-copy')
25 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
26 edited_layers = []
27
28 # We need to take a copy of the meta layer so we can modify it and not
29 # have any races against other tests that might be running in parallel
30 # however things like COREBASE mean that you can't just copy meta, you
31 # need the whole repository.
32 def bblayers_edit_cb(layerpath, canonical_layerpath):
33 global oldmetapath
34 if not canonical_layerpath.endswith('/'):
35 # This helps us match exactly when we're using this path later
36 canonical_layerpath += '/'
37 if not edited_layers and canonical_layerpath.endswith('/meta/'):
38 canonical_layerpath = os.path.realpath(canonical_layerpath) + '/'
39 edited_layers.append(layerpath)
40 oldmetapath = os.path.realpath(layerpath)
41 result = runCmd('git rev-parse --show-toplevel', cwd=canonical_layerpath)
42 oldreporoot = result.output.rstrip()
43 newmetapath = os.path.join(corecopydir, os.path.relpath(oldmetapath, oldreporoot))
44 runCmd('git clone %s %s' % (oldreporoot, corecopydir), cwd=templayerdir)
45 # Now we need to copy any modified files
46 # You might ask "why not just copy the entire tree instead of
47 # cloning and doing this?" - well, the problem with that is
48 # TMPDIR or an equally large subdirectory might exist
49 # under COREBASE and we don't want to copy that, so we have
50 # to be selective.
51 result = runCmd('git status --porcelain', cwd=oldreporoot)
52 for line in result.output.splitlines():
53 if line.startswith(' M ') or line.startswith('?? '):
54 relpth = line.split()[1]
55 pth = os.path.join(oldreporoot, relpth)
56 if pth.startswith(canonical_layerpath):
57 if relpth.endswith('/'):
58 destdir = os.path.join(corecopydir, relpth)
Andrew Geisslerc3d88e42020-10-02 09:45:00 -050059 # avoid race condition by not copying .pyc files YPBZ#13421,13803
60 shutil.copytree(pth, destdir, ignore=ignore_patterns('*.pyc', '__pycache__'))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080061 else:
62 destdir = os.path.join(corecopydir, os.path.dirname(relpth))
63 bb.utils.mkdirhier(destdir)
64 shutil.copy2(pth, destdir)
65 return newmetapath
66 else:
67 return layerpath
68 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
69
70def tearDownModule():
71 if oldmetapath:
72 edited_layers = []
73 def bblayers_edit_cb(layerpath, canonical_layerpath):
74 if not edited_layers and canonical_layerpath.endswith('/meta'):
75 edited_layers.append(layerpath)
76 return oldmetapath
77 else:
78 return layerpath
79 bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
80 bb.utils.edit_bblayers_conf(bblayers_conf, None, None, bblayers_edit_cb)
81 shutil.rmtree(templayerdir)
82
Brad Bishopd7bf8c12018-02-25 22:55:05 -050083class DevtoolBase(OESelftestTestCase):
84
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080085 @classmethod
86 def setUpClass(cls):
87 super(DevtoolBase, cls).setUpClass()
88 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
89 cls.original_sstate = bb_vars['SSTATE_DIR']
90 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
91 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
92 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
93 % cls.original_sstate)
94
95 @classmethod
96 def tearDownClass(cls):
97 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
98 runCmd('rm -rf %s' % cls.devtool_sstate)
99 super(DevtoolBase, cls).tearDownClass()
100
101 def setUp(self):
102 """Test case setup function"""
103 super(DevtoolBase, self).setUp()
104 self.workspacedir = os.path.join(self.builddir, 'workspace')
105 self.assertTrue(not os.path.exists(self.workspacedir),
106 'This test cannot be run with a workspace directory '
107 'under the build directory')
108 self.append_config(self.sstate_conf)
109
Andrew Geisslerc3d88e42020-10-02 09:45:00 -0500110 def tearDown(self):
111 # devtools tests are heavy on IO and if bitbake can't write out its caches, we see timeouts.
112 # call sync around the tests to ensure the IO queue doesn't get too large, taking any IO
113 # hit here rather than in bitbake shutdown.
114 super().tearDown()
115 os.system("sync")
116
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800117 def _check_src_repo(self, repo_dir):
118 """Check srctree git repository"""
119 self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')),
120 'git repository for external source tree not found')
121 result = runCmd('git status --porcelain', cwd=repo_dir)
122 self.assertEqual(result.output.strip(), "",
123 'Created git repo is not clean')
124 result = runCmd('git symbolic-ref HEAD', cwd=repo_dir)
125 self.assertEqual(result.output.strip(), "refs/heads/devtool",
126 'Wrong branch in git repo')
127
128 def _check_repo_status(self, repo_dir, expected_status):
129 """Check the worktree status of a repository"""
130 result = runCmd('git status . --porcelain',
131 cwd=repo_dir)
132 for line in result.output.splitlines():
133 for ind, (f_status, fn_re) in enumerate(expected_status):
134 if re.match(fn_re, line[3:]):
135 if f_status != line[:2]:
136 self.fail('Unexpected status in line: %s' % line)
137 expected_status.pop(ind)
138 break
139 else:
140 self.fail('Unexpected modified file in line: %s' % line)
141 if expected_status:
142 self.fail('Missing file changes: %s' % expected_status)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500143
144 def _test_recipe_contents(self, recipefile, checkvars, checkinherits):
145 with open(recipefile, 'r') as f:
146 invar = None
147 invalue = None
Brad Bishop6dbb3162019-11-25 09:41:34 -0500148 inherits = set()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500149 for line in f:
150 var = None
151 if invar:
152 value = line.strip().strip('"')
153 if value.endswith('\\'):
154 invalue += ' ' + value[:-1].strip()
155 continue
156 else:
157 invalue += ' ' + value.strip()
158 var = invar
159 value = invalue
160 invar = None
161 elif '=' in line:
162 splitline = line.split('=', 1)
163 var = splitline[0].rstrip()
164 value = splitline[1].strip().strip('"')
165 if value.endswith('\\'):
166 invalue = value[:-1].strip()
167 invar = var
168 continue
169 elif line.startswith('inherit '):
Brad Bishop6dbb3162019-11-25 09:41:34 -0500170 inherits.update(line.split()[1:])
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500171
172 if var and var in checkvars:
173 needvalue = checkvars.pop(var)
174 if needvalue is None:
175 self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
176 if isinstance(needvalue, set):
177 if var == 'LICENSE':
178 value = set(value.split(' & '))
179 else:
180 value = set(value.split())
181 self.assertEqual(value, needvalue, 'values for %s do not match' % var)
182
183
184 missingvars = {}
185 for var, value in checkvars.items():
186 if value is not None:
187 missingvars[var] = value
188 self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
189
190 for inherit in checkinherits:
191 self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit)
192
193 def _check_bbappend(self, testrecipe, recipefile, appenddir):
194 result = runCmd('bitbake-layers show-appends', cwd=self.builddir)
195 resultlines = result.output.splitlines()
196 inrecipe = False
197 bbappends = []
198 bbappendfile = None
199 for line in resultlines:
200 if inrecipe:
201 if line.startswith(' '):
202 bbappends.append(line.strip())
203 else:
204 break
205 elif line == '%s:' % os.path.basename(recipefile):
206 inrecipe = True
207 self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends)))
208 for bbappend in bbappends:
209 if bbappend.startswith(appenddir):
210 bbappendfile = bbappend
211 break
212 else:
213 self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe)
214 return bbappendfile
215
216 def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'):
217 create_temp_layer(templayerdir, templayername, priority, recipepathspec)
218 if addlayer:
219 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
220 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
221
222 def _process_ls_output(self, output):
223 """
224 Convert ls -l output to a format we can reasonably compare from one context
225 to another (e.g. from host to target)
226 """
227 filelist = []
228 for line in output.splitlines():
229 splitline = line.split()
230 if len(splitline) < 8:
231 self.fail('_process_ls_output: invalid output line: %s' % line)
232 # Remove trailing . on perms
233 splitline[0] = splitline[0].rstrip('.')
234 # Remove leading . on paths
235 splitline[-1] = splitline[-1].lstrip('.')
236 # Drop fields we don't want to compare
237 del splitline[7]
238 del splitline[6]
239 del splitline[5]
240 del splitline[4]
241 del splitline[1]
242 filelist.append(' '.join(splitline))
243 return filelist
244
245
246class DevtoolTests(DevtoolBase):
247
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500248 def test_create_workspace(self):
249 # Check preconditions
250 result = runCmd('bitbake-layers show-layers')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400251 self.assertTrue('\nworkspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
Brad Bishop96ff1982019-08-19 13:50:42 -0400252 # remove conf/devtool.conf to avoid it corrupting tests
253 devtoolconf = os.path.join(self.builddir, 'conf', 'devtool.conf')
254 self.track_for_cleanup(devtoolconf)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500255 # Try creating a workspace layer with a specific path
256 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
257 self.track_for_cleanup(tempdir)
258 result = runCmd('devtool create-workspace %s' % tempdir)
259 self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
260 result = runCmd('bitbake-layers show-layers')
261 self.assertIn(tempdir, result.output)
262 # Try creating a workspace layer with the default path
263 self.track_for_cleanup(self.workspacedir)
264 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
265 result = runCmd('devtool create-workspace')
266 self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
267 result = runCmd('bitbake-layers show-layers')
268 self.assertNotIn(tempdir, result.output)
269 self.assertIn(self.workspacedir, result.output)
270
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800271class DevtoolAddTests(DevtoolBase):
272
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500273 def test_devtool_add(self):
274 # Fetch source
275 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
276 self.track_for_cleanup(tempdir)
277 pn = 'pv'
278 pv = '1.5.3'
279 url = 'http://www.ivarch.com/programs/sources/pv-1.5.3.tar.bz2'
280 result = runCmd('wget %s' % url, cwd=tempdir)
281 result = runCmd('tar xfv %s' % os.path.basename(url), cwd=tempdir)
282 srcdir = os.path.join(tempdir, '%s-%s' % (pn, pv))
283 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
284 # Test devtool add
285 self.track_for_cleanup(self.workspacedir)
286 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
287 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
288 result = runCmd('devtool add %s %s' % (pn, srcdir))
289 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
290 # Test devtool status
291 result = runCmd('devtool status')
292 recipepath = '%s/recipes/%s/%s_%s.bb' % (self.workspacedir, pn, pn, pv)
293 self.assertIn(recipepath, result.output)
294 self.assertIn(srcdir, result.output)
295 # Test devtool find-recipe
296 result = runCmd('devtool -q find-recipe %s' % pn)
297 self.assertEqual(recipepath, result.output.strip())
298 # Test devtool edit-recipe
299 result = runCmd('VISUAL="echo 123" devtool -q edit-recipe %s' % pn)
300 self.assertEqual('123 %s' % recipepath, result.output.strip())
301 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
302 bitbake('%s -c cleansstate' % pn)
303 # Test devtool build
304 result = runCmd('devtool build %s' % pn)
305 bb_vars = get_bb_vars(['D', 'bindir'], pn)
306 installdir = bb_vars['D']
307 self.assertTrue(installdir, 'Could not query installdir variable')
308 bindir = bb_vars['bindir']
309 self.assertTrue(bindir, 'Could not query bindir variable')
310 if bindir[0] == '/':
311 bindir = bindir[1:]
312 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
313
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500314 def test_devtool_add_git_local(self):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800315 # We need dbus built so that DEPENDS recognition works
316 bitbake('dbus')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317 # Fetch source from a remote URL, but do it outside of devtool
318 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
319 self.track_for_cleanup(tempdir)
320 pn = 'dbus-wait'
321 srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
322 # We choose an https:// git URL here to check rewriting the URL works
323 url = 'https://git.yoctoproject.org/git/dbus-wait'
324 # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
325 # instead of the directory name
326 result = runCmd('git clone %s noname' % url, cwd=tempdir)
327 srcdir = os.path.join(tempdir, 'noname')
328 result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
329 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
330 # Test devtool add
331 self.track_for_cleanup(self.workspacedir)
332 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
333 # Don't specify a name since we should be able to auto-detect it
334 result = runCmd('devtool add %s' % srcdir)
335 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
336 # Check the recipe name is correct
337 recipefile = get_bb_var('FILE', pn)
338 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
339 self.assertIn(recipefile, result.output)
340 # Test devtool status
341 result = runCmd('devtool status')
342 self.assertIn(pn, result.output)
343 self.assertIn(srcdir, result.output)
344 self.assertIn(recipefile, result.output)
345 checkvars = {}
346 checkvars['LICENSE'] = 'GPLv2'
347 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
348 checkvars['S'] = '${WORKDIR}/git'
349 checkvars['PV'] = '0.1+git${SRCPV}'
350 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https'
351 checkvars['SRCREV'] = srcrev
352 checkvars['DEPENDS'] = set(['dbus'])
353 self._test_recipe_contents(recipefile, checkvars, [])
354
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500355 def test_devtool_add_library(self):
356 # Fetch source
357 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
358 self.track_for_cleanup(tempdir)
359 version = '1.1'
360 url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version
361 result = runCmd('wget %s' % url, cwd=tempdir)
362 result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir)
363 srcdir = os.path.join(tempdir, 'libftdi1-%s' % version)
364 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
365 # Test devtool add (and use -V so we test that too)
366 self.track_for_cleanup(self.workspacedir)
367 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
368 result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version))
369 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
370 # Test devtool status
371 result = runCmd('devtool status')
372 self.assertIn('libftdi', result.output)
373 self.assertIn(srcdir, result.output)
374 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
375 bitbake('libftdi -c cleansstate')
376 # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it
377 # There's also the matter of it installing cmake files to a path we don't
378 # normally cover, which triggers the installed-vs-shipped QA test we have
379 # within do_package
380 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
381 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
382 with open(recipefile, 'a') as f:
383 f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n')
384 # We don't have the ability to pick up this dependency automatically yet...
385 f.write('\nDEPENDS += "libusb1"\n')
386 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
387 # Test devtool build
388 result = runCmd('devtool build libftdi')
389 bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
390 staging_libdir = bb_vars['TESTLIBOUTPUT']
391 self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
392 self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), "libftdi binary not found in STAGING_LIBDIR. Output of devtool build libftdi %s" % result.output)
393 # Test devtool reset
394 stampprefix = bb_vars['STAMP']
395 result = runCmd('devtool reset libftdi')
396 result = runCmd('devtool status')
397 self.assertNotIn('libftdi', result.output)
398 self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
399 matches = glob.glob(stampprefix + '*')
400 self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
401 self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
402
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500403 def test_devtool_add_fetch(self):
404 # Fetch source
405 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
406 self.track_for_cleanup(tempdir)
407 testver = '0.23'
Brad Bishop15ae2502019-06-18 21:44:24 -0400408 url = 'https://files.pythonhosted.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-%s.tar.gz' % testver
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500409 testrecipe = 'python-markupsafe'
410 srcdir = os.path.join(tempdir, testrecipe)
411 # Test devtool add
412 self.track_for_cleanup(self.workspacedir)
413 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
414 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
415 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
416 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
417 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
418 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
419 # Test devtool status
420 result = runCmd('devtool status')
421 self.assertIn(testrecipe, result.output)
422 self.assertIn(srcdir, result.output)
423 # Check recipe
424 recipefile = get_bb_var('FILE', testrecipe)
425 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
426 checkvars = {}
427 checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}'
428 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
429 self._test_recipe_contents(recipefile, checkvars, [])
430 # Try with version specified
431 result = runCmd('devtool reset -n %s' % testrecipe)
432 shutil.rmtree(srcdir)
433 fakever = '1.9'
434 result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
435 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
436 # Test devtool status
437 result = runCmd('devtool status')
438 self.assertIn(testrecipe, result.output)
439 self.assertIn(srcdir, result.output)
440 # Check recipe
441 recipefile = get_bb_var('FILE', testrecipe)
442 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
443 checkvars = {}
444 checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver
445 checkvars['SRC_URI'] = url
446 self._test_recipe_contents(recipefile, checkvars, [])
447
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500448 def test_devtool_add_fetch_git(self):
449 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
450 self.track_for_cleanup(tempdir)
451 url = 'gitsm://git.yoctoproject.org/mraa'
452 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
453 testrecipe = 'mraa'
454 srcdir = os.path.join(tempdir, testrecipe)
455 # Test devtool add
456 self.track_for_cleanup(self.workspacedir)
457 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
458 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
459 result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
460 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created: %s' % result.output)
461 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
462 # Test devtool status
463 result = runCmd('devtool status')
464 self.assertIn(testrecipe, result.output)
465 self.assertIn(srcdir, result.output)
466 # Check recipe
467 recipefile = get_bb_var('FILE', testrecipe)
468 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
469 checkvars = {}
470 checkvars['S'] = '${WORKDIR}/git'
471 checkvars['PV'] = '1.0+git${SRCPV}'
472 checkvars['SRC_URI'] = url
473 checkvars['SRCREV'] = '${AUTOREV}'
474 self._test_recipe_contents(recipefile, checkvars, [])
475 # Try with revision and version specified
476 result = runCmd('devtool reset -n %s' % testrecipe)
477 shutil.rmtree(srcdir)
478 url_rev = '%s;rev=%s' % (url, checkrev)
479 result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
480 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
481 # Test devtool status
482 result = runCmd('devtool status')
483 self.assertIn(testrecipe, result.output)
484 self.assertIn(srcdir, result.output)
485 # Check recipe
486 recipefile = get_bb_var('FILE', testrecipe)
487 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
488 checkvars = {}
489 checkvars['S'] = '${WORKDIR}/git'
490 checkvars['PV'] = '1.5+git${SRCPV}'
491 checkvars['SRC_URI'] = url
492 checkvars['SRCREV'] = checkrev
493 self._test_recipe_contents(recipefile, checkvars, [])
494
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500495 def test_devtool_add_fetch_simple(self):
496 # Fetch source from a remote URL, auto-detecting name
497 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
498 self.track_for_cleanup(tempdir)
499 testver = '1.6.0'
500 url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver
501 testrecipe = 'pv'
502 srcdir = os.path.join(self.workspacedir, 'sources', testrecipe)
503 # Test devtool add
504 self.track_for_cleanup(self.workspacedir)
505 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
506 result = runCmd('devtool add %s' % url)
507 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. %s' % result.output)
508 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
509 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
510 # Test devtool status
511 result = runCmd('devtool status')
512 self.assertIn(testrecipe, result.output)
513 self.assertIn(srcdir, result.output)
514 # Check recipe
515 recipefile = get_bb_var('FILE', testrecipe)
516 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
517 checkvars = {}
518 checkvars['S'] = None
519 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
520 self._test_recipe_contents(recipefile, checkvars, [])
521
Andrew Geissler82c905d2020-04-13 13:39:40 -0500522 def test_devtool_add_npm(self):
523 pn = 'savoirfairelinux-node-server-example'
524 pv = '1.0.0'
525 url = 'npm://registry.npmjs.org;package=@savoirfairelinux/node-server-example;version=' + pv
526 # Test devtool add
527 self.track_for_cleanup(self.workspacedir)
528 self.add_command_to_tearDown('bitbake -c cleansstate %s' % pn)
529 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
530 result = runCmd('devtool add \'%s\'' % url)
531 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
532 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, '%s_%s.bb' % (pn, pv)), 'Recipe not created')
533 self.assertExists(os.path.join(self.workspacedir, 'recipes', pn, pn, 'npm-shrinkwrap.json'), 'Shrinkwrap not created')
534 # Test devtool status
535 result = runCmd('devtool status')
536 self.assertIn(pn, result.output)
537 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
538 bitbake('%s -c cleansstate' % pn)
539 # Test devtool build
540 result = runCmd('devtool build %s' % pn)
541
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800542class DevtoolModifyTests(DevtoolBase):
543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 def test_devtool_modify(self):
545 import oe.path
546
547 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
548 self.track_for_cleanup(tempdir)
549 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500550 self.add_command_to_tearDown('bitbake -c clean mdadm')
Brad Bishop00e122a2019-10-05 11:10:57 -0400551 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500552 result = runCmd('devtool modify mdadm -x %s' % tempdir)
553 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
554 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
555 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
556 self.assertTrue(matches, 'bbappend not created %s' % result.output)
557
558 # Test devtool status
559 result = runCmd('devtool status')
560 self.assertIn('mdadm', result.output)
561 self.assertIn(tempdir, result.output)
562 self._check_src_repo(tempdir)
563
564 bitbake('mdadm -C unpack')
565
566 def check_line(checkfile, expected, message, present=True):
567 # Check for $expected, on a line on its own, in checkfile.
568 with open(checkfile, 'r') as f:
569 if present:
570 self.assertIn(expected + '\n', f, message)
571 else:
572 self.assertNotIn(expected + '\n', f, message)
573
574 modfile = os.path.join(tempdir, 'mdadm.8.in')
575 bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
576 pkgd = bb_vars['PKGD']
577 self.assertTrue(pkgd, 'Could not query PKGD variable')
578 mandir = bb_vars['mandir']
579 self.assertTrue(mandir, 'Could not query mandir variable')
580 manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
581
582 check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
583 check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
584
585 result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
586 check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
587
588 bitbake('mdadm -c package')
589 check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
590
591 result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
592 check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
593
594 bitbake('mdadm -c package')
595 check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
596
597 result = runCmd('devtool reset mdadm')
598 result = runCmd('devtool status')
599 self.assertNotIn('mdadm', result.output)
600
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500601 def test_devtool_buildclean(self):
602 def assertFile(path, *paths):
603 f = os.path.join(path, *paths)
604 self.assertExists(f)
605 def assertNoFile(path, *paths):
606 f = os.path.join(path, *paths)
607 self.assertNotExists(f)
608
609 # Clean up anything in the workdir/sysroot/sstate cache
610 bitbake('mdadm m4 -c cleansstate')
611 # Try modifying a recipe
612 tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
613 tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
614 builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
615 self.track_for_cleanup(tempdir_mdadm)
616 self.track_for_cleanup(tempdir_m4)
617 self.track_for_cleanup(builddir_m4)
618 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500619 self.add_command_to_tearDown('bitbake -c clean mdadm m4')
Brad Bishop00e122a2019-10-05 11:10:57 -0400620 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500621 self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
622 try:
623 runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
624 runCmd('devtool modify m4 -x %s' % tempdir_m4)
625 assertNoFile(tempdir_mdadm, 'mdadm')
626 assertNoFile(builddir_m4, 'src/m4')
627 result = bitbake('m4 -e')
628 result = bitbake('mdadm m4 -c compile')
629 self.assertEqual(result.status, 0)
630 assertFile(tempdir_mdadm, 'mdadm')
631 assertFile(builddir_m4, 'src/m4')
632 # Check that buildclean task exists and does call make clean
633 bitbake('mdadm m4 -c buildclean')
634 assertNoFile(tempdir_mdadm, 'mdadm')
635 assertNoFile(builddir_m4, 'src/m4')
Brad Bishop00e122a2019-10-05 11:10:57 -0400636 runCmd('echo "#Trigger rebuild" >> %s/Makefile' % tempdir_mdadm)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500637 bitbake('mdadm m4 -c compile')
638 assertFile(tempdir_mdadm, 'mdadm')
639 assertFile(builddir_m4, 'src/m4')
640 bitbake('mdadm m4 -c clean')
641 # Check that buildclean task is run before clean for B == S
642 assertNoFile(tempdir_mdadm, 'mdadm')
643 # Check that buildclean task is not run before clean for B != S
644 assertFile(builddir_m4, 'src/m4')
645 finally:
646 self.delete_recipeinc('m4')
647
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500648 def test_devtool_modify_invalid(self):
649 # Try modifying some recipes
650 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
651 self.track_for_cleanup(tempdir)
652 self.track_for_cleanup(self.workspacedir)
653 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
654
655 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
656 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
657 result = runCmd('bitbake-layers show-recipes gcc-source*')
658 for line in result.output.splitlines():
659 # just match those lines that contain a real target
660 m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
661 if m:
662 testrecipes.append(m.group('recipe'))
663 for testrecipe in testrecipes:
664 # Check it's a valid recipe
665 bitbake('%s -e' % testrecipe)
666 # devtool extract should fail
667 result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
668 self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output))
669 self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
670 self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
671 # devtool modify should fail
672 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
673 self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output))
674 self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
675
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500676 def test_devtool_modify_native(self):
677 # Check preconditions
678 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
679 # Try modifying some recipes
680 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
681 self.track_for_cleanup(tempdir)
682 self.track_for_cleanup(self.workspacedir)
683 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
684
685 bbclassextended = False
686 inheritnative = False
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500687 testrecipes = 'cdrtools-native mtools-native apt-native desktop-file-utils-native'.split()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500688 for testrecipe in testrecipes:
689 checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split()
690 if not bbclassextended:
691 bbclassextended = checkextend
692 if not inheritnative:
693 inheritnative = not checkextend
694 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)))
695 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output)
696 result = runCmd('devtool build %s' % testrecipe)
697 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output)
698 result = runCmd('devtool reset %s' % testrecipe)
699 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output)
700
701 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
702 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
703
704
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500705 def test_devtool_modify_git(self):
706 # Check preconditions
Brad Bishop316dfdd2018-06-25 12:45:53 -0400707 testrecipe = 'psplash'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500708 src_uri = get_bb_var('SRC_URI', testrecipe)
709 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
710 # Clean up anything in the workdir/sysroot/sstate cache
711 bitbake('%s -c cleansstate' % testrecipe)
712 # Try modifying a recipe
713 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
714 self.track_for_cleanup(tempdir)
715 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500716 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -0400717 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500718 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
Brad Bishop316dfdd2018-06-25 12:45:53 -0400719 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500720 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created. devtool output: %s' % result.output)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400721 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'psplash_*.bbappend'))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500722 self.assertTrue(matches, 'bbappend not created')
723 # Test devtool status
724 result = runCmd('devtool status')
725 self.assertIn(testrecipe, result.output)
726 self.assertIn(tempdir, result.output)
727 # Check git repo
728 self._check_src_repo(tempdir)
729 # Try building
730 bitbake(testrecipe)
731
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500732 def test_devtool_modify_localfiles(self):
733 # Check preconditions
734 testrecipe = 'lighttpd'
735 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
736 foundlocal = False
737 for item in src_uri:
738 if item.startswith('file://') and '.patch' not in item:
739 foundlocal = True
740 break
741 self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
742 # Clean up anything in the workdir/sysroot/sstate cache
743 bitbake('%s -c cleansstate' % testrecipe)
744 # Try modifying a recipe
745 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
746 self.track_for_cleanup(tempdir)
747 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500748 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -0400749 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500750 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
751 self.assertExists(os.path.join(tempdir, 'configure.ac'), 'Extracted source could not be found')
752 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
753 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
754 self.assertTrue(matches, 'bbappend not created')
755 # Test devtool status
756 result = runCmd('devtool status')
757 self.assertIn(testrecipe, result.output)
758 self.assertIn(tempdir, result.output)
759 # Try building
760 bitbake(testrecipe)
761
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500762 def test_devtool_modify_virtual(self):
763 # Try modifying a virtual recipe
764 virtrecipe = 'virtual/make'
765 realrecipe = 'make'
766 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
767 self.track_for_cleanup(tempdir)
768 self.track_for_cleanup(self.workspacedir)
769 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
770 result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir))
771 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
772 self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created')
773 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe))
774 self.assertTrue(matches, 'bbappend not created %s' % result.output)
775 # Test devtool status
776 result = runCmd('devtool status')
777 self.assertNotIn(virtrecipe, result.output)
778 self.assertIn(realrecipe, result.output)
779 # Check git repo
780 self._check_src_repo(tempdir)
781 # This is probably sufficient
782
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800783class DevtoolUpdateTests(DevtoolBase):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500784
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500785 def test_devtool_update_recipe(self):
786 # Check preconditions
787 testrecipe = 'minicom'
788 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
789 recipefile = bb_vars['FILE']
790 src_uri = bb_vars['SRC_URI']
791 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
792 self._check_repo_status(os.path.dirname(recipefile), [])
793 # First, modify a recipe
794 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
795 self.track_for_cleanup(tempdir)
796 self.track_for_cleanup(self.workspacedir)
797 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
798 # (don't bother with cleaning the recipe on teardown, we won't be building it)
799 # We don't use -x here so that we test the behaviour of devtool modify without it
800 result = runCmd('devtool modify %s %s' % (testrecipe, tempdir))
801 # Check git repo
802 self._check_src_repo(tempdir)
803 # Add a couple of commits
804 # FIXME: this only tests adding, need to also test update and remove
805 result = runCmd('echo "Additional line" >> README', cwd=tempdir)
806 result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
807 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
808 result = runCmd('git add devtool-new-file', cwd=tempdir)
809 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
810 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
811 result = runCmd('devtool update-recipe %s' % testrecipe)
812 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
813 ('??', '.*/0001-Change-the-README.patch$'),
814 ('??', '.*/0002-Add-a-new-file.patch$')]
815 self._check_repo_status(os.path.dirname(recipefile), expected_status)
816
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500817 def test_devtool_update_recipe_git(self):
818 # Check preconditions
819 testrecipe = 'mtd-utils'
820 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
821 recipefile = bb_vars['FILE']
822 src_uri = bb_vars['SRC_URI']
823 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
824 patches = []
825 for entry in src_uri.split():
826 if entry.startswith('file://') and entry.endswith('.patch'):
827 patches.append(entry[7:].split(';')[0])
828 self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe)
829 self._check_repo_status(os.path.dirname(recipefile), [])
830 # First, modify a recipe
831 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
832 self.track_for_cleanup(tempdir)
833 self.track_for_cleanup(self.workspacedir)
834 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
835 # (don't bother with cleaning the recipe on teardown, we won't be building it)
836 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
837 # Check git repo
838 self._check_src_repo(tempdir)
839 # Add a couple of commits
840 # FIXME: this only tests adding, need to also test update and remove
841 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
842 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
843 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
844 result = runCmd('git add devtool-new-file', cwd=tempdir)
845 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
846 self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
847 result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe)
848 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \
849 [(' D', '.*/%s$' % patch) for patch in patches]
850 self._check_repo_status(os.path.dirname(recipefile), expected_status)
851
852 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
853 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"']
854 srcurilines = src_uri.split()
855 srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
856 srcurilines.append('"')
857 removelines = ['SRCREV = ".*"'] + srcurilines
858 for line in result.output.splitlines():
859 if line.startswith('+++') or line.startswith('---'):
860 continue
861 elif line.startswith('+'):
862 matched = False
863 for item in addlines:
864 if re.match(item, line[1:].strip()):
865 matched = True
866 break
867 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
868 elif line.startswith('-'):
869 matched = False
870 for item in removelines:
871 if re.match(item, line[1:].strip()):
872 matched = True
873 break
874 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
875 # Now try with auto mode
876 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
877 result = runCmd('devtool update-recipe %s' % testrecipe)
878 result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile))
879 topleveldir = result.output.strip()
880 relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe)
881 expected_status = [(' M', os.path.relpath(recipefile, topleveldir)),
882 ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath),
883 ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)]
884 self._check_repo_status(os.path.dirname(recipefile), expected_status)
885
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500886 def test_devtool_update_recipe_append(self):
887 # Check preconditions
888 testrecipe = 'mdadm'
889 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
890 recipefile = bb_vars['FILE']
891 src_uri = bb_vars['SRC_URI']
892 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
893 self._check_repo_status(os.path.dirname(recipefile), [])
894 # First, modify a recipe
895 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
896 tempsrcdir = os.path.join(tempdir, 'source')
897 templayerdir = os.path.join(tempdir, 'layer')
898 self.track_for_cleanup(tempdir)
899 self.track_for_cleanup(self.workspacedir)
900 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
901 # (don't bother with cleaning the recipe on teardown, we won't be building it)
902 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
903 # Check git repo
904 self._check_src_repo(tempsrcdir)
905 # Add a commit
906 result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir)
907 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
908 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
909 # Create a temporary layer and add it to bblayers.conf
910 self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe')
911 # Create the bbappend
912 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
913 self.assertNotIn('WARNING:', result.output)
914 # Check recipe is still clean
915 self._check_repo_status(os.path.dirname(recipefile), [])
916 # Check bbappend was created
917 splitpath = os.path.dirname(recipefile).split(os.sep)
918 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
919 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
920 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch')
921 self.assertExists(patchfile, 'Patch file not created')
922
923 # Check bbappend contents
924 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
925 '\n',
926 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
927 '\n']
928 with open(bbappendfile, 'r') as f:
929 self.assertEqual(expectedlines, f.readlines())
930
931 # Check we can run it again and bbappend isn't modified
932 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
933 with open(bbappendfile, 'r') as f:
934 self.assertEqual(expectedlines, f.readlines())
935 # Drop new commit and check patch gets deleted
936 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
937 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
938 self.assertNotExists(patchfile, 'Patch file not deleted')
939 expectedlines2 = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
940 '\n']
941 with open(bbappendfile, 'r') as f:
942 self.assertEqual(expectedlines2, f.readlines())
943 # Put commit back and check we can run it if layer isn't in bblayers.conf
944 os.remove(bbappendfile)
945 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
946 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
947 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
948 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
949 self.assertExists(patchfile, 'Patch file not created (with disabled layer)')
950 with open(bbappendfile, 'r') as f:
951 self.assertEqual(expectedlines, f.readlines())
952 # Deleting isn't expected to work under these circumstances
953
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500954 def test_devtool_update_recipe_append_git(self):
955 # Check preconditions
956 testrecipe = 'mtd-utils'
957 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
958 recipefile = bb_vars['FILE']
959 src_uri = bb_vars['SRC_URI']
960 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
961 for entry in src_uri.split():
962 if entry.startswith('git://'):
963 git_uri = entry
964 break
965 self._check_repo_status(os.path.dirname(recipefile), [])
966 # First, modify a recipe
967 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
968 tempsrcdir = os.path.join(tempdir, 'source')
969 templayerdir = os.path.join(tempdir, 'layer')
970 self.track_for_cleanup(tempdir)
971 self.track_for_cleanup(self.workspacedir)
972 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
973 # (don't bother with cleaning the recipe on teardown, we won't be building it)
974 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
975 # Check git repo
976 self._check_src_repo(tempsrcdir)
977 # Add a commit
978 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
979 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
980 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
981 # Create a temporary layer
982 os.makedirs(os.path.join(templayerdir, 'conf'))
983 with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f:
984 f.write('BBPATH .= ":${LAYERDIR}"\n')
985 f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n')
986 f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n')
987 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
988 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
989 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
Brad Bishop316dfdd2018-06-25 12:45:53 -0400990 f.write('LAYERSERIES_COMPAT_oeselftesttemplayer = "${LAYERSERIES_COMPAT_core}"\n')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500991 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
992 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
993 # Create the bbappend
994 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
995 self.assertNotIn('WARNING:', result.output)
996 # Check recipe is still clean
997 self._check_repo_status(os.path.dirname(recipefile), [])
998 # Check bbappend was created
999 splitpath = os.path.dirname(recipefile).split(os.sep)
1000 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
1001 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
1002 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1003
1004 # Check bbappend contents
1005 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1006 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1007 '\n',
1008 'SRC_URI = "%s"\n' % git_uri,
1009 '\n'])
1010 with open(bbappendfile, 'r') as f:
1011 self.assertEqual(expectedlines, set(f.readlines()))
1012
1013 # Check we can run it again and bbappend isn't modified
1014 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1015 with open(bbappendfile, 'r') as f:
1016 self.assertEqual(expectedlines, set(f.readlines()))
1017 # Drop new commit and check SRCREV changes
1018 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
1019 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1020 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1021 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1022 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1023 '\n',
1024 'SRC_URI = "%s"\n' % git_uri,
1025 '\n'])
1026 with open(bbappendfile, 'r') as f:
1027 self.assertEqual(expectedlines, set(f.readlines()))
1028 # Put commit back and check we can run it if layer isn't in bblayers.conf
1029 os.remove(bbappendfile)
1030 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
1031 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
1032 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
1033 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
1034 self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created')
1035 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
1036 expectedlines = set(['SRCREV = "%s"\n' % result.output,
1037 '\n',
1038 'SRC_URI = "%s"\n' % git_uri,
1039 '\n'])
1040 with open(bbappendfile, 'r') as f:
1041 self.assertEqual(expectedlines, set(f.readlines()))
1042 # Deleting isn't expected to work under these circumstances
1043
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001044 def test_devtool_update_recipe_local_files(self):
1045 """Check that local source files are copied over instead of patched"""
1046 testrecipe = 'makedevs'
1047 recipefile = get_bb_var('FILE', testrecipe)
1048 # Setup srctree for modifying the recipe
1049 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1050 self.track_for_cleanup(tempdir)
1051 self.track_for_cleanup(self.workspacedir)
1052 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1053 # (don't bother with cleaning the recipe on teardown, we won't be
1054 # building it)
1055 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1056 # Check git repo
1057 self._check_src_repo(tempdir)
1058 # Try building just to ensure we haven't broken that
1059 bitbake("%s" % testrecipe)
1060 # Edit / commit local source
1061 runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir)
1062 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
1063 runCmd('echo "Bar" > new-file', cwd=tempdir)
1064 runCmd('git add new-file', cwd=tempdir)
1065 runCmd('git commit -m "Add new file"', cwd=tempdir)
1066 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1067 os.path.dirname(recipefile))
1068 runCmd('devtool update-recipe %s' % testrecipe)
1069 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1070 (' M', '.*/makedevs/makedevs.c$'),
1071 ('??', '.*/makedevs/new-local$'),
1072 ('??', '.*/makedevs/0001-Add-new-file.patch$')]
1073 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1074
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001075 def test_devtool_update_recipe_local_files_2(self):
1076 """Check local source files support when oe-local-files is in Git"""
Brad Bishop316dfdd2018-06-25 12:45:53 -04001077 testrecipe = 'devtool-test-local'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001078 recipefile = get_bb_var('FILE', testrecipe)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001079 recipedir = os.path.dirname(recipefile)
1080 result = runCmd('git status --porcelain .', cwd=recipedir)
1081 if result.output.strip():
1082 self.fail('Recipe directory for %s contains uncommitted changes' % testrecipe)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001083 # Setup srctree for modifying the recipe
1084 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1085 self.track_for_cleanup(tempdir)
1086 self.track_for_cleanup(self.workspacedir)
1087 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1088 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1089 # Check git repo
1090 self._check_src_repo(tempdir)
1091 # Add oe-local-files to Git
1092 runCmd('rm oe-local-files/.gitignore', cwd=tempdir)
1093 runCmd('git add oe-local-files', cwd=tempdir)
1094 runCmd('git commit -m "Add local sources"', cwd=tempdir)
1095 # Edit / commit local sources
Brad Bishop316dfdd2018-06-25 12:45:53 -04001096 runCmd('echo "# Foobar" >> oe-local-files/file1', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001097 runCmd('git commit -am "Edit existing file"', cwd=tempdir)
Brad Bishop316dfdd2018-06-25 12:45:53 -04001098 runCmd('git rm oe-local-files/file2', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001099 runCmd('git commit -m"Remove file"', cwd=tempdir)
1100 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
1101 runCmd('git add oe-local-files/new-local', cwd=tempdir)
1102 runCmd('git commit -m "Add new local file"', cwd=tempdir)
1103 runCmd('echo "Gar" > new-file', cwd=tempdir)
1104 runCmd('git add new-file', cwd=tempdir)
1105 runCmd('git commit -m "Add new file"', cwd=tempdir)
1106 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1107 os.path.dirname(recipefile))
1108 # Checkout unmodified file to working copy -> devtool should still pick
1109 # the modified version from HEAD
Brad Bishop316dfdd2018-06-25 12:45:53 -04001110 runCmd('git checkout HEAD^ -- oe-local-files/file1', cwd=tempdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001111 runCmd('devtool update-recipe %s' % testrecipe)
1112 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
Brad Bishop316dfdd2018-06-25 12:45:53 -04001113 (' M', '.*/file1$'),
1114 (' D', '.*/file2$'),
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001115 ('??', '.*/new-local$'),
1116 ('??', '.*/0001-Add-new-file.patch$')]
1117 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1118
Andrew Geissler4ed12e12020-06-05 18:00:41 -05001119 def test_devtool_update_recipe_with_gitignore(self):
1120 # First, modify the recipe
1121 testrecipe = 'devtool-test-ignored'
1122 bb_vars = get_bb_vars(['FILE'], testrecipe)
1123 recipefile = bb_vars['FILE']
1124 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch')
1125 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, testrecipe + '.patch.expected')
1126 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1127 self.track_for_cleanup(tempdir)
1128 self.track_for_cleanup(self.workspacedir)
1129 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1130 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1131 result = runCmd('devtool modify %s' % testrecipe)
1132 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1133 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
1134 # Check recipe got changed as expected
1135 with open(newpatchfile, 'r') as f:
1136 desiredlines = f.readlines()
1137 with open(patchfile, 'r') as f:
1138 newlines = f.readlines()
1139 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
1140 # which changes the metadata subject which is added into the patch, but keep
1141 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
1142 # devtool-test-ignored manually, then it should generate exactly the same .patch file
1143 self.assertEqual(desiredlines[5:], newlines[5:])
1144
1145 def test_devtool_update_recipe_long_filename(self):
1146 # First, modify the recipe
1147 testrecipe = 'devtool-test-long-filename'
1148 bb_vars = get_bb_vars(['FILE'], testrecipe)
1149 recipefile = bb_vars['FILE']
1150 patchfilename = '0001-I-ll-patch-you-only-if-devtool-lets-me-to-do-it-corr.patch'
1151 patchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename)
1152 newpatchfile = os.path.join(os.path.dirname(recipefile), testrecipe, patchfilename + '.expected')
1153 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1154 self.track_for_cleanup(tempdir)
1155 self.track_for_cleanup(self.workspacedir)
1156 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1157 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1158 result = runCmd('devtool modify %s' % testrecipe)
1159 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1160 result = runCmd('devtool finish --force-patch-refresh %s meta-selftest' % testrecipe)
1161 # Check recipe got changed as expected
1162 with open(newpatchfile, 'r') as f:
1163 desiredlines = f.readlines()
1164 with open(patchfile, 'r') as f:
1165 newlines = f.readlines()
1166 # Ignore the initial lines, because oe-selftest creates own meta-selftest repo
1167 # which changes the metadata subject which is added into the patch, but keep
1168 # .patch.expected as it is in case someone runs devtool finish --force-patch-refresh
1169 # devtool-test-ignored manually, then it should generate exactly the same .patch file
1170 self.assertEqual(desiredlines[5:], newlines[5:])
1171
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001172 def test_devtool_update_recipe_local_files_3(self):
1173 # First, modify the recipe
1174 testrecipe = 'devtool-test-localonly'
1175 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1176 recipefile = bb_vars['FILE']
1177 src_uri = bb_vars['SRC_URI']
1178 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1179 self.track_for_cleanup(tempdir)
1180 self.track_for_cleanup(self.workspacedir)
1181 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1182 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1183 result = runCmd('devtool modify %s' % testrecipe)
1184 # Modify one file
1185 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files'))
1186 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1187 result = runCmd('devtool update-recipe %s' % testrecipe)
1188 expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
1189 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1190
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001191 def test_devtool_update_recipe_local_patch_gz(self):
1192 # First, modify the recipe
1193 testrecipe = 'devtool-test-patch-gz'
1194 if get_bb_var('DISTRO') == 'poky-tiny':
1195 self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
1196 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1197 recipefile = bb_vars['FILE']
1198 src_uri = bb_vars['SRC_URI']
1199 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1200 self.track_for_cleanup(tempdir)
1201 self.track_for_cleanup(self.workspacedir)
1202 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1203 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1204 result = runCmd('devtool modify %s' % testrecipe)
1205 # Modify one file
1206 srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
1207 runCmd('echo "Another line" >> README', cwd=srctree)
1208 runCmd('git commit -a --amend --no-edit', cwd=srctree)
1209 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1210 result = runCmd('devtool update-recipe %s' % testrecipe)
1211 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
1212 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1213 patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
1214 result = runCmd('file %s' % patch_gz)
1215 if 'gzip compressed data' not in result.output:
1216 self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
1217
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001218 def test_devtool_update_recipe_local_files_subdir(self):
1219 # Try devtool update-recipe on a recipe that has a file with subdir= set in
1220 # SRC_URI such that it overwrites a file that was in an archive that
1221 # was also in SRC_URI
1222 # First, modify the recipe
1223 testrecipe = 'devtool-test-subdir'
1224 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1225 recipefile = bb_vars['FILE']
1226 src_uri = bb_vars['SRC_URI']
1227 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1228 self.track_for_cleanup(tempdir)
1229 self.track_for_cleanup(self.workspacedir)
1230 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1231 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1232 result = runCmd('devtool modify %s' % testrecipe)
1233 testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
1234 self.assertExists(testfile, 'Extracted source could not be found')
1235 with open(testfile, 'r') as f:
1236 contents = f.read().rstrip()
1237 self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
1238 # Test devtool update-recipe without modifying any files
1239 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1240 result = runCmd('devtool update-recipe %s' % testrecipe)
1241 expected_status = []
1242 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1243
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001244class DevtoolExtractTests(DevtoolBase):
1245
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001246 def test_devtool_extract(self):
1247 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1248 # Try devtool extract
1249 self.track_for_cleanup(tempdir)
1250 self.track_for_cleanup(self.workspacedir)
1251 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1252 result = runCmd('devtool extract matchbox-terminal %s' % tempdir)
1253 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1254 self._check_src_repo(tempdir)
1255
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001256 def test_devtool_extract_virtual(self):
1257 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1258 # Try devtool extract
1259 self.track_for_cleanup(tempdir)
1260 self.track_for_cleanup(self.workspacedir)
1261 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1262 result = runCmd('devtool extract virtual/make %s' % tempdir)
1263 self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found')
1264 self._check_src_repo(tempdir)
1265
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001266 def test_devtool_reset_all(self):
1267 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1268 self.track_for_cleanup(tempdir)
1269 self.track_for_cleanup(self.workspacedir)
1270 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1271 testrecipe1 = 'mdadm'
1272 testrecipe2 = 'cronie'
1273 result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
1274 result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
1275 result = runCmd('devtool build %s' % testrecipe1)
1276 result = runCmd('devtool build %s' % testrecipe2)
1277 stampprefix1 = get_bb_var('STAMP', testrecipe1)
1278 self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
1279 stampprefix2 = get_bb_var('STAMP', testrecipe2)
1280 self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
1281 result = runCmd('devtool reset -a')
1282 self.assertIn(testrecipe1, result.output)
1283 self.assertIn(testrecipe2, result.output)
1284 result = runCmd('devtool status')
1285 self.assertNotIn(testrecipe1, result.output)
1286 self.assertNotIn(testrecipe2, result.output)
1287 matches1 = glob.glob(stampprefix1 + '*')
1288 self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
1289 matches2 = glob.glob(stampprefix2 + '*')
1290 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
1291
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001292 def test_devtool_deploy_target(self):
1293 # NOTE: Whilst this test would seemingly be better placed as a runtime test,
1294 # unfortunately the runtime tests run under bitbake and you can't run
1295 # devtool within bitbake (since devtool needs to run bitbake itself).
1296 # Additionally we are testing build-time functionality as well, so
1297 # really this has to be done as an oe-selftest test.
1298 #
1299 # Check preconditions
1300 machine = get_bb_var('MACHINE')
1301 if not machine.startswith('qemu'):
1302 self.skipTest('This test only works with qemu machines')
1303 if not os.path.exists('/etc/runqemu-nosudo'):
1304 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1305 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
1306 if result.status != 0:
1307 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
1308 if result.status != 0:
1309 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
1310 for line in result.output.splitlines():
1311 if line.startswith('tap'):
1312 break
1313 else:
1314 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1315 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1316 # Definitions
1317 testrecipe = 'mdadm'
1318 testfile = '/sbin/mdadm'
1319 testimage = 'oe-selftest-image'
1320 testcommand = '/sbin/mdadm --help'
1321 # Build an image to run
1322 bitbake("%s qemu-native qemu-helper-native" % testimage)
1323 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1324 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
1325 self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
1326 # Clean recipe so the first deploy will fail
1327 bitbake("%s -c clean" % testrecipe)
1328 # Try devtool modify
1329 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1330 self.track_for_cleanup(tempdir)
1331 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001332 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
Brad Bishop00e122a2019-10-05 11:10:57 -04001333 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001334 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1335 # Test that deploy-target at this point fails (properly)
1336 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True)
1337 self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
1338 self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
1339 result = runCmd('devtool build %s' % testrecipe)
1340 # First try a dry-run of deploy-target
1341 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe)
1342 self.assertIn(' %s' % testfile, result.output)
1343 # Boot the image
1344 with runqemu(testimage) as qemu:
1345 # Now really test deploy-target
1346 result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1347 # Run a test command to see if it was installed properly
1348 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
1349 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
1350 # Check if it deployed all of the files with the right ownership/perms
1351 # First look on the host - need to do this under pseudo to get the correct ownership/perms
1352 bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
1353 installdir = bb_vars['D']
1354 fakerootenv = bb_vars['FAKEROOTENV']
1355 fakerootcmd = bb_vars['FAKEROOTCMD']
Brad Bishop19323692019-04-05 15:28:33 -04001356 result = runCmd('%s %s find . -type f -exec ls -l {} \\;' % (fakerootenv, fakerootcmd), cwd=installdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001357 filelist1 = self._process_ls_output(result.output)
1358
1359 # Now look on the target
1360 tempdir2 = tempfile.mkdtemp(prefix='devtoolqa')
1361 self.track_for_cleanup(tempdir2)
1362 tmpfilelist = os.path.join(tempdir2, 'files.txt')
1363 with open(tmpfilelist, 'w') as f:
1364 for line in filelist1:
1365 splitline = line.split()
1366 f.write(splitline[-1] + '\n')
1367 result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip))
1368 filelist2 = self._process_ls_output(result.output)
1369 filelist1.sort(key=lambda item: item.split()[-1])
1370 filelist2.sort(key=lambda item: item.split()[-1])
1371 self.assertEqual(filelist1, filelist2)
1372 # Test undeploy-target
1373 result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1374 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
1375 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
1376
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001377 def test_devtool_build_image(self):
1378 """Test devtool build-image plugin"""
1379 # Check preconditions
1380 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1381 image = 'core-image-minimal'
1382 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001383 self.add_command_to_tearDown('bitbake -c clean %s' % image)
Brad Bishop00e122a2019-10-05 11:10:57 -04001384 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001385 bitbake('%s -c clean' % image)
1386 # Add target and native recipes to workspace
1387 recipes = ['mdadm', 'parted-native']
1388 for recipe in recipes:
1389 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1390 self.track_for_cleanup(tempdir)
1391 self.add_command_to_tearDown('bitbake -c clean %s' % recipe)
1392 runCmd('devtool modify %s -x %s' % (recipe, tempdir))
1393 # Try to build image
1394 result = runCmd('devtool build-image %s' % image)
1395 self.assertNotEqual(result, 0, 'devtool build-image failed')
1396 # Check if image contains expected packages
1397 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1398 image_link_name = get_bb_var('IMAGE_LINK_NAME', image)
1399 reqpkgs = [item for item in recipes if not item.endswith('-native')]
1400 with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f:
1401 for line in f:
1402 splitval = line.split()
1403 if splitval:
1404 pkg = splitval[0]
1405 if pkg in reqpkgs:
1406 reqpkgs.remove(pkg)
1407 if reqpkgs:
1408 self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs))
1409
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001410class DevtoolUpgradeTests(DevtoolBase):
1411
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001412 def test_devtool_upgrade(self):
1413 # Check preconditions
1414 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1415 self.track_for_cleanup(self.workspacedir)
1416 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1417 # Check parameters
1418 result = runCmd('devtool upgrade -h')
1419 for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
1420 self.assertIn(param, result.output)
1421 # For the moment, we are using a real recipe.
1422 recipe = 'devtool-upgrade-test1'
1423 version = '1.6.0'
1424 oldrecipefile = get_bb_var('FILE', recipe)
1425 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1426 self.track_for_cleanup(tempdir)
1427 # Check that recipe is not already under devtool control
1428 result = runCmd('devtool status')
1429 self.assertNotIn(recipe, result.output)
1430 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1431 # we are downgrading instead of upgrading.
1432 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1433 # Check if srctree at least is populated
1434 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version))
1435 # Check new recipe subdirectory is present
1436 self.assertExists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version)), 'Recipe folder should exist')
1437 # Check new recipe file is present
1438 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1439 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1440 # Check devtool status and make sure recipe is present
1441 result = runCmd('devtool status')
1442 self.assertIn(recipe, result.output)
1443 self.assertIn(tempdir, result.output)
1444 # Check recipe got changed as expected
1445 with open(oldrecipefile + '.upgraded', 'r') as f:
1446 desiredlines = f.readlines()
1447 with open(newrecipefile, 'r') as f:
1448 newlines = f.readlines()
1449 self.assertEqual(desiredlines, newlines)
1450 # Check devtool reset recipe
1451 result = runCmd('devtool reset %s -n' % recipe)
1452 result = runCmd('devtool status')
1453 self.assertNotIn(recipe, result.output)
1454 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1455
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001456 def test_devtool_upgrade_git(self):
1457 # Check preconditions
1458 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1459 self.track_for_cleanup(self.workspacedir)
1460 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1461 recipe = 'devtool-upgrade-test2'
1462 commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
1463 oldrecipefile = get_bb_var('FILE', recipe)
1464 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1465 self.track_for_cleanup(tempdir)
1466 # Check that recipe is not already under devtool control
1467 result = runCmd('devtool status')
1468 self.assertNotIn(recipe, result.output)
1469 # Check upgrade
1470 result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit))
1471 # Check if srctree at least is populated
1472 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
1473 # Check new recipe file is present
1474 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
1475 self.assertExists(newrecipefile, 'Recipe file should exist after upgrade')
1476 # Check devtool status and make sure recipe is present
1477 result = runCmd('devtool status')
1478 self.assertIn(recipe, result.output)
1479 self.assertIn(tempdir, result.output)
1480 # Check recipe got changed as expected
1481 with open(oldrecipefile + '.upgraded', 'r') as f:
1482 desiredlines = f.readlines()
1483 with open(newrecipefile, 'r') as f:
1484 newlines = f.readlines()
1485 self.assertEqual(desiredlines, newlines)
1486 # Check devtool reset recipe
1487 result = runCmd('devtool reset %s -n' % recipe)
1488 result = runCmd('devtool status')
1489 self.assertNotIn(recipe, result.output)
1490 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting')
1491
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001492 def test_devtool_layer_plugins(self):
1493 """Test that devtool can use plugins from other layers.
1494
1495 This test executes the selftest-reverse command from meta-selftest."""
1496
1497 self.track_for_cleanup(self.workspacedir)
1498 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1499
1500 s = "Microsoft Made No Profit From Anyone's Zunes Yo"
1501 result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
1502 self.assertEqual(result.output, s[::-1])
1503
1504 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
1505 dstdir = basedstdir
1506 self.assertExists(dstdir)
1507 for p in paths:
1508 dstdir = os.path.join(dstdir, p)
1509 if not os.path.exists(dstdir):
1510 os.makedirs(dstdir)
Andrew Geissler475cb722020-07-10 16:00:51 -05001511 if p == "lib":
1512 # Can race with other tests
1513 self.add_command_to_tearDown('rmdir --ignore-fail-on-non-empty %s' % dstdir)
1514 else:
1515 self.track_for_cleanup(dstdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001516 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
1517 if srcfile != dstfile:
1518 shutil.copy(srcfile, dstfile)
1519 self.track_for_cleanup(dstfile)
1520
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001521 def test_devtool_load_plugin(self):
1522 """Test that devtool loads only the first found plugin in BBPATH."""
1523
1524 self.track_for_cleanup(self.workspacedir)
1525 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1526
1527 devtool = runCmd("which devtool")
1528 fromname = runCmd("devtool --quiet pluginfile")
1529 srcfile = fromname.output
1530 bbpath = get_bb_var('BBPATH')
1531 searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
1532 plugincontent = []
1533 with open(srcfile) as fh:
1534 plugincontent = fh.readlines()
1535 try:
1536 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
1537 for path in searchpath:
1538 self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
1539 result = runCmd("devtool --quiet count")
1540 self.assertEqual(result.output, '1')
1541 result = runCmd("devtool --quiet multiloaded")
1542 self.assertEqual(result.output, "no")
1543 for path in searchpath:
1544 result = runCmd("devtool --quiet bbdir")
1545 self.assertEqual(result.output, path)
1546 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
1547 finally:
1548 with open(srcfile, 'w') as fh:
1549 fh.writelines(plugincontent)
1550
1551 def _setup_test_devtool_finish_upgrade(self):
1552 # Check preconditions
1553 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1554 self.track_for_cleanup(self.workspacedir)
1555 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1556 # Use a "real" recipe from meta-selftest
1557 recipe = 'devtool-upgrade-test1'
1558 oldversion = '1.5.3'
1559 newversion = '1.6.0'
1560 oldrecipefile = get_bb_var('FILE', recipe)
1561 recipedir = os.path.dirname(oldrecipefile)
1562 result = runCmd('git status --porcelain .', cwd=recipedir)
1563 if result.output.strip():
1564 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1565 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1566 self.track_for_cleanup(tempdir)
1567 # Check that recipe is not already under devtool control
1568 result = runCmd('devtool status')
1569 self.assertNotIn(recipe, result.output)
1570 # Do the upgrade
1571 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
1572 # Check devtool status and make sure recipe is present
1573 result = runCmd('devtool status')
1574 self.assertIn(recipe, result.output)
1575 self.assertIn(tempdir, result.output)
1576 # Make a change to the source
1577 result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
1578 result = runCmd('git status --porcelain', cwd=tempdir)
1579 self.assertIn('M src/pv/number.c', result.output)
1580 result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
1581 # Check if patch is there
1582 recipedir = os.path.dirname(oldrecipefile)
1583 olddir = os.path.join(recipedir, recipe + '-' + oldversion)
1584 patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
Brad Bishop6dbb3162019-11-25 09:41:34 -05001585 backportedpatchfn = 'backported.patch'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001586 self.assertExists(os.path.join(olddir, patchfn), 'Original patch file does not exist')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001587 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Backported patch file does not exist')
1588 return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001589
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001590 def test_devtool_finish_upgrade_origlayer(self):
Brad Bishop6dbb3162019-11-25 09:41:34 -05001591 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001592 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1593 self.assertIn('/meta-selftest/', recipedir)
1594 # Try finish to the original layer
1595 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1596 result = runCmd('devtool finish %s meta-selftest' % recipe)
1597 result = runCmd('devtool status')
1598 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1599 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1600 self.assertNotExists(oldrecipefile, 'Old recipe file should have been deleted but wasn\'t')
1601 self.assertNotExists(os.path.join(olddir, patchfn), 'Old patch file should have been deleted but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001602 self.assertNotExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should have been deleted but wasn\'t')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001603 newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
1604 newdir = os.path.join(recipedir, recipe + '-' + newversion)
1605 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
1606 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001607 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001608 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001609 with open(newrecipefile, 'r') as f:
1610 newcontent = f.read()
1611 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
1612 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
1613 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
1614 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
1615
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001616
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001617 def test_devtool_finish_upgrade_otherlayer(self):
Brad Bishop6dbb3162019-11-25 09:41:34 -05001618 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn, backportedpatchfn = self._setup_test_devtool_finish_upgrade()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001619 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1620 self.assertIn('/meta-selftest/', recipedir)
1621 # Try finish to a different layer - should create a bbappend
1622 # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
1623 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1624 oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
1625 newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
1626 newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
1627 self.track_for_cleanup(newrecipedir)
1628 result = runCmd('devtool finish %s oe-core' % recipe)
1629 result = runCmd('devtool status')
1630 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1631 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1632 self.assertExists(oldrecipefile, 'Old recipe file should not have been deleted')
1633 self.assertExists(os.path.join(olddir, patchfn), 'Old patch file should not have been deleted')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001634 self.assertExists(os.path.join(olddir, backportedpatchfn), 'Old backported patch file should not have been deleted')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001635 newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
1636 self.assertExists(newrecipefile, 'New recipe file should have been copied into existing layer but wasn\'t')
1637 self.assertExists(os.path.join(newdir, patchfn), 'Patch file should have been copied into new directory but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001638 self.assertNotExists(os.path.join(newdir, backportedpatchfn), 'Backported patch file should not have been copied into new directory but was')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001639 self.assertExists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch'), 'New patch file should have been created but wasn\'t')
Brad Bishop6dbb3162019-11-25 09:41:34 -05001640 with open(newrecipefile, 'r') as f:
1641 newcontent = f.read()
1642 self.assertNotIn(backportedpatchfn, newcontent, "Backported patch should have been removed from the recipe but wasn't")
1643 self.assertIn(patchfn, newcontent, "Old patch should have not been removed from the recipe but was")
1644 self.assertIn("0002-Add-a-comment-to-the-code.patch", newcontent, "New patch should have been added to the recipe but wasn't")
1645 self.assertIn("http://www.ivarch.com/programs/sources/pv-${PV}.tar.gz", newcontent, "New recipe no longer has upstream source in SRC_URI")
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001646
1647 def _setup_test_devtool_finish_modify(self):
1648 # Check preconditions
1649 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1650 # Try modifying a recipe
1651 self.track_for_cleanup(self.workspacedir)
1652 recipe = 'mdadm'
1653 oldrecipefile = get_bb_var('FILE', recipe)
1654 recipedir = os.path.dirname(oldrecipefile)
1655 result = runCmd('git status --porcelain .', cwd=recipedir)
1656 if result.output.strip():
1657 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1658 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1659 self.track_for_cleanup(tempdir)
1660 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1661 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
1662 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
1663 # Test devtool status
1664 result = runCmd('devtool status')
1665 self.assertIn(recipe, result.output)
1666 self.assertIn(tempdir, result.output)
1667 # Make a change to the source
1668 result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
1669 result = runCmd('git status --porcelain', cwd=tempdir)
1670 self.assertIn('M maps.c', result.output)
1671 result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
1672 for entry in os.listdir(recipedir):
1673 filesdir = os.path.join(recipedir, entry)
1674 if os.path.isdir(filesdir):
1675 break
1676 else:
1677 self.fail('Unable to find recipe files directory for %s' % recipe)
1678 return recipe, oldrecipefile, recipedir, filesdir
1679
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001680 def test_devtool_finish_modify_origlayer(self):
1681 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
1682 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1683 self.assertIn('/meta/', recipedir)
1684 # Try finish to the original layer
1685 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1686 result = runCmd('devtool finish %s meta' % recipe)
1687 result = runCmd('devtool status')
1688 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1689 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1690 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
1691 ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
1692 self._check_repo_status(recipedir, expected_status)
1693
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001694 def test_devtool_finish_modify_otherlayer(self):
1695 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
1696 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1697 self.assertIn('/meta/', recipedir)
1698 relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
1699 appenddir = os.path.join(get_test_layer(), relpth)
1700 self.track_for_cleanup(appenddir)
1701 # Try finish to the original layer
1702 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1703 result = runCmd('devtool finish %s meta-selftest' % recipe)
1704 result = runCmd('devtool status')
1705 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1706 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after finish')
1707 result = runCmd('git status --porcelain .', cwd=recipedir)
1708 if result.output.strip():
1709 self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
1710 recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
1711 recipefn = recipefn.split('_')[0] + '_%'
1712 appendfile = os.path.join(appenddir, recipefn + '.bbappend')
1713 self.assertExists(appendfile, 'bbappend %s should have been created but wasn\'t' % appendfile)
1714 newdir = os.path.join(appenddir, recipe)
1715 files = os.listdir(newdir)
1716 foundpatch = None
1717 for fn in files:
1718 if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
1719 foundpatch = fn
1720 if not foundpatch:
1721 self.fail('No patch file created next to bbappend')
1722 files.remove(foundpatch)
1723 if files:
1724 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
1725
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001726 def test_devtool_rename(self):
1727 # Check preconditions
1728 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1729 self.track_for_cleanup(self.workspacedir)
1730 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1731
1732 # First run devtool add
1733 # We already have this recipe in OE-Core, but that doesn't matter
1734 recipename = 'i2c-tools'
1735 recipever = '3.1.2'
1736 recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
1737 url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
1738 def add_recipe():
1739 result = runCmd('devtool add %s' % url)
1740 self.assertExists(recipefile, 'Expected recipe file not created')
1741 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory not created')
1742 checkvars = {}
1743 checkvars['S'] = None
1744 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
1745 self._test_recipe_contents(recipefile, checkvars, [])
1746 add_recipe()
1747 # Now rename it - change both name and version
1748 newrecipename = 'mynewrecipe'
1749 newrecipever = '456'
1750 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
1751 result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
1752 self.assertExists(newrecipefile, 'Recipe file not renamed')
1753 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
1754 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
1755 self.assertExists(newsrctree, 'Source directory not renamed')
1756 checkvars = {}
1757 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever)
1758 checkvars['SRC_URI'] = url
1759 self._test_recipe_contents(newrecipefile, checkvars, [])
1760 # Try again - change just name this time
1761 result = runCmd('devtool reset -n %s' % newrecipename)
1762 shutil.rmtree(newsrctree)
1763 add_recipe()
1764 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
1765 result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
1766 self.assertExists(newrecipefile, 'Recipe file not renamed')
1767 self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists')
1768 self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed')
1769 checkvars = {}
1770 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename
1771 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
1772 self._test_recipe_contents(newrecipefile, checkvars, [])
1773 # Try again - change just version this time
1774 result = runCmd('devtool reset -n %s' % newrecipename)
1775 shutil.rmtree(newsrctree)
1776 add_recipe()
1777 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
1778 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
1779 self.assertExists(newrecipefile, 'Recipe file not renamed')
1780 self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists')
1781 checkvars = {}
1782 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever
1783 checkvars['SRC_URI'] = url
1784 self._test_recipe_contents(newrecipefile, checkvars, [])
1785
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001786 def test_devtool_virtual_kernel_modify(self):
1787 """
1788 Summary: The purpose of this test case is to verify that
1789 devtool modify works correctly when building
1790 the kernel.
1791 Dependencies: NA
1792 Steps: 1. Build kernel with bitbake.
1793 2. Save the config file generated.
1794 3. Clean the environment.
1795 4. Use `devtool modify virtual/kernel` to validate following:
1796 4.1 The source is checked out correctly.
1797 4.2 The resulting configuration is the same as
1798 what was get on step 2.
1799 4.3 The Kernel can be build correctly.
1800 4.4 Changes made on the source are reflected on the
1801 subsequent builds.
1802 4.5 Changes on the configuration are reflected on the
1803 subsequent builds
1804 Expected: devtool modify is able to checkout the source of the kernel
1805 and modification to the source and configurations are reflected
1806 when building the kernel.
1807 """
1808 kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001809 # Clean up the environment
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001810 bitbake('%s -c clean' % kernel_provider)
1811 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1812 tempdir_cfg = tempfile.mkdtemp(prefix='config_qa')
1813 self.track_for_cleanup(tempdir)
1814 self.track_for_cleanup(tempdir_cfg)
1815 self.track_for_cleanup(self.workspacedir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001816 self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
Brad Bishop00e122a2019-10-05 11:10:57 -04001817 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001818 #Step 1
1819 #Here is just generated the config file instead of all the kernel to optimize the
1820 #time of executing this test case.
1821 bitbake('%s -c configure' % kernel_provider)
1822 bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
1823 #Step 2
1824 runCmd('cp %s %s' % (bbconfig, tempdir_cfg))
1825 self.assertExists(os.path.join(tempdir_cfg, '.config'), 'Could not copy .config file from kernel')
1826
1827 tmpconfig = os.path.join(tempdir_cfg, '.config')
1828 #Step 3
1829 bitbake('%s -c clean' % kernel_provider)
1830 #Step 4.1
1831 runCmd('devtool modify virtual/kernel -x %s' % tempdir)
1832 self.assertExists(os.path.join(tempdir, 'Makefile'), 'Extracted source could not be found')
1833 #Step 4.2
1834 configfile = os.path.join(tempdir,'.config')
1835 diff = runCmd('diff %s %s' % (tmpconfig, configfile))
1836 self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool')
1837 #Step 4.3
1838 #NOTE: virtual/kernel is mapped to kernel_provider
1839 result = runCmd('devtool build %s' % kernel_provider)
1840 self.assertEqual(0,result.status,'Cannot build kernel using `devtool build`')
1841 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
1842 self.assertExists(kernelfile, 'Kernel was not build correctly')
1843
1844 #Modify the kernel source
1845 modfile = os.path.join(tempdir,'arch/x86/boot/header.S')
1846 modstring = "Use a boot loader. Devtool testing."
1847 modapplied = runCmd("sed -i 's/Use a boot loader./%s/' %s" % (modstring, modfile))
1848 self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile)
1849 #Modify the configuration
1850 codeconfigfile = os.path.join(tempdir,'.config.new')
1851 modconfopt = "CONFIG_SG_POOL=n"
1852 modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
1853 self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile)
1854 #Build again kernel with devtool
1855 rebuild = runCmd('devtool build %s' % kernel_provider)
1856 self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config')
1857 #Step 4.4
1858 bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider)
1859 bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename)
1860 checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
1861 self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
1862 #Step 4.5
1863 checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile))
1864 self.assertEqual(0,checkmodconfg.status,'Modification to configuration file failed')