1#
2# Copyright (c) 2015, Intel Corporation.
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# AUTHORS
7# Ed Bartosh <ed.bartosh@linux.intel.com>
8
9"""Test cases for wic."""
10
11import os
12import sys
13import unittest
14import hashlib
15
16from glob import glob
17from shutil import rmtree, copy
18from tempfile import NamedTemporaryFile
19
20from oeqa.selftest.case import OESelftestTestCase
21from oeqa.core.decorator import OETestTag
22from oeqa.core.decorator.data import skipIfNotArch
23from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
24
25
26def extract_files(debugfs_output):
27    """
28    extract file names from the output of debugfs -R 'ls -p',
29    which looks like this:
30
31     /2/040755/0/0/.//\n
32     /2/040755/0/0/..//\n
33     /11/040700/0/0/lost+found^M//\n
34     /12/040755/1002/1002/run//\n
35     /13/040755/1002/1002/sys//\n
36     /14/040755/1002/1002/bin//\n
37     /80/040755/1002/1002/var//\n
38     /92/040755/1002/1002/tmp//\n
39    """
40    # NOTE the occasional ^M in file names
41    return [line.split('/')[5].strip() for line in \
42            debugfs_output.strip().split('/\n')]
43
44def files_own_by_root(debugfs_output):
45    for line in debugfs_output.strip().split('/\n'):
46        if line.split('/')[3:5] != ['0', '0']:
47            print(debugfs_output)
48            return False
49    return True
50
51class WicTestCase(OESelftestTestCase):
52    """Wic test class."""
53
54    image_is_ready = False
55    wicenv_cache = {}
56
57    def setUpLocal(self):
58        """This code is executed before each test method."""
59        self.resultdir = os.path.join(self.builddir, "wic-tmp")
60        super(WicTestCase, self).setUpLocal()
61
62        # Do this here instead of in setUpClass as the base setUp does some
63        # clean up which can result in the native tools built earlier in
64        # setUpClass being unavailable.
65        if not WicTestCase.image_is_ready:
66            if self.td['USE_NLS'] != 'yes':
67                self.skipTest('wic-tools needs USE_NLS=yes')
68
69            bitbake('wic-tools core-image-minimal core-image-minimal-mtdutils')
70            WicTestCase.image_is_ready = True
71        rmtree(self.resultdir, ignore_errors=True)
72
73    def tearDownLocal(self):
74        """Remove resultdir as it may contain images."""
75        rmtree(self.resultdir, ignore_errors=True)
76        super(WicTestCase, self).tearDownLocal()
77
78    def _get_image_env_path(self, image):
79        """Generate and obtain the path to <image>.env"""
80        if image not in WicTestCase.wicenv_cache:
81            bitbake('%s -c do_rootfs_wicenv' % image)
82            stdir = get_bb_var('STAGING_DIR', image)
83            machine = self.td["MACHINE"]
84            WicTestCase.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
85        return WicTestCase.wicenv_cache[image]
86
87class CLITests(OESelftestTestCase):
88    def test_version(self):
89        """Test wic --version"""
90        runCmd('wic --version')
91
92    def test_help(self):
93        """Test wic --help and wic -h"""
94        runCmd('wic --help')
95        runCmd('wic -h')
96
97    def test_createhelp(self):
98        """Test wic create --help"""
99        runCmd('wic create --help')
100
101    def test_listhelp(self):
102        """Test wic list --help"""
103        runCmd('wic list --help')
104
105    def test_help_create(self):
106        """Test wic help create"""
107        runCmd('wic help create')
108
109    def test_help_list(self):
110        """Test wic help list"""
111        runCmd('wic help list')
112
113    def test_help_overview(self):
114        """Test wic help overview"""
115        runCmd('wic help overview')
116
117    def test_help_plugins(self):
118        """Test wic help plugins"""
119        runCmd('wic help plugins')
120
121    def test_help_kickstart(self):
122        """Test wic help kickstart"""
123        runCmd('wic help kickstart')
124
125    def test_list_images(self):
126        """Test wic list images"""
127        runCmd('wic list images')
128
129    def test_list_source_plugins(self):
130        """Test wic list source-plugins"""
131        runCmd('wic list source-plugins')
132
133    def test_listed_images_help(self):
134        """Test wic listed images help"""
135        output = runCmd('wic list images').output
136        imagelist = [line.split()[0] for line in output.splitlines()]
137        for image in imagelist:
138            runCmd('wic list %s help' % image)
139
140    def test_unsupported_subcommand(self):
141        """Test unsupported subcommand"""
142        self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
143
144    def test_no_command(self):
145        """Test wic without command"""
146        self.assertEqual(1, runCmd('wic', ignore_status=True).status)
147
148class Wic(WicTestCase):
149    def test_build_image_name(self):
150        """Test wic create wictestdisk --image-name=core-image-minimal"""
151        cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
152        runCmd(cmd)
153        self.assertEqual(1, len(glob(os.path.join (self.resultdir, "wictestdisk-*.direct"))))
154
155    @skipIfNotArch(['i586', 'i686', 'x86_64'])
156    def test_gpt_image(self):
157        """Test creation of core-image-minimal with gpt table and UUID boot"""
158        cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
159        runCmd(cmd)
160        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
161
162    @skipIfNotArch(['i586', 'i686', 'x86_64'])
163    def test_iso_image(self):
164        """Test creation of hybrid iso image with legacy and EFI boot"""
165        config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
166                 'MACHINE_FEATURES:append = " efi"\n'\
167                 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
168        self.append_config(config)
169        bitbake('core-image-minimal core-image-minimal-initramfs')
170        self.remove_config(config)
171        cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
172        runCmd(cmd)
173        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "HYBRID_ISO_IMG-*.direct"))))
174        self.assertEqual(1, len(glob(os.path.join (self.resultdir, "HYBRID_ISO_IMG-*.iso"))))
175
176    @skipIfNotArch(['i586', 'i686', 'x86_64'])
177    def test_qemux86_directdisk(self):
178        """Test creation of qemux-86-directdisk image"""
179        cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
180        runCmd(cmd)
181        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "qemux86-directdisk-*direct"))))
182
183    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
184    def test_mkefidisk(self):
185        """Test creation of mkefidisk image"""
186        cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
187        runCmd(cmd)
188        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "mkefidisk-*direct"))))
189
190    @skipIfNotArch(['i586', 'i686', 'x86_64'])
191    def test_bootloader_config(self):
192        """Test creation of directdisk-bootloader-config image"""
193        config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
194        self.append_config(config)
195        bitbake('core-image-minimal')
196        self.remove_config(config)
197        cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
198        runCmd(cmd)
199        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-bootloader-config-*direct"))))
200
201    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
202    def test_systemd_bootdisk(self):
203        """Test creation of systemd-bootdisk image"""
204        config = 'MACHINE_FEATURES:append = " efi"\n'
205        self.append_config(config)
206        bitbake('core-image-minimal')
207        self.remove_config(config)
208        cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
209        runCmd(cmd)
210        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "systemd-bootdisk-*direct"))))
211
212    def test_efi_bootpart(self):
213        """Test creation of efi-bootpart image"""
214        cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
215        kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
216        self.append_config('IMAGE_EFI_BOOT_FILES = "%s;kernel"\n' % kimgtype)
217        runCmd(cmd)
218        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
219        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
220        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
221        self.assertIn("kernel",result.output)
222
223    def test_sdimage_bootpart(self):
224        """Test creation of sdimage-bootpart image"""
225        cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
226        kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
227        self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
228        runCmd(cmd)
229        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
230
231    # TODO this doesn't have to be x86-specific
232    @skipIfNotArch(['i586', 'i686', 'x86_64'])
233    def test_default_output_dir(self):
234        """Test default output location"""
235        for fname in glob("directdisk-*.direct"):
236            os.remove(fname)
237        config = 'DEPENDS:pn-core-image-minimal += "syslinux"\n'
238        self.append_config(config)
239        bitbake('core-image-minimal')
240        self.remove_config(config)
241        cmd = "wic create directdisk -e core-image-minimal"
242        runCmd(cmd)
243        self.assertEqual(1, len(glob("directdisk-*.direct")))
244
245    @skipIfNotArch(['i586', 'i686', 'x86_64'])
246    def test_build_artifacts(self):
247        """Test wic create directdisk providing all artifacts."""
248        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
249                              'wic-tools')
250        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
251                                   'core-image-minimal'))
252        bbvars = {key.lower(): value for key, value in bb_vars.items()}
253        bbvars['resultdir'] = self.resultdir
254        runCmd("wic create directdisk "
255                        "-b %(staging_datadir)s "
256                        "-k %(deploy_dir_image)s "
257                        "-n %(recipe_sysroot_native)s "
258                        "-r %(image_rootfs)s "
259                        "-o %(resultdir)s" % bbvars)
260        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-*.direct"))))
261
262    def test_compress_gzip(self):
263        """Test compressing an image with gzip"""
264        runCmd("wic create wictestdisk "
265                                   "--image-name core-image-minimal "
266                                   "-c gzip -o %s" % self.resultdir)
267        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.gz"))))
268
269    def test_compress_bzip2(self):
270        """Test compressing an image with bzip2"""
271        runCmd("wic create wictestdisk "
272                                   "--image-name=core-image-minimal "
273                                   "-c bzip2 -o %s" % self.resultdir)
274        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.bz2"))))
275
276    def test_compress_xz(self):
277        """Test compressing an image with xz"""
278        runCmd("wic create wictestdisk "
279                                   "--image-name=core-image-minimal "
280                                   "--compress-with=xz -o %s" % self.resultdir)
281        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct.xz"))))
282
283    def test_wrong_compressor(self):
284        """Test how wic breaks if wrong compressor is provided"""
285        self.assertEqual(2, runCmd("wic create wictestdisk "
286                                   "--image-name=core-image-minimal "
287                                   "-c wrong -o %s" % self.resultdir,
288                                   ignore_status=True).status)
289
290    def test_debug_short(self):
291        """Test -D option"""
292        runCmd("wic create wictestdisk "
293                                   "--image-name=core-image-minimal "
294                                   "-D -o %s" % self.resultdir)
295        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
296        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
297
298    def test_debug_long(self):
299        """Test --debug option"""
300        runCmd("wic create wictestdisk "
301                                   "--image-name=core-image-minimal "
302                                   "--debug -o %s" % self.resultdir)
303        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
304        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "tmp.wic*"))))
305
306    def test_skip_build_check_short(self):
307        """Test -s option"""
308        runCmd("wic create wictestdisk "
309                                   "--image-name=core-image-minimal "
310                                   "-s -o %s" % self.resultdir)
311        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
312
313    def test_skip_build_check_long(self):
314        """Test --skip-build-check option"""
315        runCmd("wic create wictestdisk "
316                                   "--image-name=core-image-minimal "
317                                   "--skip-build-check "
318                                   "--outdir %s" % self.resultdir)
319        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
320
321    def test_build_rootfs_short(self):
322        """Test -f option"""
323        runCmd("wic create wictestdisk "
324                                   "--image-name=core-image-minimal "
325                                   "-f -o %s" % self.resultdir)
326        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
327
328    def test_build_rootfs_long(self):
329        """Test --build-rootfs option"""
330        runCmd("wic create wictestdisk "
331                                   "--image-name=core-image-minimal "
332                                   "--build-rootfs "
333                                   "--outdir %s" % self.resultdir)
334        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))))
335
336    # TODO this doesn't have to be x86-specific
337    @skipIfNotArch(['i586', 'i686', 'x86_64'])
338    def test_rootfs_indirect_recipes(self):
339        """Test usage of rootfs plugin with rootfs recipes"""
340        runCmd("wic create directdisk-multi-rootfs "
341                        "--image-name=core-image-minimal "
342                        "--rootfs rootfs1=core-image-minimal "
343                        "--rootfs rootfs2=core-image-minimal "
344                        "--outdir %s" % self.resultdir)
345        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "directdisk-multi-rootfs*.direct"))))
346
347    # TODO this doesn't have to be x86-specific
348    @skipIfNotArch(['i586', 'i686', 'x86_64'])
349    def test_rootfs_artifacts(self):
350        """Test usage of rootfs plugin with rootfs paths"""
351        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
352                              'wic-tools')
353        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
354                                   'core-image-minimal'))
355        bbvars = {key.lower(): value for key, value in bb_vars.items()}
356        bbvars['wks'] = "directdisk-multi-rootfs"
357        bbvars['resultdir'] = self.resultdir
358        runCmd("wic create %(wks)s "
359                        "--bootimg-dir=%(staging_datadir)s "
360                        "--kernel-dir=%(deploy_dir_image)s "
361                        "--native-sysroot=%(recipe_sysroot_native)s "
362                        "--rootfs-dir rootfs1=%(image_rootfs)s "
363                        "--rootfs-dir rootfs2=%(image_rootfs)s "
364                        "--outdir %(resultdir)s" % bbvars)
365        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "%(wks)s-*.direct" % bbvars))))
366
367    def test_exclude_path(self):
368        """Test --exclude-path wks option."""
369
370        oldpath = os.environ['PATH']
371        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
372
373        try:
374            wks_file = 'temp.wks'
375            with open(wks_file, 'w') as wks:
376                rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
377                wks.write("""
378part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
379part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
380part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
381                          % (rootfs_dir, rootfs_dir))
382            runCmd("wic create %s -e core-image-minimal -o %s" \
383                                       % (wks_file, self.resultdir))
384
385            os.remove(wks_file)
386            wicout = glob(os.path.join(self.resultdir, "%s-*direct" % 'temp'))
387            self.assertEqual(1, len(wicout))
388
389            wicimg = wicout[0]
390
391            # verify partition size with wic
392            res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg)
393
394            # parse parted output which looks like this:
395            # BYT;\n
396            # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
397            # 1:0.00MiB:200MiB:200MiB:ext4::;\n
398            partlns = res.output.splitlines()[2:]
399
400            self.assertEqual(3, len(partlns))
401
402            for part in [1, 2, 3]:
403                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
404                partln = partlns[part-1].split(":")
405                self.assertEqual(7, len(partln))
406                start = int(partln[1].rstrip("B")) / 512
407                length = int(partln[3].rstrip("B")) / 512
408                runCmd("dd if=%s of=%s skip=%d count=%d" %
409                                           (wicimg, part_file, start, length))
410
411            # Test partition 1, should contain the normal root directories, except
412            # /usr.
413            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
414                             os.path.join(self.resultdir, "selftest_img.part1"))
415            files = extract_files(res.output)
416            self.assertIn("etc", files)
417            self.assertNotIn("usr", files)
418
419            # Partition 2, should contain common directories for /usr, not root
420            # directories.
421            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
422                             os.path.join(self.resultdir, "selftest_img.part2"))
423            files = extract_files(res.output)
424            self.assertNotIn("etc", files)
425            self.assertNotIn("usr", files)
426            self.assertIn("share", files)
427
428            # Partition 3, should contain the same as partition 2, including the bin
429            # directory, but not the files inside it.
430            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
431                             os.path.join(self.resultdir, "selftest_img.part3"))
432            files = extract_files(res.output)
433            self.assertNotIn("etc", files)
434            self.assertNotIn("usr", files)
435            self.assertIn("share", files)
436            self.assertIn("bin", files)
437            res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \
438                             os.path.join(self.resultdir, "selftest_img.part3"))
439            files = extract_files(res.output)
440            self.assertIn(".", files)
441            self.assertIn("..", files)
442            self.assertEqual(2, len(files))
443
444            for part in [1, 2, 3]:
445                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
446                os.remove(part_file)
447
448        finally:
449            os.environ['PATH'] = oldpath
450
451    def test_include_path(self):
452        """Test --include-path wks option."""
453
454        oldpath = os.environ['PATH']
455        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
456
457        try:
458            include_path = os.path.join(self.resultdir, 'test-include')
459            os.makedirs(include_path)
460            with open(os.path.join(include_path, 'test-file'), 'w') as t:
461                t.write("test\n")
462            wks_file = os.path.join(include_path, 'temp.wks')
463            with open(wks_file, 'w') as wks:
464                rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
465                wks.write("""
466part /part1 --source rootfs --ondisk mmcblk0 --fstype=ext4
467part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s"""
468                          % (include_path))
469            runCmd("wic create %s -e core-image-minimal -o %s" \
470                                       % (wks_file, self.resultdir))
471
472            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
473            part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0]
474
475            # Test partition 1, should not contain 'test-file'
476            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
477            files = extract_files(res.output)
478            self.assertNotIn('test-file', files)
479            self.assertEqual(True, files_own_by_root(res.output))
480
481            # Test partition 2, should contain 'test-file'
482            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part2))
483            files = extract_files(res.output)
484            self.assertIn('test-file', files)
485            self.assertEqual(True, files_own_by_root(res.output))
486
487        finally:
488            os.environ['PATH'] = oldpath
489
490    def test_include_path_embeded(self):
491        """Test --include-path wks option."""
492
493        oldpath = os.environ['PATH']
494        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
495
496        try:
497            include_path = os.path.join(self.resultdir, 'test-include')
498            os.makedirs(include_path)
499            with open(os.path.join(include_path, 'test-file'), 'w') as t:
500                t.write("test\n")
501            wks_file = os.path.join(include_path, 'temp.wks')
502            with open(wks_file, 'w') as wks:
503                wks.write("""
504part / --source rootfs  --fstype=ext4 --include-path %s --include-path core-image-minimal-mtdutils export/"""
505                          % (include_path))
506            runCmd("wic create %s -e core-image-minimal -o %s" \
507                                       % (wks_file, self.resultdir))
508
509            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
510
511            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
512            files = extract_files(res.output)
513            self.assertIn('test-file', files)
514            self.assertEqual(True, files_own_by_root(res.output))
515
516            res = runCmd("debugfs -R 'ls -p /export/etc/' %s 2>/dev/null" % (part1))
517            files = extract_files(res.output)
518            self.assertIn('passwd', files)
519            self.assertEqual(True, files_own_by_root(res.output))
520
521        finally:
522            os.environ['PATH'] = oldpath
523
524    def test_include_path_errors(self):
525        """Test --include-path wks option error handling."""
526        wks_file = 'temp.wks'
527
528        # Absolute argument.
529        with open(wks_file, 'w') as wks:
530            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils /export")
531        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
532                                      % (wks_file, self.resultdir), ignore_status=True).status)
533        os.remove(wks_file)
534
535        # Argument pointing to parent directory.
536        with open(wks_file, 'w') as wks:
537            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils ././..")
538        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
539                                      % (wks_file, self.resultdir), ignore_status=True).status)
540        os.remove(wks_file)
541
542        # 3 Argument pointing to parent directory.
543        with open(wks_file, 'w') as wks:
544            wks.write("part / --source rootfs --fstype=ext4 --include-path core-image-minimal-mtdutils export/ dummy")
545        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
546                                      % (wks_file, self.resultdir), ignore_status=True).status)
547        os.remove(wks_file)
548
549    def test_exclude_path_errors(self):
550        """Test --exclude-path wks option error handling."""
551        wks_file = 'temp.wks'
552
553        # Absolute argument.
554        with open(wks_file, 'w') as wks:
555            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
556        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
557                                      % (wks_file, self.resultdir), ignore_status=True).status)
558        os.remove(wks_file)
559
560        # Argument pointing to parent directory.
561        with open(wks_file, 'w') as wks:
562            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
563        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
564                                      % (wks_file, self.resultdir), ignore_status=True).status)
565        os.remove(wks_file)
566
567    def test_permissions(self):
568        """Test permissions are respected"""
569
570        # prepare wicenv and rootfs
571        bitbake('core-image-minimal core-image-minimal-mtdutils -c do_rootfs_wicenv')
572
573        oldpath = os.environ['PATH']
574        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
575
576        t_normal = """
577part / --source rootfs --fstype=ext4
578"""
579        t_exclude = """
580part / --source rootfs --fstype=ext4 --exclude-path=home
581"""
582        t_multi = """
583part / --source rootfs --ondisk sda --fstype=ext4
584part /export --source rootfs --rootfs=core-image-minimal-mtdutils --fstype=ext4
585"""
586        t_change = """
587part / --source rootfs --ondisk sda --fstype=ext4 --exclude-path=etc/   
588part /etc --source rootfs --fstype=ext4 --change-directory=etc
589"""
590        tests = [t_normal, t_exclude, t_multi, t_change]
591
592        try:
593            for test in tests:
594                include_path = os.path.join(self.resultdir, 'test-include')
595                os.makedirs(include_path)
596                wks_file = os.path.join(include_path, 'temp.wks')
597                with open(wks_file, 'w') as wks:
598                    wks.write(test)
599                runCmd("wic create %s -e core-image-minimal -o %s" \
600                                       % (wks_file, self.resultdir))
601
602                for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')):
603                    res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part))
604                    self.assertEqual(True, files_own_by_root(res.output))
605
606                config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file
607                self.append_config(config)
608                bitbake('core-image-minimal')
609                tmpdir = os.path.join(get_bb_var('WORKDIR', 'core-image-minimal'),'build-wic')
610
611                # check each partition for permission
612                for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')):
613                    res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part))
614                    self.assertTrue(files_own_by_root(res.output)
615                        ,msg='Files permission incorrect using wks set "%s"' % test)
616
617                # clean config and result directory for next cases
618                self.remove_config(config)
619                rmtree(self.resultdir, ignore_errors=True)
620
621        finally:
622            os.environ['PATH'] = oldpath
623
624    def test_change_directory(self):
625        """Test --change-directory wks option."""
626
627        oldpath = os.environ['PATH']
628        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
629
630        try:
631            include_path = os.path.join(self.resultdir, 'test-include')
632            os.makedirs(include_path)
633            wks_file = os.path.join(include_path, 'temp.wks')
634            with open(wks_file, 'w') as wks:
635                wks.write("part /etc --source rootfs --fstype=ext4 --change-directory=etc")
636            runCmd("wic create %s -e core-image-minimal -o %s" \
637                                       % (wks_file, self.resultdir))
638
639            part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0]
640
641            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1))
642            files = extract_files(res.output)
643            self.assertIn('passwd', files)
644
645        finally:
646            os.environ['PATH'] = oldpath
647
648    def test_change_directory_errors(self):
649        """Test --change-directory wks option error handling."""
650        wks_file = 'temp.wks'
651
652        # Absolute argument.
653        with open(wks_file, 'w') as wks:
654            wks.write("part / --source rootfs --fstype=ext4 --change-directory /usr")
655        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
656                                      % (wks_file, self.resultdir), ignore_status=True).status)
657        os.remove(wks_file)
658
659        # Argument pointing to parent directory.
660        with open(wks_file, 'w') as wks:
661            wks.write("part / --source rootfs --fstype=ext4 --change-directory ././..")
662        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
663                                      % (wks_file, self.resultdir), ignore_status=True).status)
664        os.remove(wks_file)
665
666    def test_no_fstab_update(self):
667        """Test --no-fstab-update wks option."""
668
669        oldpath = os.environ['PATH']
670        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
671
672        # Get stock fstab from base-files recipe
673        bitbake('base-files -c do_install')
674        bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab')
675        self.assertEqual(True, os.path.exists(bf_fstab))
676        bf_fstab_md5sum = runCmd('md5sum %s 2>/dev/null' % bf_fstab).output.split(" ")[0]
677
678        try:
679            no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update')
680            os.makedirs(no_fstab_update_path)
681            wks_file = os.path.join(no_fstab_update_path, 'temp.wks')
682            with open(wks_file, 'w') as wks:
683                wks.writelines(['part / --source rootfs --fstype=ext4 --label rootfs\n',
684                                'part /mnt/p2 --source rootfs --rootfs-dir=core-image-minimal ',
685                                '--fstype=ext4 --label p2 --no-fstab-update\n'])
686            runCmd("wic create %s -e core-image-minimal -o %s" \
687                                       % (wks_file, self.resultdir))
688
689            part_fstab_md5sum = []
690            for i in range(1, 3):
691                part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0]
692                part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s 2>/dev/null" % (part))
693                part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest())
694
695            # '/etc/fstab' in partition 2 should contain the same stock fstab file
696            # as the one installed by the base-file recipe.
697            self.assertEqual(bf_fstab_md5sum, part_fstab_md5sum[1])
698
699            # '/etc/fstab' in partition 1 should contain an updated fstab file.
700            self.assertNotEqual(bf_fstab_md5sum, part_fstab_md5sum[0])
701
702        finally:
703            os.environ['PATH'] = oldpath
704
705    def test_no_fstab_update_errors(self):
706        """Test --no-fstab-update wks option error handling."""
707        wks_file = 'temp.wks'
708
709        # Absolute argument.
710        with open(wks_file, 'w') as wks:
711            wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update /etc")
712        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
713                                      % (wks_file, self.resultdir), ignore_status=True).status)
714        os.remove(wks_file)
715
716        # Argument pointing to parent directory.
717        with open(wks_file, 'w') as wks:
718            wks.write("part / --source rootfs --fstype=ext4 --no-fstab-update ././..")
719        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
720                                      % (wks_file, self.resultdir), ignore_status=True).status)
721        os.remove(wks_file)
722
723    def test_extra_space(self):
724        """Test --extra-space wks option."""
725        extraspace = 1024**3
726        runCmd("wic create wictestdisk "
727                                   "--image-name core-image-minimal "
728                                   "--extra-space %i -o %s" % (extraspace ,self.resultdir))
729        wicout = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
730        self.assertEqual(1, len(wicout))
731        size = os.path.getsize(wicout[0])
732        self.assertTrue(size > extraspace, msg="Extra space not present (%s vs %s)" % (size, extraspace))
733
734    def test_no_table(self):
735        """Test --no-table wks option."""
736        wks_file = 'temp.wks'
737
738        # Absolute argument.
739        with open(wks_file, 'w') as wks:
740            wks.write("part testspace --no-table --fixed-size 16k --offset 4080k")
741        runCmd("wic create %s --image-name core-image-minimal -o %s" % (wks_file, self.resultdir))
742
743        wicout = glob(os.path.join(self.resultdir, "*.*"))
744
745        self.assertEqual(1, len(wicout))
746        size = os.path.getsize(wicout[0])
747        self.assertEqual(size, 4 * 1024 * 1024)
748
749        os.remove(wks_file)
750
751class Wic2(WicTestCase):
752
753    def test_bmap_short(self):
754        """Test generation of .bmap file -m option"""
755        cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
756        runCmd(cmd)
757        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
758        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
759
760    def test_bmap_long(self):
761        """Test generation of .bmap file --bmap option"""
762        cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
763        runCmd(cmd)
764        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
765        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
766
767    def test_image_env(self):
768        """Test generation of <image>.env files."""
769        image = 'core-image-minimal'
770        imgdatadir = self._get_image_env_path(image)
771
772        bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
773        basename = bb_vars['IMAGE_BASENAME']
774        self.assertEqual(basename, image)
775        path = os.path.join(imgdatadir, basename) + '.env'
776        self.assertTrue(os.path.isfile(path), msg="File %s wasn't generated as expected" % path)
777
778        wicvars = set(bb_vars['WICVARS'].split())
779        # filter out optional variables
780        wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
781                                      'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE',
782                                      'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME',
783                                      'APPEND', 'IMAGE_EFI_BOOT_FILES'))
784        with open(path) as envfile:
785            content = dict(line.split("=", 1) for line in envfile)
786            # test if variables used by wic present in the .env file
787            for var in wicvars:
788                self.assertTrue(var in content, "%s is not in .env file" % var)
789                self.assertTrue(content[var], "%s doesn't have a value (%s)" % (var, content[var]))
790
791    def test_image_vars_dir_short(self):
792        """Test image vars directory selection -v option"""
793        image = 'core-image-minimal'
794        imgenvdir = self._get_image_env_path(image)
795        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
796
797        runCmd("wic create wictestdisk "
798                                   "--image-name=%s -v %s -n %s -o %s"
799                                   % (image, imgenvdir, native_sysroot,
800                                      self.resultdir))
801        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
802
803    def test_image_vars_dir_long(self):
804        """Test image vars directory selection --vars option"""
805        image = 'core-image-minimal'
806        imgenvdir = self._get_image_env_path(image)
807        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
808
809        runCmd("wic create wictestdisk "
810                                   "--image-name=%s "
811                                   "--vars %s "
812                                   "--native-sysroot %s "
813                                   "--outdir %s"
814                                   % (image, imgenvdir, native_sysroot,
815                                      self.resultdir))
816        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
817
818    # TODO this test could also work on aarch64
819    @skipIfNotArch(['i586', 'i686', 'x86_64'])
820    def test_wic_image_type(self):
821        """Test building wic images by bitbake"""
822        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
823                 'MACHINE_FEATURES:append = " efi"\n'
824        self.append_config(config)
825        image = 'wic-image-minimal'
826        bitbake(image)
827        self.remove_config(config)
828
829        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
830        prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME'])
831
832        # check if we have result image and manifests symlinks
833        # pointing to existing files
834        for suffix in ('wic', 'manifest'):
835            path = prefix + suffix
836            self.assertTrue(os.path.islink(path), msg="Link %s wasn't generated as expected" % path)
837            self.assertTrue(os.path.isfile(os.path.realpath(path)), msg="File linked to by %s wasn't generated as expected" % path)
838
839    # TODO this should work on aarch64
840    @skipIfNotArch(['i586', 'i686', 'x86_64'])
841    @OETestTag("runqemu")
842    def test_qemu(self):
843        """Test wic-image-minimal under qemu"""
844        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
845                 'MACHINE_FEATURES:append = " efi"\n'
846        self.append_config(config)
847        bitbake('wic-image-minimal')
848        self.remove_config(config)
849
850        with runqemu('wic-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
851            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \
852                  "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'"
853            status, output = qemu.run_serial(cmd)
854            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
855            self.assertEqual(output, '4')
856            cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab"
857            status, output = qemu.run_serial(cmd)
858            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
859            self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0')
860
861    @skipIfNotArch(['i586', 'i686', 'x86_64'])
862    @OETestTag("runqemu")
863    def test_qemu_efi(self):
864        """Test core-image-minimal efi image under qemu"""
865        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
866        self.append_config(config)
867        bitbake('core-image-minimal ovmf')
868        self.remove_config(config)
869
870        with runqemu('core-image-minimal', ssh=False,
871                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
872            cmd = "grep sda. /proc/partitions  |wc -l"
873            status, output = qemu.run_serial(cmd)
874            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
875            self.assertEqual(output, '3')
876
877    @staticmethod
878    def _make_fixed_size_wks(size):
879        """
880        Create a wks of an image with a single partition. Size of the partition is set
881        using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
882        """
883        with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
884            wkspath = tempf.name
885            tempf.write("part " \
886                     "--source rootfs --ondisk hda --align 4 --fixed-size %d "
887                     "--fstype=ext4\n" % size)
888
889        return wkspath
890
891    def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False):
892        p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir),
893                   ignore_status=ignore_status)
894
895        if p.status:
896            return (p, None)
897
898        wksname = os.path.splitext(os.path.basename(wkspath))[0]
899
900        wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
901
902        if not wicout:
903            return (p, None)
904
905        wicimg = wicout[0]
906
907        if not native_sysroot:
908            native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
909
910        # verify partition size with wic
911        res = runCmd("parted -m %s unit kib p 2>/dev/null" % wicimg,
912                     native_sysroot=native_sysroot)
913
914        # parse parted output which looks like this:
915        # BYT;\n
916        # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
917        # 1:0.00MiB:200MiB:200MiB:ext4::;\n
918        return (p, res.output.splitlines()[2:])
919
920    def test_fixed_size(self):
921        """
922        Test creation of a simple image with partition size controlled through
923        --fixed-size flag
924        """
925        wkspath = Wic2._make_fixed_size_wks(200)
926        _, partlns = self._get_wic_partitions(wkspath)
927        os.remove(wkspath)
928
929        self.assertEqual(partlns, [
930                        "1:4.00kiB:204804kiB:204800kiB:ext4::;",
931                        ])
932
933    def test_fixed_size_error(self):
934        """
935        Test creation of a simple image with partition size controlled through
936        --fixed-size flag. The size of partition is intentionally set to 1MiB
937        in order to trigger an error in wic.
938        """
939        wkspath = Wic2._make_fixed_size_wks(1)
940        p, _ = self._get_wic_partitions(wkspath, ignore_status=True)
941        os.remove(wkspath)
942
943        self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
944
945    def test_offset(self):
946        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
947
948        with NamedTemporaryFile("w", suffix=".wks") as tempf:
949            # Test that partitions are placed at the correct offsets, default KB
950            tempf.write("bootloader --ptable gpt\n" \
951                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
952                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
953            tempf.flush()
954
955            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
956            self.assertEqual(partlns, [
957                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
958                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
959                ])
960
961        with NamedTemporaryFile("w", suffix=".wks") as tempf:
962            # Test that partitions are placed at the correct offsets, same with explicit KB
963            tempf.write("bootloader --ptable gpt\n" \
964                        "part /    --source rootfs --ondisk hda --offset 32K     --fixed-size 100M --fstype=ext4\n" \
965                        "part /bar                 --ondisk hda --offset 102432K --fixed-size 100M --fstype=ext4\n")
966            tempf.flush()
967
968            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
969            self.assertEqual(partlns, [
970                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
971                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
972                ])
973
974        with NamedTemporaryFile("w", suffix=".wks") as tempf:
975            # Test that partitions are placed at the correct offsets using MB
976            tempf.write("bootloader --ptable gpt\n" \
977                        "part /    --source rootfs --ondisk hda --offset 32K  --fixed-size 100M --fstype=ext4\n" \
978                        "part /bar                 --ondisk hda --offset 101M --fixed-size 100M --fstype=ext4\n")
979            tempf.flush()
980
981            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
982            self.assertEqual(partlns, [
983                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
984                "2:103424kiB:205824kiB:102400kiB:ext4:primary:;",
985                ])
986
987        with NamedTemporaryFile("w", suffix=".wks") as tempf:
988            # Test that partitions can be placed on a 512 byte sector boundary
989            tempf.write("bootloader --ptable gpt\n" \
990                        "part /    --source rootfs --ondisk hda --offset 65s --fixed-size 99M --fstype=ext4\n" \
991                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
992            tempf.flush()
993
994            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
995            self.assertEqual(partlns, [
996                "1:32.5kiB:101408kiB:101376kiB:ext4:primary:;",
997                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
998                ])
999
1000        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1001            # Test that a partition can be placed immediately after a MSDOS partition table
1002            tempf.write("bootloader --ptable msdos\n" \
1003                        "part /    --source rootfs --ondisk hda --offset 1s --fixed-size 100M --fstype=ext4\n")
1004            tempf.flush()
1005
1006            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1007            self.assertEqual(partlns, [
1008                "1:0.50kiB:102400kiB:102400kiB:ext4::;",
1009                ])
1010
1011        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1012            # Test that image creation fails if the partitions would overlap
1013            tempf.write("bootloader --ptable gpt\n" \
1014                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
1015                        "part /bar                 --ondisk hda --offset 102431 --fixed-size 100M --fstype=ext4\n")
1016            tempf.flush()
1017
1018            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1019            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1020
1021        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1022            # Test that partitions are not allowed to overlap with the booloader
1023            tempf.write("bootloader --ptable gpt\n" \
1024                        "part /    --source rootfs --ondisk hda --offset 8 --fixed-size 100M --fstype=ext4\n")
1025            tempf.flush()
1026
1027            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1028            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1029
1030    def test_extra_space(self):
1031        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1032
1033        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1034            tempf.write("bootloader --ptable gpt\n" \
1035                        "part /     --source rootfs --ondisk hda --extra-space 200M --fstype=ext4\n")
1036            tempf.flush()
1037
1038            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1039            self.assertEqual(len(partlns), 1)
1040            size = partlns[0].split(':')[3]
1041            self.assertRegex(size, r'^[0-9]+kiB$')
1042            size = int(size[:-3])
1043            self.assertGreaterEqual(size, 204800)
1044
1045    # TODO this test could also work on aarch64
1046    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1047    @OETestTag("runqemu")
1048    def test_rawcopy_plugin_qemu(self):
1049        """Test rawcopy plugin in qemu"""
1050        # build ext4 and then use it for a wic image
1051        config = 'IMAGE_FSTYPES = "ext4"\n'
1052        self.append_config(config)
1053        bitbake('core-image-minimal')
1054        image_link_name = get_bb_var('IMAGE_LINK_NAME', 'core-image-minimal')
1055        self.remove_config(config)
1056
1057        config = 'IMAGE_FSTYPES = "wic"\n' \
1058                 'IMAGE_LINK_NAME_CORE_IMAGE_MINIMAL = "%s"\n'\
1059                 'WKS_FILE = "test_rawcopy_plugin.wks.in"\n'\
1060                 % image_link_name
1061        self.append_config(config)
1062        bitbake('core-image-minimal-mtdutils')
1063        self.remove_config(config)
1064
1065        with runqemu('core-image-minimal-mtdutils', ssh=False,
1066                     runqemuparams='nographic', image_fstype='wic') as qemu:
1067            cmd = "grep sda. /proc/partitions  |wc -l"
1068            status, output = qemu.run_serial(cmd)
1069            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1070            self.assertEqual(output, '2')
1071
1072    def _rawcopy_plugin(self, fstype):
1073        """Test rawcopy plugin"""
1074        image = 'core-image-minimal'
1075        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1076        params = ',unpack' if fstype.endswith('.gz') else ''
1077        with NamedTemporaryFile("w", suffix=".wks") as wks:
1078            wks.write('part / --source rawcopy --sourceparams="file=%s.%s%s"\n'\
1079                      % (bb_vars['IMAGE_LINK_NAME'], fstype, params))
1080            wks.flush()
1081            cmd = "wic create %s -e %s -o %s" % (wks.name, image, self.resultdir)
1082            runCmd(cmd)
1083            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1084            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1085            self.assertEqual(1, len(out))
1086
1087    def test_rawcopy_plugin(self):
1088        self._rawcopy_plugin('ext4')
1089
1090    def test_rawcopy_plugin_unpack(self):
1091        fstype = 'ext4.gz'
1092        config = 'IMAGE_FSTYPES = "%s"\n' % fstype
1093        self.append_config(config)
1094        self.assertEqual(0, bitbake('core-image-minimal').status)
1095        self.remove_config(config)
1096        self._rawcopy_plugin(fstype)
1097
1098    def test_empty_plugin(self):
1099        """Test empty plugin"""
1100        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n'
1101        self.append_config(config)
1102        image = 'core-image-minimal'
1103        bitbake(image)
1104        self.remove_config(config)
1105        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1106        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1107        self.assertTrue(os.path.exists(image_path), msg="Image file %s wasn't generated as expected" % image_path)
1108
1109        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1110
1111        # Fstype column from 'wic ls' should be empty for the second partition
1112        # as listed in test_empty_plugin.wks
1113        result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot))
1114        self.assertEqual('1', result.output)
1115
1116    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1117    @OETestTag("runqemu")
1118    def test_biosplusefi_plugin_qemu(self):
1119        """Test biosplusefi plugin in qemu"""
1120        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1121        self.append_config(config)
1122        bitbake('core-image-minimal')
1123        self.remove_config(config)
1124
1125        with runqemu('core-image-minimal', ssh=False,
1126                     runqemuparams='nographic', image_fstype='wic') as qemu:
1127            # Check that we have ONLY two /dev/sda* partitions (/boot and /)
1128            cmd = "grep sda. /proc/partitions | wc -l"
1129            status, output = qemu.run_serial(cmd)
1130            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1131            self.assertEqual(output, '2')
1132            # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is /
1133            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'"
1134            status, output = qemu.run_serial(cmd)
1135            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1136            self.assertEqual(output, '2')
1137            # Check that /boot has EFI bootx64.efi (required for EFI)
1138            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1139            status, output = qemu.run_serial(cmd)
1140            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1141            self.assertEqual(output, '1')
1142            # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios)
1143            # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command
1144            cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat"
1145            status, output = qemu.run_serial(cmd)
1146            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1147            self.assertEqual(output, '*')
1148
1149    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1150    def test_biosplusefi_plugin(self):
1151        """Test biosplusefi plugin"""
1152        # Wic generation below may fail depending on the order of the unittests
1153        # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1154        #    which may or may not exists depending on what was built already
1155        # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1156        #   will raise with "Couldn't find correct bootimg_dir"
1157        # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call
1158        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1159        self.append_config(config)
1160        bitbake('core-image-minimal')
1161        self.remove_config(config)
1162
1163        img = 'core-image-minimal'
1164        with NamedTemporaryFile("w", suffix=".wks") as wks:
1165            wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n',
1166                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1167                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1168            wks.flush()
1169            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1170            runCmd(cmd)
1171            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1172            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1173            self.assertEqual(1, len(out))
1174
1175    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
1176    def test_uefi_kernel(self):
1177        """ Test uefi-kernel in wic """
1178        config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n'
1179        self.append_config(config)
1180        bitbake('core-image-minimal')
1181        self.remove_config(config)
1182
1183        img = 'core-image-minimal'
1184        with NamedTemporaryFile("w", suffix=".wks") as wks:
1185            wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n'
1186                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1187                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1188            wks.flush()
1189            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1190            runCmd(cmd)
1191            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1192            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1193            self.assertEqual(1, len(out))
1194
1195    # TODO this test could also work on aarch64
1196    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1197    @OETestTag("runqemu")
1198    def test_efi_plugin_unified_kernel_image_qemu(self):
1199        """Test efi plugin's Unified Kernel Image feature in qemu"""
1200        config = 'IMAGE_FSTYPES = "wic"\n'\
1201                 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
1202                 'WKS_FILE = "test_efi_plugin.wks"\n'\
1203                 'MACHINE_FEATURES:append = " efi"\n'
1204        self.append_config(config)
1205        bitbake('core-image-minimal core-image-minimal-initramfs ovmf')
1206        self.remove_config(config)
1207
1208        with runqemu('core-image-minimal', ssh=False,
1209                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
1210            # Check that /boot has EFI bootx64.efi (required for EFI)
1211            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1212            status, output = qemu.run_serial(cmd)
1213            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1214            self.assertEqual(output, '1')
1215            # Check that /boot has EFI/Linux/linux.efi (required for Unified Kernel Images auto detection)
1216            cmd = "ls /boot/EFI/Linux/linux.efi | wc -l"
1217            status, output = qemu.run_serial(cmd)
1218            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1219            self.assertEqual(output, '1')
1220            # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
1221            cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
1222            status, output = qemu.run_serial(cmd)
1223            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1224            self.assertEqual(output, '0')
1225
1226    def test_fs_types(self):
1227        """Test filesystem types for empty and not empty partitions"""
1228        img = 'core-image-minimal'
1229        with NamedTemporaryFile("w", suffix=".wks") as wks:
1230            wks.writelines(['part ext2   --fstype ext2     --source rootfs\n',
1231                            'part btrfs  --fstype btrfs    --source rootfs --size 40M\n',
1232                            'part squash --fstype squashfs --source rootfs\n',
1233                            'part swap   --fstype swap --size 1M\n',
1234                            'part emptyvfat   --fstype vfat   --size 1M\n',
1235                            'part emptymsdos  --fstype msdos  --size 1M\n',
1236                            'part emptyext2   --fstype ext2   --size 1M\n',
1237                            'part emptybtrfs  --fstype btrfs  --size 150M\n'])
1238            wks.flush()
1239            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1240            runCmd(cmd)
1241            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1242            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1243            self.assertEqual(1, len(out))
1244
1245    def test_kickstart_parser(self):
1246        """Test wks parser options"""
1247        with NamedTemporaryFile("w", suffix=".wks") as wks:
1248            wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
1249                            '--overhead-factor 1.2 --size 100k\n'])
1250            wks.flush()
1251            cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
1252            runCmd(cmd)
1253            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1254            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1255            self.assertEqual(1, len(out))
1256
1257    def test_image_bootpart_globbed(self):
1258        """Test globbed sources with image-bootpart plugin"""
1259        img = "core-image-minimal"
1260        cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
1261        config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
1262        self.append_config(config)
1263        runCmd(cmd)
1264        self.remove_config(config)
1265        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
1266
1267    def test_sparse_copy(self):
1268        """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
1269        libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic')
1270        sys.path.insert(0, libpath)
1271        from  filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
1272        with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
1273            src_name = sparse.name
1274            src_size = 1024 * 10
1275            sparse.truncate(src_size)
1276            # write one byte to the file
1277            with open(src_name, 'r+b') as sfile:
1278                sfile.seek(1024 * 4)
1279                sfile.write(b'\x00')
1280            dest = sparse.name + '.out'
1281            # copy src file to dest using different filemap APIs
1282            for api in (FilemapFiemap, FilemapSeek, None):
1283                if os.path.exists(dest):
1284                    os.unlink(dest)
1285                try:
1286                    sparse_copy(sparse.name, dest, api=api)
1287                except ErrorNotSupp:
1288                    continue # skip unsupported API
1289                dest_stat = os.stat(dest)
1290                self.assertEqual(dest_stat.st_size, src_size)
1291                # 8 blocks is 4K (physical sector size)
1292                self.assertEqual(dest_stat.st_blocks, 8)
1293            os.unlink(dest)
1294
1295    def test_mkfs_extraopts(self):
1296        """Test wks option --mkfs-extraopts for empty and not empty partitions"""
1297        img = 'core-image-minimal'
1298        with NamedTemporaryFile("w", suffix=".wks") as wks:
1299            wks.writelines(
1300                ['part ext2   --fstype ext2     --source rootfs --mkfs-extraopts "-D -F -i 8192"\n',
1301                 "part btrfs  --fstype btrfs    --source rootfs --size 40M --mkfs-extraopts='--quiet'\n",
1302                 'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n',
1303                 'part emptyvfat   --fstype vfat   --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1304                 'part emptymsdos  --fstype msdos  --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1305                 'part emptyext2   --fstype ext2   --size 1M --mkfs-extraopts "-D -F -i 8192"\n',
1306                 'part emptybtrfs  --fstype btrfs  --size 100M --mkfs-extraopts "--mixed -K"\n'])
1307            wks.flush()
1308            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1309            runCmd(cmd)
1310            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1311            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1312            self.assertEqual(1, len(out))
1313
1314    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1315    @OETestTag("runqemu")
1316    def test_expand_mbr_image(self):
1317        """Test wic write --expand command for mbr image"""
1318        # build an image
1319        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n'
1320        self.append_config(config)
1321        image = 'core-image-minimal'
1322        bitbake(image)
1323
1324        # get path to the image
1325        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1326        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1327
1328        self.remove_config(config)
1329
1330        try:
1331            # expand image to 1G
1332            new_image_path = None
1333            with NamedTemporaryFile(mode='wb', suffix='.wic.exp',
1334                                    dir=bb_vars['DEPLOY_DIR_IMAGE'], delete=False) as sparse:
1335                sparse.truncate(1024 ** 3)
1336                new_image_path = sparse.name
1337
1338            sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1339            cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path)
1340            runCmd(cmd)
1341
1342            # check if partitions are expanded
1343            orig = runCmd("wic ls %s -n %s" % (image_path, sysroot))
1344            exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot))
1345            orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]]
1346            exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]]
1347            self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized
1348            self.assertTrue(orig_sizes[1] < exp_sizes[1], msg="Parition size wasn't enlarged (%s vs %s)" % (orig_sizes[1], exp_sizes[1]))
1349
1350            # Check if all free space is partitioned
1351            result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path))
1352            self.assertIn("0 B, 0 bytes, 0 sectors", result.output)
1353
1354            os.rename(image_path, image_path + '.bak')
1355            os.rename(new_image_path, image_path)
1356
1357            # Check if it boots in qemu
1358            with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
1359                cmd = "ls /etc/"
1360                status, output = qemu.run_serial('true')
1361                self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1362        finally:
1363            if os.path.exists(new_image_path):
1364                os.unlink(new_image_path)
1365            if os.path.exists(image_path + '.bak'):
1366                os.rename(image_path + '.bak', image_path)
1367
1368    def test_gpt_partition_name(self):
1369        """Test --part-name argument to set partition name in GPT table"""
1370        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "test_gpt_partition_name.wks"\n'
1371        self.append_config(config)
1372        image = 'core-image-minimal'
1373        bitbake(image)
1374        self.remove_config(config)
1375        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1376        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image)
1377        image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.wic' % bb_vars['IMAGE_LINK_NAME'])
1378
1379        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1380
1381        # Image is created
1382        self.assertTrue(os.path.exists(image_path), "image file %s doesn't exist" % image_path)
1383
1384        # Check the names of the three partitions
1385        # as listed in test_gpt_partition_name.wks
1386        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 1" % (sysroot, image_path))
1387        self.assertEqual('boot-A', result.output)
1388        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 2" % (sysroot, image_path))
1389        self.assertEqual('root-A', result.output)
1390        # When the --part-name is not defined, the partition name is equal to the --label
1391        result = runCmd("%s/usr/sbin/sfdisk --part-label %s 3" % (sysroot, image_path))
1392        self.assertEqual('ext-space', result.output)
1393
1394class ModifyTests(WicTestCase):
1395    def test_wic_ls(self):
1396        """Test listing image content using 'wic ls'"""
1397        runCmd("wic create wictestdisk "
1398                                   "--image-name=core-image-minimal "
1399                                   "-D -o %s" % self.resultdir)
1400        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1401        self.assertEqual(1, len(images))
1402
1403        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1404
1405        # list partitions
1406        result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
1407        self.assertEqual(3, len(result.output.split('\n')))
1408
1409        # list directory content of the first partition
1410        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1411        self.assertEqual(6, len(result.output.split('\n')))
1412
1413    def test_wic_cp(self):
1414        """Test copy files and directories to the the wic image."""
1415        runCmd("wic create wictestdisk "
1416                                   "--image-name=core-image-minimal "
1417                                   "-D -o %s" % self.resultdir)
1418        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1419        self.assertEqual(1, len(images))
1420
1421        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1422
1423        # list directory content of the first partition
1424        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1425        self.assertEqual(6, len(result.output.split('\n')))
1426
1427        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1428            testfile.write("test")
1429
1430            # copy file to the partition
1431            runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot))
1432
1433            # check if file is there
1434            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1435            self.assertEqual(7, len(result.output.split('\n')))
1436            self.assertIn(os.path.basename(testfile.name), result.output)
1437
1438            # prepare directory
1439            testdir = os.path.join(self.resultdir, 'wic-test-cp-dir')
1440            testsubdir = os.path.join(testdir, 'subdir')
1441            os.makedirs(os.path.join(testsubdir))
1442            copy(testfile.name, testdir)
1443
1444            # copy directory to the partition
1445            runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot))
1446
1447            # check if directory is there
1448            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1449            self.assertEqual(8, len(result.output.split('\n')))
1450            self.assertIn(os.path.basename(testdir), result.output)
1451
1452            # copy the file from the partition and check if it success
1453            dest = '%s-cp' % testfile.name
1454            runCmd("wic cp %s:1/%s %s -n %s" % (images[0],
1455                    os.path.basename(testfile.name), dest, sysroot))
1456            self.assertTrue(os.path.exists(dest), msg="File %s wasn't generated as expected" % dest)
1457
1458
1459    def test_wic_rm(self):
1460        """Test removing files and directories from the the wic image."""
1461        runCmd("wic create mkefidisk "
1462                                   "--image-name=core-image-minimal "
1463                                   "-D -o %s" % self.resultdir)
1464        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1465        self.assertEqual(1, len(images))
1466
1467        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1468        # Not bulletproof but hopefully sufficient
1469        kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel')
1470
1471        # list directory content of the first partition
1472        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1473        self.assertIn('\n%s ' % kerneltype.upper(), result.output)
1474        self.assertIn('\nEFI          <DIR>     ', result.output)
1475
1476        # remove file. EFI partitions are case-insensitive so exercise that too
1477        runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot))
1478
1479        # remove directory
1480        runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot))
1481
1482        # check if they're removed
1483        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1484        self.assertNotIn('\n%s        ' % kerneltype.upper(), result.output)
1485        self.assertNotIn('\nEFI          <DIR>     ', result.output)
1486
1487    def test_wic_ls_ext(self):
1488        """Test listing content of the ext partition using 'wic ls'"""
1489        runCmd("wic create wictestdisk "
1490                                   "--image-name=core-image-minimal "
1491                                   "-D -o %s" % self.resultdir)
1492        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1493        self.assertEqual(1, len(images))
1494
1495        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1496
1497        # list directory content of the second ext4 partition
1498        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1499        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(
1500                            set(line.split()[-1] for line in result.output.split('\n') if line)), msg="Expected directories not present %s" % result.output)
1501
1502    def test_wic_cp_ext(self):
1503        """Test copy files and directories to the ext partition."""
1504        runCmd("wic create wictestdisk "
1505                                   "--image-name=core-image-minimal "
1506                                   "-D -o %s" % self.resultdir)
1507        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1508        self.assertEqual(1, len(images))
1509
1510        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1511
1512        # list directory content of the ext4 partition
1513        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1514        dirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1515        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs), msg="Expected directories not present %s" % dirs)
1516
1517        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1518            testfile.write("test")
1519
1520            # copy file to the partition
1521            runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot))
1522
1523            # check if file is there
1524            result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1525            newdirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1526            self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)]))
1527
1528            # check if the file to copy is in the partition
1529            result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1530            self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1531
1532            # copy file from the partition, replace the temporary file content with it and
1533            # check for the file size to validate the copy
1534            runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot))
1535            self.assertTrue(os.stat(testfile.name).st_size > 0, msg="Filesize not as expected %s" % os.stat(testfile.name).st_size)
1536
1537
1538    def test_wic_rm_ext(self):
1539        """Test removing files from the ext partition."""
1540        runCmd("wic create mkefidisk "
1541                                   "--image-name=core-image-minimal "
1542                                   "-D -o %s" % self.resultdir)
1543        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1544        self.assertEqual(1, len(images))
1545
1546        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1547
1548        # list directory content of the /etc directory on ext4 partition
1549        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1550        self.assertIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1551
1552        # remove file
1553        runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot))
1554
1555        # check if it's removed
1556        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1557        self.assertNotIn('fstab', [line.split()[-1] for line in result.output.split('\n') if line])
1558
1559        # remove non-empty directory
1560        runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot))
1561
1562        # check if it's removed
1563        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1564        self.assertNotIn('etc', [line.split()[-1] for line in result.output.split('\n') if line])
1565