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)
733
734class Wic2(WicTestCase):
735
736    def test_bmap_short(self):
737        """Test generation of .bmap file -m option"""
738        cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
739        runCmd(cmd)
740        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
741        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
742
743    def test_bmap_long(self):
744        """Test generation of .bmap file --bmap option"""
745        cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
746        runCmd(cmd)
747        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
748        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct.bmap"))))
749
750    def test_image_env(self):
751        """Test generation of <image>.env files."""
752        image = 'core-image-minimal'
753        imgdatadir = self._get_image_env_path(image)
754
755        bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
756        basename = bb_vars['IMAGE_BASENAME']
757        self.assertEqual(basename, image)
758        path = os.path.join(imgdatadir, basename) + '.env'
759        self.assertTrue(os.path.isfile(path))
760
761        wicvars = set(bb_vars['WICVARS'].split())
762        # filter out optional variables
763        wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
764                                      'INITRD', 'INITRD_LIVE', 'ISODIR','INITRAMFS_IMAGE',
765                                      'INITRAMFS_IMAGE_BUNDLE', 'INITRAMFS_LINK_NAME',
766                                      'APPEND', 'IMAGE_EFI_BOOT_FILES'))
767        with open(path) as envfile:
768            content = dict(line.split("=", 1) for line in envfile)
769            # test if variables used by wic present in the .env file
770            for var in wicvars:
771                self.assertTrue(var in content, "%s is not in .env file" % var)
772                self.assertTrue(content[var])
773
774    def test_image_vars_dir_short(self):
775        """Test image vars directory selection -v option"""
776        image = 'core-image-minimal'
777        imgenvdir = self._get_image_env_path(image)
778        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
779
780        runCmd("wic create wictestdisk "
781                                   "--image-name=%s -v %s -n %s -o %s"
782                                   % (image, imgenvdir, native_sysroot,
783                                      self.resultdir))
784        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
785
786    def test_image_vars_dir_long(self):
787        """Test image vars directory selection --vars option"""
788        image = 'core-image-minimal'
789        imgenvdir = self._get_image_env_path(image)
790        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
791
792        runCmd("wic create wictestdisk "
793                                   "--image-name=%s "
794                                   "--vars %s "
795                                   "--native-sysroot %s "
796                                   "--outdir %s"
797                                   % (image, imgenvdir, native_sysroot,
798                                      self.resultdir))
799        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "wictestdisk-*direct"))))
800
801    # TODO this test could also work on aarch64
802    @skipIfNotArch(['i586', 'i686', 'x86_64'])
803    def test_wic_image_type(self):
804        """Test building wic images by bitbake"""
805        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
806                 'MACHINE_FEATURES:append = " efi"\n'
807        self.append_config(config)
808        bitbake('wic-image-minimal')
809        self.remove_config(config)
810
811        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
812        machine = self.td['MACHINE']
813        prefix = os.path.join(deploy_dir, 'wic-image-minimal-%s.' % machine)
814        # check if we have result image and manifests symlinks
815        # pointing to existing files
816        for suffix in ('wic', 'manifest'):
817            path = prefix + suffix
818            self.assertTrue(os.path.islink(path))
819            self.assertTrue(os.path.isfile(os.path.realpath(path)))
820
821    # TODO this should work on aarch64
822    @skipIfNotArch(['i586', 'i686', 'x86_64'])
823    @OETestTag("runqemu")
824    def test_qemu(self):
825        """Test wic-image-minimal under qemu"""
826        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
827                 'MACHINE_FEATURES:append = " efi"\n'
828        self.append_config(config)
829        bitbake('wic-image-minimal')
830        self.remove_config(config)
831
832        with runqemu('wic-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
833            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \
834                  "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'"
835            status, output = qemu.run_serial(cmd)
836            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
837            self.assertEqual(output, '4')
838            cmd = "grep UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba /etc/fstab"
839            status, output = qemu.run_serial(cmd)
840            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
841            self.assertEqual(output, 'UUID=2c71ef06-a81d-4735-9d3a-379b69c6bdba\t/media\text4\tdefaults\t0\t0')
842
843    @skipIfNotArch(['i586', 'i686', 'x86_64'])
844    @OETestTag("runqemu")
845    def test_qemu_efi(self):
846        """Test core-image-minimal efi image under qemu"""
847        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
848        self.append_config(config)
849        bitbake('core-image-minimal ovmf')
850        self.remove_config(config)
851
852        with runqemu('core-image-minimal', ssh=False,
853                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
854            cmd = "grep sda. /proc/partitions  |wc -l"
855            status, output = qemu.run_serial(cmd)
856            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
857            self.assertEqual(output, '3')
858
859    @staticmethod
860    def _make_fixed_size_wks(size):
861        """
862        Create a wks of an image with a single partition. Size of the partition is set
863        using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
864        """
865        with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
866            wkspath = tempf.name
867            tempf.write("part " \
868                     "--source rootfs --ondisk hda --align 4 --fixed-size %d "
869                     "--fstype=ext4\n" % size)
870
871        return wkspath
872
873    def _get_wic_partitions(self, wkspath, native_sysroot=None, ignore_status=False):
874        p = runCmd("wic create %s -e core-image-minimal -o %s" % (wkspath, self.resultdir),
875                   ignore_status=ignore_status)
876
877        if p.status:
878            return (p, None)
879
880        wksname = os.path.splitext(os.path.basename(wkspath))[0]
881
882        wicout = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
883
884        if not wicout:
885            return (p, None)
886
887        wicimg = wicout[0]
888
889        if not native_sysroot:
890            native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
891
892        # verify partition size with wic
893        res = runCmd("parted -m %s unit kib p 2>/dev/null" % wicimg,
894                     native_sysroot=native_sysroot)
895
896        # parse parted output which looks like this:
897        # BYT;\n
898        # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
899        # 1:0.00MiB:200MiB:200MiB:ext4::;\n
900        return (p, res.output.splitlines()[2:])
901
902    def test_fixed_size(self):
903        """
904        Test creation of a simple image with partition size controlled through
905        --fixed-size flag
906        """
907        wkspath = Wic2._make_fixed_size_wks(200)
908        _, partlns = self._get_wic_partitions(wkspath)
909        os.remove(wkspath)
910
911        self.assertEqual(partlns, [
912                        "1:4.00kiB:204804kiB:204800kiB:ext4::;",
913                        ])
914
915    def test_fixed_size_error(self):
916        """
917        Test creation of a simple image with partition size controlled through
918        --fixed-size flag. The size of partition is intentionally set to 1MiB
919        in order to trigger an error in wic.
920        """
921        wkspath = Wic2._make_fixed_size_wks(1)
922        p, _ = self._get_wic_partitions(wkspath, ignore_status=True)
923        os.remove(wkspath)
924
925        self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
926
927    def test_offset(self):
928        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
929
930        with NamedTemporaryFile("w", suffix=".wks") as tempf:
931            # Test that partitions are placed at the correct offsets, default KB
932            tempf.write("bootloader --ptable gpt\n" \
933                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
934                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
935            tempf.flush()
936
937            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
938            self.assertEqual(partlns, [
939                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
940                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
941                ])
942
943        with NamedTemporaryFile("w", suffix=".wks") as tempf:
944            # Test that partitions are placed at the correct offsets, same with explicit KB
945            tempf.write("bootloader --ptable gpt\n" \
946                        "part /    --source rootfs --ondisk hda --offset 32K     --fixed-size 100M --fstype=ext4\n" \
947                        "part /bar                 --ondisk hda --offset 102432K --fixed-size 100M --fstype=ext4\n")
948            tempf.flush()
949
950            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
951            self.assertEqual(partlns, [
952                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
953                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
954                ])
955
956        with NamedTemporaryFile("w", suffix=".wks") as tempf:
957            # Test that partitions are placed at the correct offsets using MB
958            tempf.write("bootloader --ptable gpt\n" \
959                        "part /    --source rootfs --ondisk hda --offset 32K  --fixed-size 100M --fstype=ext4\n" \
960                        "part /bar                 --ondisk hda --offset 101M --fixed-size 100M --fstype=ext4\n")
961            tempf.flush()
962
963            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
964            self.assertEqual(partlns, [
965                "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;",
966                "2:103424kiB:205824kiB:102400kiB:ext4:primary:;",
967                ])
968
969        with NamedTemporaryFile("w", suffix=".wks") as tempf:
970            # Test that partitions can be placed on a 512 byte sector boundary
971            tempf.write("bootloader --ptable gpt\n" \
972                        "part /    --source rootfs --ondisk hda --offset 65s --fixed-size 99M --fstype=ext4\n" \
973                        "part /bar                 --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n")
974            tempf.flush()
975
976            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
977            self.assertEqual(partlns, [
978                "1:32.5kiB:101408kiB:101376kiB:ext4:primary:;",
979                "2:102432kiB:204832kiB:102400kiB:ext4:primary:;",
980                ])
981
982        with NamedTemporaryFile("w", suffix=".wks") as tempf:
983            # Test that a partition can be placed immediately after a MSDOS partition table
984            tempf.write("bootloader --ptable msdos\n" \
985                        "part /    --source rootfs --ondisk hda --offset 1s --fixed-size 100M --fstype=ext4\n")
986            tempf.flush()
987
988            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
989            self.assertEqual(partlns, [
990                "1:0.50kiB:102400kiB:102400kiB:ext4::;",
991                ])
992
993        with NamedTemporaryFile("w", suffix=".wks") as tempf:
994            # Test that image creation fails if the partitions would overlap
995            tempf.write("bootloader --ptable gpt\n" \
996                        "part /    --source rootfs --ondisk hda --offset 32     --fixed-size 100M --fstype=ext4\n" \
997                        "part /bar                 --ondisk hda --offset 102431 --fixed-size 100M --fstype=ext4\n")
998            tempf.flush()
999
1000            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1001            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1002
1003        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1004            # Test that partitions are not allowed to overlap with the booloader
1005            tempf.write("bootloader --ptable gpt\n" \
1006                        "part /    --source rootfs --ondisk hda --offset 8 --fixed-size 100M --fstype=ext4\n")
1007            tempf.flush()
1008
1009            p, _ = self._get_wic_partitions(tempf.name, ignore_status=True)
1010            self.assertNotEqual(p.status, 0, "wic exited successfully when an error was expected:\n%s" % p.output)
1011
1012    def test_extra_space(self):
1013        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
1014
1015        with NamedTemporaryFile("w", suffix=".wks") as tempf:
1016            tempf.write("bootloader --ptable gpt\n" \
1017                        "part /     --source rootfs --ondisk hda --extra-space 200M --fstype=ext4\n")
1018            tempf.flush()
1019
1020            _, partlns = self._get_wic_partitions(tempf.name, native_sysroot)
1021            self.assertEqual(len(partlns), 1)
1022            size = partlns[0].split(':')[3]
1023            self.assertRegex(size, r'^[0-9]+kiB$')
1024            size = int(size[:-3])
1025            self.assertGreaterEqual(size, 204800)
1026
1027    # TODO this test could also work on aarch64
1028    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1029    @OETestTag("runqemu")
1030    def test_rawcopy_plugin_qemu(self):
1031        """Test rawcopy plugin in qemu"""
1032        # build ext4 and then use it for a wic image
1033        config = 'IMAGE_FSTYPES = "ext4"\n'
1034        self.append_config(config)
1035        bitbake('core-image-minimal')
1036        self.remove_config(config)
1037
1038        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_rawcopy_plugin.wks.in"\n'
1039        self.append_config(config)
1040        bitbake('core-image-minimal-mtdutils')
1041        self.remove_config(config)
1042
1043        with runqemu('core-image-minimal-mtdutils', ssh=False,
1044                     runqemuparams='nographic', image_fstype='wic') as qemu:
1045            cmd = "grep sda. /proc/partitions  |wc -l"
1046            status, output = qemu.run_serial(cmd)
1047            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1048            self.assertEqual(output, '2')
1049
1050    def _rawcopy_plugin(self, fstype):
1051        """Test rawcopy plugin"""
1052        img = 'core-image-minimal'
1053        machine = self.td["MACHINE"]
1054        params = ',unpack' if fstype.endswith('.gz') else ''
1055        with NamedTemporaryFile("w", suffix=".wks") as wks:
1056            wks.write('part / --source rawcopy --sourceparams="file=%s-%s.%s%s"\n'\
1057                      % (img, machine, fstype, params))
1058            wks.flush()
1059            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1060            runCmd(cmd)
1061            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1062            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1063            self.assertEqual(1, len(out))
1064
1065    def test_rawcopy_plugin(self):
1066        self._rawcopy_plugin('ext4')
1067
1068    def test_rawcopy_plugin_unpack(self):
1069        fstype = 'ext4.gz'
1070        config = 'IMAGE_FSTYPES = "%s"\n' % fstype
1071        self.append_config(config)
1072        self.assertEqual(0, bitbake('core-image-minimal').status)
1073        self.remove_config(config)
1074        self._rawcopy_plugin(fstype)
1075
1076    def test_empty_plugin(self):
1077        """Test empty plugin"""
1078        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_empty_plugin.wks"\n'
1079        self.append_config(config)
1080        bitbake('core-image-minimal')
1081        self.remove_config(config)
1082        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1083        machine = self.td['MACHINE']
1084
1085        image_path = os.path.join(deploy_dir, 'core-image-minimal-%s.wic' % machine)
1086        self.assertTrue(os.path.exists(image_path))
1087
1088        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1089
1090        # Fstype column from 'wic ls' should be empty for the second partition
1091        # as listed in test_empty_plugin.wks
1092        result = runCmd("wic ls %s -n %s | awk -F ' ' '{print $1 \" \" $5}' | grep '^2' | wc -w" % (image_path, sysroot))
1093        self.assertEqual('1', result.output)
1094
1095    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1096    @OETestTag("runqemu")
1097    def test_biosplusefi_plugin_qemu(self):
1098        """Test biosplusefi plugin in qemu"""
1099        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1100        self.append_config(config)
1101        bitbake('core-image-minimal')
1102        self.remove_config(config)
1103
1104        with runqemu('core-image-minimal', ssh=False,
1105                     runqemuparams='nographic', image_fstype='wic') as qemu:
1106            # Check that we have ONLY two /dev/sda* partitions (/boot and /)
1107            cmd = "grep sda. /proc/partitions | wc -l"
1108            status, output = qemu.run_serial(cmd)
1109            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1110            self.assertEqual(output, '2')
1111            # Check that /dev/sda1 is /boot and that either /dev/root OR /dev/sda2 is /
1112            cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' -e '/dev/root /|/dev/sda2 /'"
1113            status, output = qemu.run_serial(cmd)
1114            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1115            self.assertEqual(output, '2')
1116            # Check that /boot has EFI bootx64.efi (required for EFI)
1117            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1118            status, output = qemu.run_serial(cmd)
1119            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1120            self.assertEqual(output, '1')
1121            # Check that "BOOTABLE" flag is set on boot partition (required for PC-Bios)
1122            # Trailing "cat" seems to be required; otherwise run_serial() sends back echo of the input command
1123            cmd = "fdisk -l /dev/sda | grep /dev/sda1 | awk {print'$2'} | cat"
1124            status, output = qemu.run_serial(cmd)
1125            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1126            self.assertEqual(output, '*')
1127
1128    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1129    def test_biosplusefi_plugin(self):
1130        """Test biosplusefi plugin"""
1131        # Wic generation below may fail depending on the order of the unittests
1132        # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1133        #    which may or may not exists depending on what was built already
1134        # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1135        #   will raise with "Couldn't find correct bootimg_dir"
1136        # The easiest way to work-around this issue is to make sure we already built an image here, hence the bitbake call
1137        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "test_biosplusefi_plugin.wks"\nMACHINE_FEATURES:append = " efi"\n'
1138        self.append_config(config)
1139        bitbake('core-image-minimal')
1140        self.remove_config(config)
1141
1142        img = 'core-image-minimal'
1143        with NamedTemporaryFile("w", suffix=".wks") as wks:
1144            wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n',
1145                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1146                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1147            wks.flush()
1148            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1149            runCmd(cmd)
1150            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1151            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1152            self.assertEqual(1, len(out))
1153
1154    @skipIfNotArch(['i586', 'i686', 'x86_64', 'aarch64'])
1155    def test_uefi_kernel(self):
1156        """ Test uefi-kernel in wic """
1157        config = 'IMAGE_EFI_BOOT_FILES="/etc/fstab;testfile"\nIMAGE_FSTYPES = "wic"\nWKS_FILE = "test_uefikernel.wks"\nMACHINE_FEATURES:append = " efi"\n'
1158        self.append_config(config)
1159        bitbake('core-image-minimal')
1160        self.remove_config(config)
1161
1162        img = 'core-image-minimal'
1163        with NamedTemporaryFile("w", suffix=".wks") as wks:
1164            wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n'
1165                            'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1166                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1167            wks.flush()
1168            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1169            runCmd(cmd)
1170            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1171            out = glob(os.path.join(self.resultdir, "%s-*.direct" % wksname))
1172            self.assertEqual(1, len(out))
1173
1174    # TODO this test could also work on aarch64
1175    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1176    @OETestTag("runqemu")
1177    def test_efi_plugin_unified_kernel_image_qemu(self):
1178        """Test efi plugin's Unified Kernel Image feature in qemu"""
1179        config = 'IMAGE_FSTYPES = "wic"\n'\
1180                 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
1181                 'WKS_FILE = "test_efi_plugin.wks"\n'\
1182                 'MACHINE_FEATURES:append = " efi"\n'
1183        self.append_config(config)
1184        bitbake('core-image-minimal core-image-minimal-initramfs ovmf')
1185        self.remove_config(config)
1186
1187        with runqemu('core-image-minimal', ssh=False,
1188                     runqemuparams='nographic ovmf', image_fstype='wic') as qemu:
1189            # Check that /boot has EFI bootx64.efi (required for EFI)
1190            cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
1191            status, output = qemu.run_serial(cmd)
1192            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1193            self.assertEqual(output, '1')
1194            # Check that /boot has EFI/Linux/linux.efi (required for Unified Kernel Images auto detection)
1195            cmd = "ls /boot/EFI/Linux/linux.efi | wc -l"
1196            status, output = qemu.run_serial(cmd)
1197            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1198            self.assertEqual(output, '1')
1199            # Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
1200            cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
1201            status, output = qemu.run_serial(cmd)
1202            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1203            self.assertEqual(output, '0')
1204
1205    def test_fs_types(self):
1206        """Test filesystem types for empty and not empty partitions"""
1207        img = 'core-image-minimal'
1208        with NamedTemporaryFile("w", suffix=".wks") as wks:
1209            wks.writelines(['part ext2   --fstype ext2     --source rootfs\n',
1210                            'part btrfs  --fstype btrfs    --source rootfs --size 40M\n',
1211                            'part squash --fstype squashfs --source rootfs\n',
1212                            'part swap   --fstype swap --size 1M\n',
1213                            'part emptyvfat   --fstype vfat   --size 1M\n',
1214                            'part emptymsdos  --fstype msdos  --size 1M\n',
1215                            'part emptyext2   --fstype ext2   --size 1M\n',
1216                            'part emptybtrfs  --fstype btrfs  --size 150M\n'])
1217            wks.flush()
1218            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1219            runCmd(cmd)
1220            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1221            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1222            self.assertEqual(1, len(out))
1223
1224    def test_kickstart_parser(self):
1225        """Test wks parser options"""
1226        with NamedTemporaryFile("w", suffix=".wks") as wks:
1227            wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
1228                            '--overhead-factor 1.2 --size 100k\n'])
1229            wks.flush()
1230            cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
1231            runCmd(cmd)
1232            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1233            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1234            self.assertEqual(1, len(out))
1235
1236    def test_image_bootpart_globbed(self):
1237        """Test globbed sources with image-bootpart plugin"""
1238        img = "core-image-minimal"
1239        cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
1240        config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
1241        self.append_config(config)
1242        runCmd(cmd)
1243        self.remove_config(config)
1244        self.assertEqual(1, len(glob(os.path.join(self.resultdir, "sdimage-bootpart-*direct"))))
1245
1246    def test_sparse_copy(self):
1247        """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
1248        libpath = os.path.join(self.td['COREBASE'], 'scripts', 'lib', 'wic')
1249        sys.path.insert(0, libpath)
1250        from  filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
1251        with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
1252            src_name = sparse.name
1253            src_size = 1024 * 10
1254            sparse.truncate(src_size)
1255            # write one byte to the file
1256            with open(src_name, 'r+b') as sfile:
1257                sfile.seek(1024 * 4)
1258                sfile.write(b'\x00')
1259            dest = sparse.name + '.out'
1260            # copy src file to dest using different filemap APIs
1261            for api in (FilemapFiemap, FilemapSeek, None):
1262                if os.path.exists(dest):
1263                    os.unlink(dest)
1264                try:
1265                    sparse_copy(sparse.name, dest, api=api)
1266                except ErrorNotSupp:
1267                    continue # skip unsupported API
1268                dest_stat = os.stat(dest)
1269                self.assertEqual(dest_stat.st_size, src_size)
1270                # 8 blocks is 4K (physical sector size)
1271                self.assertEqual(dest_stat.st_blocks, 8)
1272            os.unlink(dest)
1273
1274    def test_mkfs_extraopts(self):
1275        """Test wks option --mkfs-extraopts for empty and not empty partitions"""
1276        img = 'core-image-minimal'
1277        with NamedTemporaryFile("w", suffix=".wks") as wks:
1278            wks.writelines(
1279                ['part ext2   --fstype ext2     --source rootfs --mkfs-extraopts "-D -F -i 8192"\n',
1280                 "part btrfs  --fstype btrfs    --source rootfs --size 40M --mkfs-extraopts='--quiet'\n",
1281                 'part squash --fstype squashfs --source rootfs --mkfs-extraopts "-no-sparse -b 4096"\n',
1282                 'part emptyvfat   --fstype vfat   --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1283                 'part emptymsdos  --fstype msdos  --size 1M --mkfs-extraopts "-S 1024 -s 64"\n',
1284                 'part emptyext2   --fstype ext2   --size 1M --mkfs-extraopts "-D -F -i 8192"\n',
1285                 'part emptybtrfs  --fstype btrfs  --size 100M --mkfs-extraopts "--mixed -K"\n'])
1286            wks.flush()
1287            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
1288            runCmd(cmd)
1289            wksname = os.path.splitext(os.path.basename(wks.name))[0]
1290            out = glob(os.path.join(self.resultdir, "%s-*direct" % wksname))
1291            self.assertEqual(1, len(out))
1292
1293    @skipIfNotArch(['i586', 'i686', 'x86_64'])
1294    @OETestTag("runqemu")
1295    def test_expand_mbr_image(self):
1296        """Test wic write --expand command for mbr image"""
1297        # build an image
1298        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "directdisk.wks"\n'
1299        self.append_config(config)
1300        bitbake('core-image-minimal')
1301
1302        # get path to the image
1303        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
1304        machine = self.td['MACHINE']
1305        image_path = os.path.join(deploy_dir, 'core-image-minimal-%s.wic' % machine)
1306
1307        self.remove_config(config)
1308
1309        try:
1310            # expand image to 1G
1311            new_image_path = None
1312            with NamedTemporaryFile(mode='wb', suffix='.wic.exp',
1313                                    dir=deploy_dir, delete=False) as sparse:
1314                sparse.truncate(1024 ** 3)
1315                new_image_path = sparse.name
1316
1317            sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1318            cmd = "wic write -n %s --expand 1:0 %s %s" % (sysroot, image_path, new_image_path)
1319            runCmd(cmd)
1320
1321            # check if partitions are expanded
1322            orig = runCmd("wic ls %s -n %s" % (image_path, sysroot))
1323            exp = runCmd("wic ls %s -n %s" % (new_image_path, sysroot))
1324            orig_sizes = [int(line.split()[3]) for line in orig.output.split('\n')[1:]]
1325            exp_sizes = [int(line.split()[3]) for line in exp.output.split('\n')[1:]]
1326            self.assertEqual(orig_sizes[0], exp_sizes[0]) # first partition is not resized
1327            self.assertTrue(orig_sizes[1] < exp_sizes[1])
1328
1329            # Check if all free space is partitioned
1330            result = runCmd("%s/usr/sbin/sfdisk -F %s" % (sysroot, new_image_path))
1331            self.assertTrue("0 B, 0 bytes, 0 sectors" in result.output)
1332
1333            os.rename(image_path, image_path + '.bak')
1334            os.rename(new_image_path, image_path)
1335
1336            # Check if it boots in qemu
1337            with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic') as qemu:
1338                cmd = "ls /etc/"
1339                status, output = qemu.run_serial('true')
1340                self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
1341        finally:
1342            if os.path.exists(new_image_path):
1343                os.unlink(new_image_path)
1344            if os.path.exists(image_path + '.bak'):
1345                os.rename(image_path + '.bak', image_path)
1346
1347class ModifyTests(WicTestCase):
1348    def test_wic_ls(self):
1349        """Test listing image content using 'wic ls'"""
1350        runCmd("wic create wictestdisk "
1351                                   "--image-name=core-image-minimal "
1352                                   "-D -o %s" % self.resultdir)
1353        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1354        self.assertEqual(1, len(images))
1355
1356        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1357
1358        # list partitions
1359        result = runCmd("wic ls %s -n %s" % (images[0], sysroot))
1360        self.assertEqual(3, len(result.output.split('\n')))
1361
1362        # list directory content of the first partition
1363        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1364        self.assertEqual(6, len(result.output.split('\n')))
1365
1366    def test_wic_cp(self):
1367        """Test copy files and directories to the the wic image."""
1368        runCmd("wic create wictestdisk "
1369                                   "--image-name=core-image-minimal "
1370                                   "-D -o %s" % self.resultdir)
1371        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1372        self.assertEqual(1, len(images))
1373
1374        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1375
1376        # list directory content of the first partition
1377        result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1378        self.assertEqual(6, len(result.output.split('\n')))
1379
1380        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1381            testfile.write("test")
1382
1383            # copy file to the partition
1384            runCmd("wic cp %s %s:1/ -n %s" % (testfile.name, images[0], sysroot))
1385
1386            # check if file is there
1387            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1388            self.assertEqual(7, len(result.output.split('\n')))
1389            self.assertTrue(os.path.basename(testfile.name) in result.output)
1390
1391            # prepare directory
1392            testdir = os.path.join(self.resultdir, 'wic-test-cp-dir')
1393            testsubdir = os.path.join(testdir, 'subdir')
1394            os.makedirs(os.path.join(testsubdir))
1395            copy(testfile.name, testdir)
1396
1397            # copy directory to the partition
1398            runCmd("wic cp %s %s:1/ -n %s" % (testdir, images[0], sysroot))
1399
1400            # check if directory is there
1401            result = runCmd("wic ls %s:1/ -n %s" % (images[0], sysroot))
1402            self.assertEqual(8, len(result.output.split('\n')))
1403            self.assertTrue(os.path.basename(testdir) in result.output)
1404
1405            # copy the file from the partition and check if it success
1406            dest = '%s-cp' % testfile.name
1407            runCmd("wic cp %s:1/%s %s -n %s" % (images[0],
1408                    os.path.basename(testfile.name), dest, sysroot))
1409            self.assertTrue(os.path.exists(dest))
1410
1411
1412    def test_wic_rm(self):
1413        """Test removing files and directories from the the wic image."""
1414        runCmd("wic create mkefidisk "
1415                                   "--image-name=core-image-minimal "
1416                                   "-D -o %s" % self.resultdir)
1417        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1418        self.assertEqual(1, len(images))
1419
1420        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1421        # Not bulletproof but hopefully sufficient
1422        kerneltype = get_bb_var('KERNEL_IMAGETYPE', 'virtual/kernel')
1423
1424        # list directory content of the first partition
1425        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1426        self.assertIn('\n%s ' % kerneltype.upper(), result.output)
1427        self.assertIn('\nEFI          <DIR>     ', result.output)
1428
1429        # remove file. EFI partitions are case-insensitive so exercise that too
1430        runCmd("wic rm %s:1/%s -n %s" % (images[0], kerneltype.lower(), sysroot))
1431
1432        # remove directory
1433        runCmd("wic rm %s:1/efi -n %s" % (images[0], sysroot))
1434
1435        # check if they're removed
1436        result = runCmd("wic ls %s:1 -n %s" % (images[0], sysroot))
1437        self.assertNotIn('\n%s        ' % kerneltype.upper(), result.output)
1438        self.assertNotIn('\nEFI          <DIR>     ', result.output)
1439
1440    def test_wic_ls_ext(self):
1441        """Test listing content of the ext partition using 'wic ls'"""
1442        runCmd("wic create wictestdisk "
1443                                   "--image-name=core-image-minimal "
1444                                   "-D -o %s" % self.resultdir)
1445        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1446        self.assertEqual(1, len(images))
1447
1448        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1449
1450        # list directory content of the second ext4 partition
1451        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1452        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(
1453                            set(line.split()[-1] for line in result.output.split('\n') if line)))
1454
1455    def test_wic_cp_ext(self):
1456        """Test copy files and directories to the ext partition."""
1457        runCmd("wic create wictestdisk "
1458                                   "--image-name=core-image-minimal "
1459                                   "-D -o %s" % self.resultdir)
1460        images = glob(os.path.join(self.resultdir, "wictestdisk-*.direct"))
1461        self.assertEqual(1, len(images))
1462
1463        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1464
1465        # list directory content of the ext4 partition
1466        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1467        dirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1468        self.assertTrue(set(['bin', 'home', 'proc', 'usr', 'var', 'dev', 'lib', 'sbin']).issubset(dirs))
1469
1470        with NamedTemporaryFile("w", suffix=".wic-cp") as testfile:
1471            testfile.write("test")
1472
1473            # copy file to the partition
1474            runCmd("wic cp %s %s:2/ -n %s" % (testfile.name, images[0], sysroot))
1475
1476            # check if file is there
1477            result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1478            newdirs = set(line.split()[-1] for line in result.output.split('\n') if line)
1479            self.assertEqual(newdirs.difference(dirs), set([os.path.basename(testfile.name)]))
1480
1481            # check if the file to copy is in the partition
1482            result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1483            self.assertTrue('fstab' in [line.split()[-1] for line in result.output.split('\n') if line])
1484
1485            # copy file from the partition, replace the temporary file content with it and
1486            # check for the file size to validate the copy
1487            runCmd("wic cp %s:2/etc/fstab %s -n %s" % (images[0], testfile.name, sysroot))
1488            self.assertTrue(os.stat(testfile.name).st_size > 0)
1489
1490
1491    def test_wic_rm_ext(self):
1492        """Test removing files from the ext partition."""
1493        runCmd("wic create mkefidisk "
1494                                   "--image-name=core-image-minimal "
1495                                   "-D -o %s" % self.resultdir)
1496        images = glob(os.path.join(self.resultdir, "mkefidisk-*.direct"))
1497        self.assertEqual(1, len(images))
1498
1499        sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools')
1500
1501        # list directory content of the /etc directory on ext4 partition
1502        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1503        self.assertTrue('fstab' in [line.split()[-1] for line in result.output.split('\n') if line])
1504
1505        # remove file
1506        runCmd("wic rm %s:2/etc/fstab -n %s" % (images[0], sysroot))
1507
1508        # check if it's removed
1509        result = runCmd("wic ls %s:2/etc/ -n %s" % (images[0], sysroot))
1510        self.assertTrue('fstab' not in [line.split()[-1] for line in result.output.split('\n') if line])
1511
1512        # remove non-empty directory
1513        runCmd("wic rm -r %s:2/etc/ -n %s" % (images[0], sysroot))
1514
1515        # check if it's removed
1516        result = runCmd("wic ls %s:2/ -n %s" % (images[0], sysroot))
1517        self.assertTrue('etc' not in [line.split()[-1] for line in result.output.split('\n') if line])
1518