summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/cases/retain.py
blob: 892be45857abfccb6361dd47e5907db8a8340591 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Tests for retain.bbclass
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: MIT
#

import os
import glob
import fnmatch
import oe.path
import shutil
import tarfile
from oeqa.utils.commands import bitbake, get_bb_vars
from oeqa.selftest.case import OESelftestTestCase

class Retain(OESelftestTestCase):

    def test_retain_always(self):
        """
        Summary:     Test retain class with RETAIN_DIRS_ALWAYS
        Expected:    Archive written to RETAIN_OUTDIR when build of test recipe completes
        Product:     oe-core
        Author:      Paul Eggleton <paul.eggleton@microsoft.com>
        """

        test_recipe = 'quilt-native'

        features = 'INHERIT += "retain"\n'
        features += 'RETAIN_DIRS_ALWAYS = "${T}"\n'
        self.write_config(features)

        bitbake('-c clean %s' % test_recipe)

        bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR'])
        retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
        tmpdir = bb_vars['TMPDIR']
        if len(retain_outdir) < 5:
            self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
        if not oe.path.is_path_parent(tmpdir, retain_outdir):
            self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
        try:
            shutil.rmtree(retain_outdir)
        except FileNotFoundError:
            pass

        bitbake(test_recipe)
        if not glob.glob(os.path.join(retain_outdir, '%s_temp_*.tar.gz' % test_recipe)):
            self.fail('No output archive for %s created' % test_recipe)


    def test_retain_failure(self):
        """
        Summary:     Test retain class default behaviour
        Expected:    Archive written to RETAIN_OUTDIR only when build of test
                     recipe fails, and archive contents are as expected
        Product:     oe-core
        Author:      Paul Eggleton <paul.eggleton@microsoft.com>
        """

        test_recipe_fail = 'error'

        features = 'INHERIT += "retain"\n'
        self.write_config(features)

        bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'RETAIN_DIRS_ALWAYS', 'RETAIN_DIRS_GLOBAL_ALWAYS'])
        if bb_vars['RETAIN_DIRS_ALWAYS']:
            self.fail('RETAIN_DIRS_ALWAYS is set, this interferes with the test')
        if bb_vars['RETAIN_DIRS_GLOBAL_ALWAYS']:
            self.fail('RETAIN_DIRS_GLOBAL_ALWAYS is set, this interferes with the test')
        retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
        tmpdir = bb_vars['TMPDIR']
        if len(retain_outdir) < 5:
            self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
        if not oe.path.is_path_parent(tmpdir, retain_outdir):
            self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))

        try:
            shutil.rmtree(retain_outdir)
        except FileNotFoundError:
            pass

        bitbake('-c clean %s' % test_recipe_fail)

        if os.path.exists(retain_outdir):
            retain_dirlist = os.listdir(retain_outdir)
            if retain_dirlist:
                self.fail('RETAIN_OUTDIR should be empty without failure, contents:\n%s' % '\n'.join(retain_dirlist))

        result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
        if result.status == 0:
            self.fail('Build of %s did not fail as expected' % test_recipe_fail)

        archives = glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % test_recipe_fail))
        if not archives:
            self.fail('No output archive for %s created' % test_recipe_fail)
        if len(archives) > 1:
            self.fail('More than one archive for %s created' % test_recipe_fail)
        for archive in archives:
            found = False
            archive_prefix = os.path.basename(archive).split('.tar')[0]
            expected_prefix_start = '%s_workdir' % test_recipe_fail
            if not archive_prefix.startswith(expected_prefix_start):
                self.fail('Archive %s name does not start with expected prefix "%s"' % (os.path.basename(archive), expected_prefix_start))
            with tarfile.open(archive) as tf:
                for ti in tf:
                    if not fnmatch.fnmatch(ti.name, '%s/*' % archive_prefix):
                        self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name))
                    if ti.name.endswith('/temp/log.do_compile'):
                        found = True
            if not found:
                self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive))


    def test_retain_global(self):
        """
        Summary:     Test retain class RETAIN_DIRS_GLOBAL_* behaviour
        Expected:    Ensure RETAIN_DIRS_GLOBAL_ALWAYS always causes an
                     archive to be created, and RETAIN_DIRS_GLOBAL_FAILURE
                     only causes an archive to be created on failure.
                     Also test archive naming (with : character) as an
                     added bonus.
        Product:     oe-core
        Author:      Paul Eggleton <paul.eggleton@microsoft.com>
        """

        test_recipe = 'quilt-native'
        test_recipe_fail = 'error'

        features = 'INHERIT += "retain"\n'
        features += 'RETAIN_DIRS_GLOBAL_ALWAYS = "${LOG_DIR};prefix=buildlogs"\n'
        features += 'RETAIN_DIRS_GLOBAL_FAILURE = "${STAMPS_DIR}"\n'
        self.write_config(features)

        bitbake('-c clean %s' % test_recipe)

        bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'STAMPS_DIR'])
        retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
        tmpdir = bb_vars['TMPDIR']
        if len(retain_outdir) < 5:
            self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
        if not oe.path.is_path_parent(tmpdir, retain_outdir):
            self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))
        try:
            shutil.rmtree(retain_outdir)
        except FileNotFoundError:
            pass

        # Test success case
        bitbake(test_recipe)
        if not glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz')):
            self.fail('No output archive for LOG_DIR created')
        stamps_dir = bb_vars['STAMPS_DIR']
        if glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))):
            self.fail('Output archive for STAMPS_DIR created when it should not have been')

        # Test failure case
        result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
        if result.status == 0:
            self.fail('Build of %s did not fail as expected' % test_recipe_fail)
        if not glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))):
            self.fail('Output archive for STAMPS_DIR not created')
        if len(glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz'))) != 2:
            self.fail('Should be exactly two buildlogs archives in output dir')


    def test_retain_misc(self):
        """
        Summary:     Test retain class with RETAIN_ENABLED and RETAIN_TARBALL_SUFFIX
        Expected:    Archive written to RETAIN_OUTDIR only when RETAIN_ENABLED is set
                     and archive contents are as expected. Also test archive naming
                     (with : character) as an added bonus.
        Product:     oe-core
        Author:      Paul Eggleton <paul.eggleton@microsoft.com>
        """

        test_recipe_fail = 'error'

        features = 'INHERIT += "retain"\n'
        features += 'RETAIN_DIRS_ALWAYS = "${T}"\n'
        features += 'RETAIN_ENABLED = "0"\n'
        self.write_config(features)

        bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR'])
        retain_outdir = bb_vars['RETAIN_OUTDIR'] or ''
        tmpdir = bb_vars['TMPDIR']
        if len(retain_outdir) < 5:
            self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir)
        if not oe.path.is_path_parent(tmpdir, retain_outdir):
            self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir))

        try:
            shutil.rmtree(retain_outdir)
        except FileNotFoundError:
            pass

        bitbake('-c clean %s' % test_recipe_fail)
        result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
        if result.status == 0:
            self.fail('Build of %s did not fail as expected' % test_recipe_fail)

        if os.path.exists(retain_outdir) and os.listdir(retain_outdir):
            self.fail('RETAIN_OUTDIR should be empty with RETAIN_ENABLED = "0"')

        features = 'INHERIT += "retain"\n'
        features += 'RETAIN_DIRS_ALWAYS = "${T};prefix=recipelogs"\n'
        features += 'RETAIN_TARBALL_SUFFIX = "${DATETIME}-testsuffix.tar.bz2"\n'
        features += 'RETAIN_ENABLED = "1"\n'
        self.write_config(features)

        result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True)
        if result.status == 0:
            self.fail('Build of %s did not fail as expected' % test_recipe_fail)

        archives = glob.glob(os.path.join(retain_outdir, '%s_*-testsuffix.tar.bz2' % test_recipe_fail))
        if not archives:
            self.fail('No output archive for %s created' % test_recipe_fail)
        if len(archives) != 2:
            self.fail('Two archives for %s expected, but %d exist' % (test_recipe_fail, len(archives)))
        recipelogs_found = False
        workdir_found = False
        for archive in archives:
            contents_found = False
            archive_prefix = os.path.basename(archive).split('.tar')[0]
            if archive_prefix.startswith('%s_recipelogs' % test_recipe_fail):
                recipelogs_found = True
            if archive_prefix.startswith('%s_workdir' % test_recipe_fail):
                workdir_found = True
            with tarfile.open(archive, 'r:bz2') as tf:
                for ti in tf:
                    if not fnmatch.fnmatch(ti.name, '%s/*' % (archive_prefix)):
                        self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name))
                    if ti.name.endswith('/log.do_compile'):
                        contents_found = True
            if not contents_found:
                # Both archives should contain this file
                self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive))
        if not recipelogs_found:
            self.fail('No archive with expected "recipelogs" prefix found')
        if not workdir_found:
            self.fail('No archive with expected "workdir" prefix found')