xref: /openbmc/openbmc/poky/meta/lib/oeqa/selftest/case.py (revision ac13d5f3)
1#
2# Copyright (C) 2013-2017 Intel Corporation
3#
4# SPDX-License-Identifier: MIT
5#
6
7import sys
8import os
9import glob
10import errno
11from unittest.util import safe_repr
12
13import oeqa.utils.ftools as ftools
14from oeqa.utils.commands import runCmd, bitbake, get_bb_var
15from oeqa.core.case import OETestCase
16
17import bb.utils
18
19class OESelftestTestCase(OETestCase):
20    def __init__(self, methodName="runTest"):
21        self._extra_tear_down_commands = []
22        super(OESelftestTestCase, self).__init__(methodName)
23
24    @classmethod
25    def setUpClass(cls):
26        super(OESelftestTestCase, cls).setUpClass()
27
28        cls.testlayer_path = cls.tc.config_paths['testlayer_path']
29        cls.builddir = cls.tc.config_paths['builddir']
30
31        cls.localconf_path = cls.tc.config_paths['localconf']
32        cls.local_bblayers_path = cls.tc.config_paths['bblayers']
33
34        cls.testinc_path = os.path.join(cls.tc.config_paths['builddir'],
35                "conf/selftest.inc")
36        cls.testinc_bblayers_path = os.path.join(cls.tc.config_paths['builddir'],
37                "conf/bblayers.inc")
38        cls.machineinc_path = os.path.join(cls.tc.config_paths['builddir'],
39                "conf/machine.inc")
40
41        cls._track_for_cleanup = [
42            cls.testinc_path, cls.testinc_bblayers_path,
43            cls.machineinc_path]
44
45        cls.add_include()
46
47    @classmethod
48    def tearDownClass(cls):
49        cls.remove_include()
50        cls.remove_inc_files()
51        super(OESelftestTestCase, cls).tearDownClass()
52
53    @classmethod
54    def add_include(cls):
55        if "#include added by oe-selftest" \
56            not in ftools.read_file(os.path.join(cls.builddir, "conf/local.conf")):
57                cls.logger.info("Adding: \"include selftest.inc\" in %s" % os.path.join(cls.builddir, "conf/local.conf"))
58                ftools.append_file(os.path.join(cls.builddir, "conf/local.conf"), \
59                        "\n#include added by oe-selftest\ninclude machine.inc\ninclude selftest.inc")
60
61        if "#include added by oe-selftest" \
62            not in ftools.read_file(os.path.join(cls.builddir, "conf/bblayers.conf")):
63                cls.logger.info("Adding: \"include bblayers.inc\" in bblayers.conf")
64                ftools.append_file(os.path.join(cls.builddir, "conf/bblayers.conf"), \
65                        "\n#include added by oe-selftest\ninclude bblayers.inc")
66
67    @classmethod
68    def remove_include(cls):
69        if "#include added by oe-selftest.py" \
70            in ftools.read_file(os.path.join(cls.builddir, "conf/local.conf")):
71                cls.logger.info("Removing the include from local.conf")
72                ftools.remove_from_file(os.path.join(cls.builddir, "conf/local.conf"), \
73                        "\n#include added by oe-selftest.py\ninclude machine.inc\ninclude selftest.inc")
74
75        if "#include added by oe-selftest.py" \
76            in ftools.read_file(os.path.join(cls.builddir, "conf/bblayers.conf")):
77                cls.logger.info("Removing the include from bblayers.conf")
78                ftools.remove_from_file(os.path.join(cls.builddir, "conf/bblayers.conf"), \
79                        "\n#include added by oe-selftest.py\ninclude bblayers.inc")
80
81    @classmethod
82    def remove_inc_files(cls):
83        try:
84            os.remove(os.path.join(cls.builddir, "conf/selftest.inc"))
85            for root, _, files in os.walk(cls.testlayer_path):
86                for f in files:
87                    if f == 'test_recipe.inc':
88                        os.remove(os.path.join(root, f))
89        except OSError as e:
90            pass
91
92        for incl_file in ['conf/bblayers.inc', 'conf/machine.inc']:
93            try:
94                os.remove(os.path.join(cls.builddir, incl_file))
95            except:
96                pass
97
98    def setUp(self):
99        super(OESelftestTestCase, self).setUp()
100        os.chdir(self.builddir)
101        # we don't know what the previous test left around in config or inc files
102        # if it failed so we need a fresh start
103        try:
104            os.remove(self.testinc_path)
105        except OSError as e:
106            if e.errno != errno.ENOENT:
107                raise
108        for root, _, files in os.walk(self.testlayer_path):
109            for f in files:
110                if f == 'test_recipe.inc':
111                    os.remove(os.path.join(root, f))
112
113        for incl_file in [self.testinc_bblayers_path, self.machineinc_path]:
114            try:
115                os.remove(incl_file)
116            except OSError as e:
117                if e.errno != errno.ENOENT:
118                    raise
119
120        # tests might need their own setup
121        # but if they overwrite this one they have to call
122        # super each time, so let's give them an alternative
123        self.setUpLocal()
124
125    def setUpLocal(self):
126        pass
127
128    def tearDown(self):
129        if self._extra_tear_down_commands:
130            failed_extra_commands = []
131            for command in self._extra_tear_down_commands:
132                result = runCmd(command, ignore_status=True)
133                if not result.status ==  0:
134                    failed_extra_commands.append(command)
135            if failed_extra_commands:
136                self.logger.warning("tearDown commands have failed: %s" % ', '.join(map(str, failed_extra_commands)))
137                self.logger.debug("Trying to move on.")
138            self._extra_tear_down_commands = []
139
140        if self._track_for_cleanup:
141            for path in self._track_for_cleanup:
142                if os.path.isdir(path):
143                    bb.utils.remove(path, recurse=True)
144                if os.path.isfile(path):
145                    os.remove(path)
146            self._track_for_cleanup = []
147
148        self.tearDownLocal()
149        super(OESelftestTestCase, self).tearDown()
150
151    def tearDownLocal(self):
152        pass
153
154    def add_command_to_tearDown(self, command):
155        """Add test specific commands to the tearDown method"""
156        self.logger.debug("Adding command '%s' to tearDown for this test." % command)
157        self._extra_tear_down_commands.append(command)
158
159    def track_for_cleanup(self, path):
160        """Add test specific files or directories to be removed in the tearDown method"""
161        self.logger.debug("Adding path '%s' to be cleaned up when test is over" % path)
162        self._track_for_cleanup.append(path)
163
164    def write_config(self, data, multiconfig=None):
165        """Write to config file"""
166        if multiconfig:
167            multiconfigdir = "%s/conf/multiconfig" % self.builddir
168            os.makedirs(multiconfigdir, exist_ok=True)
169            dest_path = '%s/%s.conf' % (multiconfigdir, multiconfig)
170            self.track_for_cleanup(dest_path)
171        else:
172            dest_path = self.testinc_path
173
174        self.logger.debug("Writing to: %s\n%s\n" % (dest_path, data))
175        ftools.write_file(dest_path, data)
176
177    def append_config(self, data):
178        """Append to <builddir>/conf/selftest.inc"""
179        self.logger.debug("Appending to: %s\n%s\n" % (self.testinc_path, data))
180        ftools.append_file(self.testinc_path, data)
181
182    def remove_config(self, data):
183        """Remove data from <builddir>/conf/selftest.inc"""
184        self.logger.debug("Removing from: %s\n%s\n" % (self.testinc_path, data))
185        ftools.remove_from_file(self.testinc_path, data)
186
187    def recipeinc(self, recipe):
188        """Return absolute path of meta-selftest/recipes-test/<recipe>/test_recipe.inc"""
189        return os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
190
191    def write_recipeinc(self, recipe, data):
192        """Write to meta-selftest/recipes-test/<recipe>/test_recipe.inc"""
193        inc_file = self.recipeinc(recipe)
194        self.logger.debug("Writing to: %s\n%s\n" % (inc_file, data))
195        ftools.write_file(inc_file, data)
196        return inc_file
197
198    def append_recipeinc(self, recipe, data):
199        """Append data to meta-selftest/recipes-test/<recipe>/test_recipe.inc"""
200        inc_file = self.recipeinc(recipe)
201        self.logger.debug("Appending to: %s\n%s\n" % (inc_file, data))
202        ftools.append_file(inc_file, data)
203        return inc_file
204
205    def remove_recipeinc(self, recipe, data):
206        """Remove data from meta-selftest/recipes-test/<recipe>/test_recipe.inc"""
207        inc_file = self.recipeinc(recipe)
208        self.logger.debug("Removing from: %s\n%s\n" % (inc_file, data))
209        ftools.remove_from_file(inc_file, data)
210
211    def delete_recipeinc(self, recipe):
212        """Delete meta-selftest/recipes-test/<recipe>/test_recipe.inc file"""
213        inc_file = self.recipeinc(recipe)
214        self.logger.debug("Deleting file: %s" % inc_file)
215        try:
216            os.remove(inc_file)
217        except OSError as e:
218            if e.errno != errno.ENOENT:
219                raise
220    def write_bblayers_config(self, data):
221        """Write to <builddir>/conf/bblayers.inc"""
222        self.logger.debug("Writing to: %s\n%s\n" % (self.testinc_bblayers_path, data))
223        ftools.write_file(self.testinc_bblayers_path, data)
224
225    def append_bblayers_config(self, data):
226        """Append to <builddir>/conf/bblayers.inc"""
227        self.logger.debug("Appending to: %s\n%s\n" % (self.testinc_bblayers_path, data))
228        ftools.append_file(self.testinc_bblayers_path, data)
229
230    def remove_bblayers_config(self, data):
231        """Remove data from <builddir>/conf/bblayers.inc"""
232        self.logger.debug("Removing from: %s\n%s\n" % (self.testinc_bblayers_path, data))
233        ftools.remove_from_file(self.testinc_bblayers_path, data)
234
235    def set_machine_config(self, data):
236        """Write to <builddir>/conf/machine.inc"""
237        self.logger.debug("Writing to: %s\n%s\n" % (self.machineinc_path, data))
238        ftools.write_file(self.machineinc_path, data)
239
240    def disable_class(self, classname):
241        destfile = "%s/classes/%s.bbclass" % (self.builddir, classname)
242        os.makedirs(os.path.dirname(destfile), exist_ok=True)
243        self.track_for_cleanup(destfile)
244        self.logger.debug("Creating empty class: %s\n" % (destfile))
245        ftools.write_file(destfile, "")
246
247    # check does path exist
248    def assertExists(self, expr, msg=None):
249        if not os.path.exists(expr):
250            msg = self._formatMessage(msg, "%s does not exist" % safe_repr(expr))
251            raise self.failureException(msg)
252
253    # check does path not exist
254    def assertNotExists(self, expr, msg=None):
255        if os.path.exists(expr):
256            msg = self._formatMessage(msg, "%s exists when it should not" % safe_repr(expr))
257            raise self.failureException(msg)
258