summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa
diff options
context:
space:
mode:
authorTudor Florea <tudor.florea@enea.com>2015-10-09 22:59:03 +0200
committerTudor Florea <tudor.florea@enea.com>2015-10-09 22:59:03 +0200
commit972dcfcdbfe75dcfeb777150c136576cf1a71e99 (patch)
tree97a61cd7e293d7ae9d56ef7ed0f81253365bb026 /meta/lib/oeqa
downloadpoky-972dcfcdbfe75dcfeb777150c136576cf1a71e99.tar.gz
initial commit for Enea Linux 5.0 arm
Signed-off-by: Tudor Florea <tudor.florea@enea.com>
Diffstat (limited to 'meta/lib/oeqa')
-rw-r--r--meta/lib/oeqa/__init__.py0
-rw-r--r--meta/lib/oeqa/controllers/__init__.py3
-rw-r--r--meta/lib/oeqa/controllers/masterimage.py201
-rw-r--r--meta/lib/oeqa/controllers/testtargetloader.py70
-rw-r--r--meta/lib/oeqa/oetest.py106
-rwxr-xr-xmeta/lib/oeqa/runexported.py140
-rw-r--r--meta/lib/oeqa/runtime/__init__.py3
-rw-r--r--meta/lib/oeqa/runtime/_ptest.py124
-rw-r--r--meta/lib/oeqa/runtime/buildcvs.py31
-rw-r--r--meta/lib/oeqa/runtime/buildiptables.py31
-rw-r--r--meta/lib/oeqa/runtime/buildsudoku.py28
-rw-r--r--meta/lib/oeqa/runtime/connman.py30
-rw-r--r--meta/lib/oeqa/runtime/date.py23
-rw-r--r--meta/lib/oeqa/runtime/df.py12
-rw-r--r--meta/lib/oeqa/runtime/dmesg.py12
-rw-r--r--meta/lib/oeqa/runtime/files/hellomod.c19
-rw-r--r--meta/lib/oeqa/runtime/files/hellomod_makefile8
-rw-r--r--meta/lib/oeqa/runtime/files/test.c26
-rw-r--r--meta/lib/oeqa/runtime/files/test.cpp3
-rw-r--r--meta/lib/oeqa/runtime/files/test.pl2
-rw-r--r--meta/lib/oeqa/runtime/files/test.py6
-rw-r--r--meta/lib/oeqa/runtime/files/testmakefile5
-rw-r--r--meta/lib/oeqa/runtime/gcc.py46
-rw-r--r--meta/lib/oeqa/runtime/kernelmodule.py34
-rw-r--r--meta/lib/oeqa/runtime/ldd.py20
-rw-r--r--meta/lib/oeqa/runtime/logrotate.py28
-rw-r--r--meta/lib/oeqa/runtime/multilib.py18
-rw-r--r--meta/lib/oeqa/runtime/pam.py25
-rw-r--r--meta/lib/oeqa/runtime/parselogs.py178
-rw-r--r--meta/lib/oeqa/runtime/perl.py29
-rw-r--r--meta/lib/oeqa/runtime/ping.py20
-rw-r--r--meta/lib/oeqa/runtime/python.py34
-rw-r--r--meta/lib/oeqa/runtime/rpm.py53
-rw-r--r--meta/lib/oeqa/runtime/scanelf.py28
-rw-r--r--meta/lib/oeqa/runtime/scp.py22
-rw-r--r--meta/lib/oeqa/runtime/skeletoninit.py29
-rw-r--r--meta/lib/oeqa/runtime/smart.py121
-rw-r--r--meta/lib/oeqa/runtime/ssh.py19
-rw-r--r--meta/lib/oeqa/runtime/syslog.py48
-rw-r--r--meta/lib/oeqa/runtime/systemd.py88
-rw-r--r--meta/lib/oeqa/runtime/vnc.py20
-rw-r--r--meta/lib/oeqa/runtime/x32lib.py18
-rw-r--r--meta/lib/oeqa/runtime/xorg.py17
-rw-r--r--meta/lib/oeqa/sdk/__init__.py3
-rw-r--r--meta/lib/oeqa/sdk/buildcvs.py25
-rw-r--r--meta/lib/oeqa/sdk/buildiptables.py26
-rw-r--r--meta/lib/oeqa/sdk/buildsudoku.py26
-rw-r--r--meta/lib/oeqa/selftest/__init__.py2
-rw-r--r--meta/lib/oeqa/selftest/_sstatetests_noauto.py95
-rw-r--r--meta/lib/oeqa/selftest/_toaster.py445
-rw-r--r--meta/lib/oeqa/selftest/base.py131
-rw-r--r--meta/lib/oeqa/selftest/bblayers.py43
-rw-r--r--meta/lib/oeqa/selftest/bbtests.py178
-rw-r--r--meta/lib/oeqa/selftest/buildhistory.py45
-rw-r--r--meta/lib/oeqa/selftest/buildoptions.py120
-rw-r--r--meta/lib/oeqa/selftest/oescripts.py54
-rw-r--r--meta/lib/oeqa/selftest/prservice.py121
-rw-r--r--meta/lib/oeqa/selftest/sstate.py53
-rw-r--r--meta/lib/oeqa/selftest/sstatetests.py204
-rw-r--r--meta/lib/oeqa/targetcontrol.py199
-rw-r--r--meta/lib/oeqa/utils/__init__.py15
-rw-r--r--meta/lib/oeqa/utils/commands.py154
-rw-r--r--meta/lib/oeqa/utils/decorators.py158
-rw-r--r--meta/lib/oeqa/utils/ftools.py27
-rw-r--r--meta/lib/oeqa/utils/httpserver.py35
-rw-r--r--meta/lib/oeqa/utils/logparser.py125
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py237
-rw-r--r--meta/lib/oeqa/utils/sshcontrol.py138
-rw-r--r--meta/lib/oeqa/utils/targetbuild.py132
69 files changed, 4569 insertions, 0 deletions
diff --git a/meta/lib/oeqa/__init__.py b/meta/lib/oeqa/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/meta/lib/oeqa/__init__.py
diff --git a/meta/lib/oeqa/controllers/__init__.py b/meta/lib/oeqa/controllers/__init__.py
new file mode 100644
index 0000000000..8eda92763c
--- /dev/null
+++ b/meta/lib/oeqa/controllers/__init__.py
@@ -0,0 +1,3 @@
1# Enable other layers to have modules in the same named directory
2from pkgutil import extend_path
3__path__ = extend_path(__path__, __name__)
diff --git a/meta/lib/oeqa/controllers/masterimage.py b/meta/lib/oeqa/controllers/masterimage.py
new file mode 100644
index 0000000000..311f0cf68c
--- /dev/null
+++ b/meta/lib/oeqa/controllers/masterimage.py
@@ -0,0 +1,201 @@
1# Copyright (C) 2014 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module adds support to testimage.bbclass to deploy images and run
6# tests using a "master image" - this is a "known good" image that is
7# installed onto the device as part of initial setup and will be booted into
8# with no interaction; we can then use it to deploy the image to be tested
9# to a second partition before running the tests.
10#
11# For an example master image, see core-image-testmaster
12# (meta/recipes-extended/images/core-image-testmaster.bb)
13
14import os
15import bb
16import traceback
17import time
18import subprocess
19
20import oeqa.targetcontrol
21import oeqa.utils.sshcontrol as sshcontrol
22import oeqa.utils.commands as commands
23from oeqa.utils import CommandError
24
25from abc import ABCMeta, abstractmethod
26
27class MasterImageHardwareTarget(oeqa.targetcontrol.BaseTarget):
28
29 __metaclass__ = ABCMeta
30
31 supported_image_fstypes = ['tar.gz', 'tar.bz2']
32
33 def __init__(self, d):
34 super(MasterImageHardwareTarget, self).__init__(d)
35
36 # target ip
37 addr = d.getVar("TEST_TARGET_IP", True) or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
38 self.ip = addr.split(":")[0]
39 try:
40 self.port = addr.split(":")[1]
41 except IndexError:
42 self.port = None
43 bb.note("Target IP: %s" % self.ip)
44 self.server_ip = d.getVar("TEST_SERVER_IP", True)
45 if not self.server_ip:
46 try:
47 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
48 except Exception as e:
49 bb.fatal("Failed to determine the host IP address (alternatively you can set TEST_SERVER_IP with the IP address of this machine): %s" % e)
50 bb.note("Server IP: %s" % self.server_ip)
51
52 # test rootfs + kernel
53 self.image_fstype = self.get_image_fstype(d)
54 self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
55 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE") + '-' + d.getVar('MACHINE') + '.bin')
56 if not os.path.isfile(self.rootfs):
57 # we could've checked that IMAGE_FSTYPES contains tar.gz but the config for running testimage might not be
58 # the same as the config with which the image was build, ie
59 # you bitbake core-image-sato with IMAGE_FSTYPES += "tar.gz"
60 # and your autobuilder overwrites the config, adds the test bits and runs bitbake core-image-sato -c testimage
61 bb.fatal("No rootfs found. Did you build the image ?\nIf yes, did you build it with IMAGE_FSTYPES += \"tar.gz\" ? \
62 \nExpected path: %s" % self.rootfs)
63 if not os.path.isfile(self.kernel):
64 bb.fatal("No kernel found. Expected path: %s" % self.kernel)
65
66 # master ssh connection
67 self.master = None
68 # if the user knows what they are doing, then by all means...
69 self.user_cmds = d.getVar("TEST_DEPLOY_CMDS", True)
70 self.deploy_cmds = None
71
72 # this is the name of the command that controls the power for a board
73 # e.g: TEST_POWERCONTROL_CMD = "/home/user/myscripts/powercontrol.py ${MACHINE} what-ever-other-args-the-script-wants"
74 # the command should take as the last argument "off" and "on" and "cycle" (off, on)
75 self.powercontrol_cmd = d.getVar("TEST_POWERCONTROL_CMD", True) or None
76 self.powercontrol_args = d.getVar("TEST_POWERCONTROL_EXTRA_ARGS") or ""
77
78 self.serialcontrol_cmd = d.getVar("TEST_SERIALCONTROL_CMD", True) or None
79 self.serialcontrol_args = d.getVar("TEST_SERIALCONTROL_EXTRA_ARGS") or ""
80
81 self.origenv = os.environ
82 if self.powercontrol_cmd or self.serialcontrol_cmd:
83 # the external script for controlling power might use ssh
84 # ssh + keys means we need the original user env
85 bborigenv = d.getVar("BB_ORIGENV", False) or {}
86 for key in bborigenv:
87 val = bborigenv.getVar(key, True)
88 if val is not None:
89 self.origenv[key] = str(val)
90
91 if self.powercontrol_cmd:
92 if self.powercontrol_args:
93 self.powercontrol_cmd = "%s %s" % (self.powercontrol_cmd, self.powercontrol_args)
94 if self.serialcontrol_cmd:
95 if self.serialcontrol_args:
96 self.serialcontrol_cmd = "%s %s" % (self.serialcontrol_cmd, self.serialcontrol_args)
97
98 def power_ctl(self, msg):
99 if self.powercontrol_cmd:
100 cmd = "%s %s" % (self.powercontrol_cmd, msg)
101 try:
102 commands.runCmd(cmd, assert_error=False, preexec_fn=os.setsid, env=self.origenv)
103 except CommandError as e:
104 bb.fatal(str(e))
105
106 def power_cycle(self, conn):
107 if self.powercontrol_cmd:
108 # be nice, don't just cut power
109 conn.run("shutdown -h now")
110 time.sleep(10)
111 self.power_ctl("cycle")
112 else:
113 status, output = conn.run("reboot")
114 if status != 0:
115 bb.error("Failed rebooting target and no power control command defined. You need to manually reset the device.\n%s" % output)
116
117 def _wait_until_booted(self):
118 ''' Waits until the target device has booted (if we have just power cycled it) '''
119 # Subclasses with better methods of determining boot can override this
120 time.sleep(120)
121
122 def deploy(self):
123 # base class just sets the ssh log file for us
124 super(MasterImageHardwareTarget, self).deploy()
125 self.master = sshcontrol.SSHControl(ip=self.ip, logfile=self.sshlog, timeout=600, port=self.port)
126 status, output = self.master.run("cat /etc/masterimage")
127 if status != 0:
128 # We're not booted into the master image, so try rebooting
129 bb.plain("%s - booting into the master image" % self.pn)
130 self.power_ctl("cycle")
131 self._wait_until_booted()
132
133 bb.plain("%s - deploying image on target" % self.pn)
134 status, output = self.master.run("cat /etc/masterimage")
135 if status != 0:
136 bb.fatal("No ssh connectivity or target isn't running a master image.\n%s" % output)
137 if self.user_cmds:
138 self.deploy_cmds = self.user_cmds.split("\n")
139 try:
140 self._deploy()
141 except Exception as e:
142 bb.fatal("Failed deploying test image: %s" % e)
143
144 @abstractmethod
145 def _deploy(self):
146 pass
147
148 def start(self, params=None):
149 bb.plain("%s - boot test image on target" % self.pn)
150 self._start()
151 # set the ssh object for the target/test image
152 self.connection = sshcontrol.SSHControl(self.ip, logfile=self.sshlog, port=self.port)
153 bb.plain("%s - start running tests" % self.pn)
154
155 @abstractmethod
156 def _start(self):
157 pass
158
159 def stop(self):
160 bb.plain("%s - reboot/powercycle target" % self.pn)
161 self.power_cycle(self.connection)
162
163
164class GummibootTarget(MasterImageHardwareTarget):
165
166 def __init__(self, d):
167 super(GummibootTarget, self).__init__(d)
168 # this the value we need to set in the LoaderEntryOneShot EFI variable
169 # so the system boots the 'test' bootloader label and not the default
170 # The first four bytes are EFI bits, and the rest is an utf-16le string
171 # (EFI vars values need to be utf-16)
172 # $ echo -en "test\0" | iconv -f ascii -t utf-16le | hexdump -C
173 # 00000000 74 00 65 00 73 00 74 00 00 00 |t.e.s.t...|
174 self.efivarvalue = r'\x07\x00\x00\x00\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00'
175 self.deploy_cmds = [
176 'mount -L boot /boot',
177 'mkdir -p /mnt/testrootfs',
178 'mount -L testrootfs /mnt/testrootfs',
179 'modprobe efivarfs',
180 'mount -t efivarfs efivarfs /sys/firmware/efi/efivars',
181 'cp ~/test-kernel /boot',
182 'rm -rf /mnt/testrootfs/*',
183 'tar xvf ~/test-rootfs.%s -C /mnt/testrootfs' % self.image_fstype,
184 'printf "%s" > /sys/firmware/efi/efivars/LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f' % self.efivarvalue
185 ]
186
187 def _deploy(self):
188 # make sure these aren't mounted
189 self.master.run("umount /boot; umount /mnt/testrootfs; umount /sys/firmware/efi/efivars;")
190 # from now on, every deploy cmd should return 0
191 # else an exception will be thrown by sshcontrol
192 self.master.ignore_status = False
193 self.master.copy_to(self.rootfs, "~/test-rootfs." + self.image_fstype)
194 self.master.copy_to(self.kernel, "~/test-kernel")
195 for cmd in self.deploy_cmds:
196 self.master.run(cmd)
197
198 def _start(self, params=None):
199 self.power_cycle(self.master)
200 # there are better ways than a timeout but this should work for now
201 time.sleep(120)
diff --git a/meta/lib/oeqa/controllers/testtargetloader.py b/meta/lib/oeqa/controllers/testtargetloader.py
new file mode 100644
index 0000000000..a1b7b1d92b
--- /dev/null
+++ b/meta/lib/oeqa/controllers/testtargetloader.py
@@ -0,0 +1,70 @@
1import types
2import bb
3import os
4
5# This class is responsible for loading a test target controller
6class TestTargetLoader:
7
8 # Search oeqa.controllers module directory for and return a controller
9 # corresponding to the given target name.
10 # AttributeError raised if not found.
11 # ImportError raised if a provided module can not be imported.
12 def get_controller_module(self, target, bbpath):
13 controllerslist = self.get_controller_modulenames(bbpath)
14 bb.note("Available controller modules: %s" % str(controllerslist))
15 controller = self.load_controller_from_name(target, controllerslist)
16 return controller
17
18 # Return a list of all python modules in lib/oeqa/controllers for each
19 # layer in bbpath
20 def get_controller_modulenames(self, bbpath):
21
22 controllerslist = []
23
24 def add_controller_list(path):
25 if not os.path.exists(os.path.join(path, '__init__.py')):
26 bb.fatal('Controllers directory %s exists but is missing __init__.py' % path)
27 files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')])
28 for f in files:
29 module = 'oeqa.controllers.' + f[:-3]
30 if module not in controllerslist:
31 controllerslist.append(module)
32 else:
33 bb.warn("Duplicate controller module found for %s, only one added. Layers should create unique controller module names" % module)
34
35 for p in bbpath:
36 controllerpath = os.path.join(p, 'lib', 'oeqa', 'controllers')
37 bb.debug(2, 'Searching for target controllers in %s' % controllerpath)
38 if os.path.exists(controllerpath):
39 add_controller_list(controllerpath)
40 return controllerslist
41
42 # Search for and return a controller from given target name and
43 # set of module names.
44 # Raise AttributeError if not found.
45 # Raise ImportError if a provided module can not be imported
46 def load_controller_from_name(self, target, modulenames):
47 for name in modulenames:
48 obj = self.load_controller_from_module(target, name)
49 if obj:
50 return obj
51 raise AttributeError("Unable to load {0} from available modules: {1}".format(target, str(modulenames)))
52
53 # Search for and return a controller or None from given module name
54 def load_controller_from_module(self, target, modulename):
55 obj = None
56 # import module, allowing it to raise import exception
57 module = __import__(modulename, globals(), locals(), [target])
58 # look for target class in the module, catching any exceptions as it
59 # is valid that a module may not have the target class.
60 try:
61 obj = getattr(module, target)
62 if obj:
63 from oeqa.targetcontrol import BaseTarget
64 if (not isinstance(obj, (type, types.ClassType))):
65 bb.warn("Target {0} found, but not of type Class".format(target))
66 if( not issubclass(obj, BaseTarget)):
67 bb.warn("Target {0} found, but subclass is not BaseTarget".format(target))
68 except:
69 obj = None
70 return obj
diff --git a/meta/lib/oeqa/oetest.py b/meta/lib/oeqa/oetest.py
new file mode 100644
index 0000000000..0b7e7dc42d
--- /dev/null
+++ b/meta/lib/oeqa/oetest.py
@@ -0,0 +1,106 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Main unittest module used by testimage.bbclass
6# This provides the oeRuntimeTest base class which is inherited by all tests in meta/lib/oeqa/runtime.
7
8# It also has some helper functions and it's responsible for actually starting the tests
9
10import os, re, mmap
11import unittest
12import inspect
13import subprocess
14from oeqa.utils.decorators import LogResults
15
16def loadTests(tc, type="runtime"):
17 if type == "runtime":
18 # set the context object passed from the test class
19 setattr(oeTest, "tc", tc)
20 # set ps command to use
21 setattr(oeRuntimeTest, "pscmd", "ps -ef" if oeTest.hasPackage("procps") else "ps")
22 # prepare test suite, loader and runner
23 suite = unittest.TestSuite()
24 elif type == "sdk":
25 # set the context object passed from the test class
26 setattr(oeTest, "tc", tc)
27 testloader = unittest.TestLoader()
28 testloader.sortTestMethodsUsing = None
29 suite = testloader.loadTestsFromNames(tc.testslist)
30
31 return suite
32
33def runTests(tc, type="runtime"):
34
35 suite = loadTests(tc, type)
36 print("Test modules %s" % tc.testslist)
37 print("Found %s tests" % suite.countTestCases())
38 runner = unittest.TextTestRunner(verbosity=2)
39 result = runner.run(suite)
40
41 return result
42
43@LogResults
44class oeTest(unittest.TestCase):
45
46 longMessage = True
47
48 @classmethod
49 def hasPackage(self, pkg):
50
51 if re.search(pkg, oeTest.tc.pkgmanifest):
52 return True
53 return False
54
55 @classmethod
56 def hasFeature(self,feature):
57
58 if feature in oeTest.tc.imagefeatures or \
59 feature in oeTest.tc.distrofeatures:
60 return True
61 else:
62 return False
63
64class oeRuntimeTest(oeTest):
65 def __init__(self, methodName='runTest'):
66 self.target = oeRuntimeTest.tc.target
67 super(oeRuntimeTest, self).__init__(methodName)
68
69 #TODO: use package_manager.py to install packages on any type of image
70 def install_packages(self, packagelist):
71 for package in packagelist:
72 (status, result) = self.target.run("smart install -y "+package)
73 if status != 0:
74 return status
75
76class oeSDKTest(oeTest):
77 def __init__(self, methodName='runTest'):
78 self.sdktestdir = oeSDKTest.tc.sdktestdir
79 super(oeSDKTest, self).__init__(methodName)
80
81def getmodule(pos=2):
82 # stack returns a list of tuples containg frame information
83 # First element of the list the is current frame, caller is 1
84 frameinfo = inspect.stack()[pos]
85 modname = inspect.getmodulename(frameinfo[1])
86 #modname = inspect.getmodule(frameinfo[0]).__name__
87 return modname
88
89def skipModule(reason, pos=2):
90 modname = getmodule(pos)
91 if modname not in oeTest.tc.testsrequired:
92 raise unittest.SkipTest("%s: %s" % (modname, reason))
93 else:
94 raise Exception("\nTest %s wants to be skipped.\nReason is: %s" \
95 "\nTest was required in TEST_SUITES, so either the condition for skipping is wrong" \
96 "\nor the image really doesn't have the required feature/package when it should." % (modname, reason))
97
98def skipModuleIf(cond, reason):
99
100 if cond:
101 skipModule(reason, 3)
102
103def skipModuleUnless(cond, reason):
104
105 if not cond:
106 skipModule(reason, 3)
diff --git a/meta/lib/oeqa/runexported.py b/meta/lib/oeqa/runexported.py
new file mode 100755
index 0000000000..e1b6642ec2
--- /dev/null
+++ b/meta/lib/oeqa/runexported.py
@@ -0,0 +1,140 @@
1#!/usr/bin/env python
2
3
4# Copyright (C) 2013 Intel Corporation
5#
6# Released under the MIT license (see COPYING.MIT)
7
8# This script should be used outside of the build system to run image tests.
9# It needs a json file as input as exported by the build.
10# E.g for an already built image:
11#- export the tests:
12# TEST_EXPORT_ONLY = "1"
13# TEST_TARGET = "simpleremote"
14# TEST_TARGET_IP = "192.168.7.2"
15# TEST_SERVER_IP = "192.168.7.1"
16# bitbake core-image-sato -c testimage
17# Setup your target, e.g for qemu: runqemu core-image-sato
18# cd build/tmp/testimage/core-image-sato
19# ./runexported.py testdata.json
20
21import sys
22import os
23import time
24from optparse import OptionParser
25
26try:
27 import simplejson as json
28except ImportError:
29 import json
30
31sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "oeqa")))
32
33from oeqa.oetest import runTests
34from oeqa.utils.sshcontrol import SSHControl
35
36# this isn't pretty but we need a fake target object
37# for running the tests externally as we don't care
38# about deploy/start we only care about the connection methods (run, copy)
39class FakeTarget(object):
40 def __init__(self, d):
41 self.connection = None
42 self.ip = None
43 self.server_ip = None
44 self.datetime = time.strftime('%Y%m%d%H%M%S',time.gmtime())
45 self.testdir = d.getVar("TEST_LOG_DIR", True)
46 self.pn = d.getVar("PN", True)
47
48 def exportStart(self):
49 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
50 sshloglink = os.path.join(self.testdir, "ssh_target_log")
51 if os.path.islink(sshloglink):
52 os.unlink(sshloglink)
53 os.symlink(self.sshlog, sshloglink)
54 print("SSH log file: %s" % self.sshlog)
55 self.connection = SSHControl(self.ip, logfile=self.sshlog)
56
57 def run(self, cmd, timeout=None):
58 return self.connection.run(cmd, timeout)
59
60 def copy_to(self, localpath, remotepath):
61 return self.connection.copy_to(localpath, remotepath)
62
63 def copy_from(self, remotepath, localpath):
64 return self.connection.copy_from(remotepath, localpath)
65
66
67class MyDataDict(dict):
68 def getVar(self, key, unused = None):
69 return self.get(key, "")
70
71class TestContext(object):
72 def __init__(self):
73 self.d = None
74 self.target = None
75
76def main():
77
78 usage = "usage: %prog [options] <json file>"
79 parser = OptionParser(usage=usage)
80 parser.add_option("-t", "--target-ip", dest="ip", help="The IP address of the target machine. Use this to \
81 overwrite the value determined from TEST_TARGET_IP at build time")
82 parser.add_option("-s", "--server-ip", dest="server_ip", help="The IP address of this machine. Use this to \
83 overwrite the value determined from TEST_SERVER_IP at build time.")
84 parser.add_option("-d", "--deploy-dir", dest="deploy_dir", help="Full path to the package feeds, that this \
85 the contents of what used to be DEPLOY_DIR on the build machine. If not specified it will use the value \
86 specified in the json if that directory actually exists or it will error out.")
87 parser.add_option("-l", "--log-dir", dest="log_dir", help="This sets the path for TEST_LOG_DIR. If not specified \
88 the current dir is used. This is used for usually creating a ssh log file and a scp test file.")
89
90 (options, args) = parser.parse_args()
91 if len(args) != 1:
92 parser.error("Incorrect number of arguments. The one and only argument should be a json file exported by the build system")
93
94 with open(args[0], "r") as f:
95 loaded = json.load(f)
96
97 if options.ip:
98 loaded["target"]["ip"] = options.ip
99 if options.server_ip:
100 loaded["target"]["server_ip"] = options.server_ip
101
102 d = MyDataDict()
103 for key in loaded["d"].keys():
104 d[key] = loaded["d"][key]
105
106 if options.log_dir:
107 d["TEST_LOG_DIR"] = options.log_dir
108 else:
109 d["TEST_LOG_DIR"] = os.path.abspath(os.path.dirname(__file__))
110 if options.deploy_dir:
111 d["DEPLOY_DIR"] = options.deploy_dir
112 else:
113 if not os.path.isdir(d["DEPLOY_DIR"]):
114 raise Exception("The path to DEPLOY_DIR does not exists: %s" % d["DEPLOY_DIR"])
115
116
117 target = FakeTarget(d)
118 for key in loaded["target"].keys():
119 setattr(target, key, loaded["target"][key])
120
121 tc = TestContext()
122 setattr(tc, "d", d)
123 setattr(tc, "target", target)
124 for key in loaded.keys():
125 if key != "d" and key != "target":
126 setattr(tc, key, loaded[key])
127
128 target.exportStart()
129 runTests(tc)
130
131 return 0
132
133if __name__ == "__main__":
134 try:
135 ret = main()
136 except Exception:
137 ret = 1
138 import traceback
139 traceback.print_exc(5)
140 sys.exit(ret)
diff --git a/meta/lib/oeqa/runtime/__init__.py b/meta/lib/oeqa/runtime/__init__.py
new file mode 100644
index 0000000000..4cf3fa76b6
--- /dev/null
+++ b/meta/lib/oeqa/runtime/__init__.py
@@ -0,0 +1,3 @@
1# Enable other layers to have tests in the same named directory
2from pkgutil import extend_path
3__path__ = extend_path(__path__, __name__)
diff --git a/meta/lib/oeqa/runtime/_ptest.py b/meta/lib/oeqa/runtime/_ptest.py
new file mode 100644
index 0000000000..4c58dc1d7f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/_ptest.py
@@ -0,0 +1,124 @@
1import unittest, os, shutil
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4from oeqa.utils.logparser import *
5from oeqa.utils.httpserver import HTTPService
6import bb
7import glob
8from oe.package_manager import RpmPkgsList
9import subprocess
10
11def setUpModule():
12 if not oeRuntimeTest.hasFeature("package-management"):
13 skipModule("Image doesn't have package management feature")
14 if not oeRuntimeTest.hasPackage("smart"):
15 skipModule("Image doesn't have smart installed")
16 if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
17 skipModule("Rpm is not the primary package manager")
18
19class PtestRunnerTest(oeRuntimeTest):
20
21 # a ptest log parser
22 def parse_ptest(self, logfile):
23 parser = Lparser(test_0_pass_regex="^PASS:(.+)", test_0_fail_regex="^FAIL:(.+)", section_0_begin_regex="^BEGIN: .*/(.+)/ptest", section_0_end_regex="^END: .*/(.+)/ptest")
24 parser.init()
25 result = Result()
26
27 with open(logfile) as f:
28 for line in f:
29 result_tuple = parser.parse_line(line)
30 if not result_tuple:
31 continue
32 result_tuple = line_type, category, status, name = parser.parse_line(line)
33
34 if line_type == 'section' and status == 'begin':
35 current_section = name
36 continue
37
38 if line_type == 'section' and status == 'end':
39 current_section = None
40 continue
41
42 if line_type == 'test' and status == 'pass':
43 result.store(current_section, name, status)
44 continue
45
46 if line_type == 'test' and status == 'fail':
47 result.store(current_section, name, status)
48 continue
49
50 result.sort_tests()
51 return result
52
53 @classmethod
54 def setUpClass(self):
55 #note the existing channels that are on the board before creating new ones
56# self.existingchannels = set()
57# (status, result) = oeRuntimeTest.tc.target.run('smart channel --show | grep "\["', 0)
58# for x in result.split("\n"):
59# self.existingchannels.add(x)
60 self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
61 self.repo_server.start()
62
63 @classmethod
64 def tearDownClass(self):
65 self.repo_server.stop()
66 #remove created channels to be able to repeat the tests on same image
67# (status, result) = oeRuntimeTest.tc.target.run('smart channel --show | grep "\["', 0)
68# for x in result.split("\n"):
69# if x not in self.existingchannels:
70# oeRuntimeTest.tc.target.run('smart channel --remove '+x[1:-1]+' -y', 0)
71
72 def add_smart_channel(self):
73 image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
74 deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
75 pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True).replace("-","_").split()
76 for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
77 if arch in pkgarchs:
78 self.target.run('smart channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url), 0)
79 self.target.run('smart update', 0)
80
81 def install_complementary(self, globs=None):
82 installed_pkgs_file = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True),
83 "installed_pkgs.txt")
84 self.pkgs_list = RpmPkgsList(oeRuntimeTest.tc.d, oeRuntimeTest.tc.d.getVar('IMAGE_ROOTFS', True), oeRuntimeTest.tc.d.getVar('arch_var', True), oeRuntimeTest.tc.d.getVar('os_var', True))
85 with open(installed_pkgs_file, "w+") as installed_pkgs:
86 installed_pkgs.write(self.pkgs_list.list("arch"))
87
88 cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
89 "glob", oeRuntimeTest.tc.d.getVar('PKGDATA_DIR', True), installed_pkgs_file,
90 globs]
91 try:
92 bb.note("Installing complementary packages ...")
93 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
94 except subprocess.CalledProcessError as e:
95 bb.fatal("Could not compute complementary packages list. Command "
96 "'%s' returned %d:\n%s" %
97 (' '.join(cmd), e.returncode, e.output))
98
99 return complementary_pkgs.split()
100
101 def setUp(self):
102 self.buildhist_dir = oeRuntimeTest.tc.d.getVar("BUILDHISTORY_DIR_IMAGE", True)
103 self.assertTrue(os.path.exists(self.buildhist_dir))
104 self.ptest_log = os.path.join(oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR",True), "ptest-%s.log" % oeRuntimeTest.tc.d.getVar('DATETIME', True))
105
106 @skipUnlessPassed('test_ssh')
107 def test_ptestrunner(self):
108 self.add_smart_channel()
109 cond = oeRuntimeTest.hasPackage("ptest-runner") and oeRuntimeTest.hasFeature("ptest") and oeRuntimeTest.hasPackage("-ptest")
110 if not cond:
111 self.install_packages(self.install_complementary("*-ptest"))
112 self.install_packages(['ptest-runner'])
113
114 self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
115 self.target.copy_from('/tmp/ptest.log', self.ptest_log)
116 shutil.copyfile(self.ptest_log, os.path.join(self.buildhist_dir, "ptest.log"))
117
118 result = self.parse_ptest(os.path.join(self.buildhist_dir, "ptest.log"))
119 log_results_to_location = "./results"
120 if os.path.exists(log_results_to_location):
121 shutil.rmtree(log_results_to_location)
122 os.makedirs(log_results_to_location)
123
124 result.log_as_files(log_results_to_location, test_status = ['fail'])
diff --git a/meta/lib/oeqa/runtime/buildcvs.py b/meta/lib/oeqa/runtime/buildcvs.py
new file mode 100644
index 0000000000..fe6cbfbcd5
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildcvs.py
@@ -0,0 +1,31 @@
1from oeqa.oetest import oeRuntimeTest, skipModule
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import TargetBuildProject
4
5def setUpModule():
6 if not oeRuntimeTest.hasFeature("tools-sdk"):
7 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
8
9class BuildCvsTest(oeRuntimeTest):
10
11 @classmethod
12 def setUpClass(self):
13 self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
14 "http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2")
15 self.project.download_archive()
16
17 @testcase(205)
18 @skipUnlessPassed("test_ssh")
19 def test_cvs(self):
20 self.assertEqual(self.project.run_configure(), 0,
21 msg="Running configure failed")
22
23 self.assertEqual(self.project.run_make(), 0,
24 msg="Running make failed")
25
26 self.assertEqual(self.project.run_install(), 0,
27 msg="Running make install failed")
28
29 @classmethod
30 def tearDownClass(self):
31 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/buildiptables.py b/meta/lib/oeqa/runtime/buildiptables.py
new file mode 100644
index 0000000000..09e252df8c
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildiptables.py
@@ -0,0 +1,31 @@
1from oeqa.oetest import oeRuntimeTest, skipModule
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import TargetBuildProject
4
5def setUpModule():
6 if not oeRuntimeTest.hasFeature("tools-sdk"):
7 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
8
9class BuildIptablesTest(oeRuntimeTest):
10
11 @classmethod
12 def setUpClass(self):
13 self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
14 "http://netfilter.org/projects/iptables/files/iptables-1.4.13.tar.bz2")
15 self.project.download_archive()
16
17 @testcase(206)
18 @skipUnlessPassed("test_ssh")
19 def test_iptables(self):
20 self.assertEqual(self.project.run_configure(), 0,
21 msg="Running configure failed")
22
23 self.assertEqual(self.project.run_make(), 0,
24 msg="Running make failed")
25
26 self.assertEqual(self.project.run_install(), 0,
27 msg="Running make install failed")
28
29 @classmethod
30 def tearDownClass(self):
31 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/buildsudoku.py b/meta/lib/oeqa/runtime/buildsudoku.py
new file mode 100644
index 0000000000..802b060010
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildsudoku.py
@@ -0,0 +1,28 @@
1from oeqa.oetest import oeRuntimeTest, skipModule
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import TargetBuildProject
4
5def setUpModule():
6 if not oeRuntimeTest.hasFeature("tools-sdk"):
7 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
8
9class SudokuTest(oeRuntimeTest):
10
11 @classmethod
12 def setUpClass(self):
13 self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
14 "http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2")
15 self.project.download_archive()
16
17 @testcase(207)
18 @skipUnlessPassed("test_ssh")
19 def test_sudoku(self):
20 self.assertEqual(self.project.run_configure(), 0,
21 msg="Running configure failed")
22
23 self.assertEqual(self.project.run_make(), 0,
24 msg="Running make failed")
25
26 @classmethod
27 def tearDownClass(self):
28 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/connman.py b/meta/lib/oeqa/runtime/connman.py
new file mode 100644
index 0000000000..cc537f7766
--- /dev/null
+++ b/meta/lib/oeqa/runtime/connman.py
@@ -0,0 +1,30 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 if not oeRuntimeTest.hasPackage("connman"):
7 skipModule("No connman package in image")
8
9
10class ConnmanTest(oeRuntimeTest):
11
12 def service_status(self, service):
13 if oeRuntimeTest.hasFeature("systemd"):
14 (status, output) = self.target.run('systemctl status -l %s' % service)
15 return output
16 else:
17 return "Unable to get status or logs for %s" % service
18
19 @skipUnlessPassed('test_ssh')
20 def test_connmand_help(self):
21 (status, output) = self.target.run('/usr/sbin/connmand --help')
22 self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
23
24 @testcase(221)
25 @skipUnlessPassed('test_connmand_help')
26 def test_connmand_running(self):
27 (status, output) = self.target.run(oeRuntimeTest.pscmd + ' | grep [c]onnmand')
28 if status != 0:
29 print self.service_status("connman")
30 self.fail("No connmand process running")
diff --git a/meta/lib/oeqa/runtime/date.py b/meta/lib/oeqa/runtime/date.py
new file mode 100644
index 0000000000..97e8ee42ad
--- /dev/null
+++ b/meta/lib/oeqa/runtime/date.py
@@ -0,0 +1,23 @@
1from oeqa.oetest import oeRuntimeTest
2from oeqa.utils.decorators import *
3import re
4
5class DateTest(oeRuntimeTest):
6
7 @testcase(211)
8 @skipUnlessPassed("test_ssh")
9 def test_date(self):
10 (status, output) = self.target.run('date +"%Y-%m-%d %T"')
11 self.assertEqual(status, 0, msg="Failed to get initial date, output: %s" % output)
12 oldDate = output
13
14 sampleDate = '"2016-08-09 10:00:00"'
15 (status, output) = self.target.run("date -s %s" % sampleDate)
16 self.assertEqual(status, 0, msg="Date set failed, output: %s" % output)
17
18 (status, output) = self.target.run("date -R")
19 p = re.match('Tue, 09 Aug 2016 10:00:.. \+0000', output)
20 self.assertTrue(p, msg="The date was not set correctly, output: %s" % output)
21
22 (status, output) = self.target.run('date -s "%s"' % oldDate)
23 self.assertEqual(status, 0, msg="Failed to reset date, output: %s" % output)
diff --git a/meta/lib/oeqa/runtime/df.py b/meta/lib/oeqa/runtime/df.py
new file mode 100644
index 0000000000..09569d5ff6
--- /dev/null
+++ b/meta/lib/oeqa/runtime/df.py
@@ -0,0 +1,12 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest
3from oeqa.utils.decorators import *
4
5
6class DfTest(oeRuntimeTest):
7
8 @testcase(234)
9 @skipUnlessPassed("test_ssh")
10 def test_df(self):
11 (status,output) = self.target.run("df / | sed -n '2p' | awk '{print $4}'")
12 self.assertTrue(int(output)>5120, msg="Not enough space on image. Current size is %s" % output)
diff --git a/meta/lib/oeqa/runtime/dmesg.py b/meta/lib/oeqa/runtime/dmesg.py
new file mode 100644
index 0000000000..5831471e50
--- /dev/null
+++ b/meta/lib/oeqa/runtime/dmesg.py
@@ -0,0 +1,12 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest
3from oeqa.utils.decorators import *
4
5
6class DmesgTest(oeRuntimeTest):
7
8 @testcase(215)
9 @skipUnlessPassed('test_ssh')
10 def test_dmesg(self):
11 (status, output) = self.target.run('dmesg | grep -v mmci-pl18x | grep -v "error changing net interface name" | grep -iv "dma timeout" | grep -v usbhid | grep -i error')
12 self.assertEqual(status, 1, msg = "Error messages in dmesg log: %s" % output)
diff --git a/meta/lib/oeqa/runtime/files/hellomod.c b/meta/lib/oeqa/runtime/files/hellomod.c
new file mode 100644
index 0000000000..a383397e93
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/hellomod.c
@@ -0,0 +1,19 @@
1#include <linux/module.h>
2#include <linux/kernel.h>
3#include <linux/init.h>
4
5static int __init hello_init(void)
6{
7 printk(KERN_INFO "Hello world!\n");
8 return 0;
9}
10
11static void __exit hello_cleanup(void)
12{
13 printk(KERN_INFO "Cleaning up hellomod.\n");
14}
15
16module_init(hello_init);
17module_exit(hello_cleanup);
18
19MODULE_LICENSE("GPL");
diff --git a/meta/lib/oeqa/runtime/files/hellomod_makefile b/meta/lib/oeqa/runtime/files/hellomod_makefile
new file mode 100644
index 0000000000..b92d5c8fe0
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/hellomod_makefile
@@ -0,0 +1,8 @@
1obj-m := hellomod.o
2KDIR := /usr/src/kernel
3
4all:
5 $(MAKE) -C $(KDIR) M=$(PWD) modules
6
7clean:
8 $(MAKE) -C $(KDIR) M=$(PWD) clean
diff --git a/meta/lib/oeqa/runtime/files/test.c b/meta/lib/oeqa/runtime/files/test.c
new file mode 100644
index 0000000000..2d8389c92e
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/test.c
@@ -0,0 +1,26 @@
1#include <stdio.h>
2#include <math.h>
3#include <stdlib.h>
4
5double convert(long long l)
6{
7 return (double)l;
8}
9
10int main(int argc, char * argv[]) {
11
12 long long l = 10;
13 double f;
14 double check = 10.0;
15
16 f = convert(l);
17 printf("convert: %lld => %f\n", l, f);
18 if ( f != check ) exit(1);
19
20 f = 1234.67;
21 check = 1234.0;
22 printf("floorf(%f) = %f\n", f, floorf(f));
23 if ( floorf(f) != check) exit(1);
24
25 return 0;
26}
diff --git a/meta/lib/oeqa/runtime/files/test.cpp b/meta/lib/oeqa/runtime/files/test.cpp
new file mode 100644
index 0000000000..9e1a76473d
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/test.cpp
@@ -0,0 +1,3 @@
1#include <limits>
2
3int main() {} \ No newline at end of file
diff --git a/meta/lib/oeqa/runtime/files/test.pl b/meta/lib/oeqa/runtime/files/test.pl
new file mode 100644
index 0000000000..689c8f1635
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/test.pl
@@ -0,0 +1,2 @@
1$a = 9.01e+21 - 9.01e+21 + 0.01;
2print ("the value of a is ", $a, "\n");
diff --git a/meta/lib/oeqa/runtime/files/test.py b/meta/lib/oeqa/runtime/files/test.py
new file mode 100644
index 0000000000..f3a2273c52
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/test.py
@@ -0,0 +1,6 @@
1import os
2
3os.system('touch /tmp/testfile.python')
4
5a = 9.01e+21 - 9.01e+21 + 0.01
6print "the value of a is %s" % a
diff --git a/meta/lib/oeqa/runtime/files/testmakefile b/meta/lib/oeqa/runtime/files/testmakefile
new file mode 100644
index 0000000000..ca1844e930
--- /dev/null
+++ b/meta/lib/oeqa/runtime/files/testmakefile
@@ -0,0 +1,5 @@
1test: test.o
2 gcc -o test test.o -lm
3test.o: test.c
4 gcc -c test.c
5
diff --git a/meta/lib/oeqa/runtime/gcc.py b/meta/lib/oeqa/runtime/gcc.py
new file mode 100644
index 0000000000..a7f62e1758
--- /dev/null
+++ b/meta/lib/oeqa/runtime/gcc.py
@@ -0,0 +1,46 @@
1import unittest
2import os
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasFeature("tools-sdk"):
8 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
9
10
11class GccCompileTest(oeRuntimeTest):
12
13 @classmethod
14 def setUpClass(self):
15 oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.c"), "/tmp/test.c")
16 oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "testmakefile"), "/tmp/testmakefile")
17 oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.cpp"), "/tmp/test.cpp")
18
19 @testcase(203)
20 def test_gcc_compile(self):
21 (status, output) = self.target.run('gcc /tmp/test.c -o /tmp/test -lm')
22 self.assertEqual(status, 0, msg="gcc compile failed, output: %s" % output)
23 (status, output) = self.target.run('/tmp/test')
24 self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
25
26 @testcase(200)
27 def test_gpp_compile(self):
28 (status, output) = self.target.run('g++ /tmp/test.c -o /tmp/test -lm')
29 self.assertEqual(status, 0, msg="g++ compile failed, output: %s" % output)
30 (status, output) = self.target.run('/tmp/test')
31 self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
32
33 def test_gpp2_compile(self):
34 (status, output) = self.target.run('g++ /tmp/test.cpp -o /tmp/test -lm')
35 self.assertEqual(status, 0, msg="g++ compile failed, output: %s" % output)
36 (status, output) = self.target.run('/tmp/test')
37 self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
38
39 @testcase(204)
40 def test_make(self):
41 (status, output) = self.target.run('cd /tmp; make -f testmakefile')
42 self.assertEqual(status, 0, msg="running make failed, output %s" % output)
43
44 @classmethod
45 def tearDownClass(self):
46 oeRuntimeTest.tc.target.run("rm /tmp/test.c /tmp/test.o /tmp/test /tmp/testmakefile")
diff --git a/meta/lib/oeqa/runtime/kernelmodule.py b/meta/lib/oeqa/runtime/kernelmodule.py
new file mode 100644
index 0000000000..2e81720327
--- /dev/null
+++ b/meta/lib/oeqa/runtime/kernelmodule.py
@@ -0,0 +1,34 @@
1import unittest
2import os
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasFeature("tools-sdk"):
8 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
9
10
11class KernelModuleTest(oeRuntimeTest):
12
13 def setUp(self):
14 self.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "hellomod.c"), "/tmp/hellomod.c")
15 self.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "hellomod_makefile"), "/tmp/Makefile")
16
17 @testcase('316')
18 @skipUnlessPassed('test_ssh')
19 @skipUnlessPassed('test_gcc_compile')
20 def test_kernel_module(self):
21 cmds = [
22 'cd /usr/src/kernel && make scripts',
23 'cd /tmp && make',
24 'cd /tmp && insmod hellomod.ko',
25 'lsmod | grep hellomod',
26 'dmesg | grep Hello',
27 'rmmod hellomod', 'dmesg | grep "Cleaning up hellomod"'
28 ]
29 for cmd in cmds:
30 (status, output) = self.target.run(cmd, 900)
31 self.assertEqual(status, 0, msg="\n".join([cmd, output]))
32
33 def tearDown(self):
34 self.target.run('rm -f /tmp/Makefile /tmp/hellomod.c')
diff --git a/meta/lib/oeqa/runtime/ldd.py b/meta/lib/oeqa/runtime/ldd.py
new file mode 100644
index 0000000000..bce56c4270
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ldd.py
@@ -0,0 +1,20 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 if not oeRuntimeTest.hasFeature("tools-sdk"):
7 skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
8
9class LddTest(oeRuntimeTest):
10
11 @skipUnlessPassed('test_ssh')
12 def test_ldd_exists(self):
13 (status, output) = self.target.run('which ldd')
14 self.assertEqual(status, 0, msg = "ldd does not exist in PATH: which ldd: %s" % output)
15
16 @testcase(239)
17 @skipUnlessPassed('test_ldd_exists')
18 def test_ldd_rtldlist_check(self):
19 (status, output) = self.target.run('for i in $(which ldd | xargs cat | grep "^RTLDLIST"|cut -d\'=\' -f2|tr -d \'"\'); do test -f $i && echo $i && break; done')
20 self.assertEqual(status, 0, msg = "ldd path not correct or RTLDLIST files don't exist. ")
diff --git a/meta/lib/oeqa/runtime/logrotate.py b/meta/lib/oeqa/runtime/logrotate.py
new file mode 100644
index 0000000000..86d791c300
--- /dev/null
+++ b/meta/lib/oeqa/runtime/logrotate.py
@@ -0,0 +1,28 @@
1# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=289 testcase
2# Note that the image under test must have logrotate installed
3
4import unittest
5from oeqa.oetest import oeRuntimeTest, skipModule
6from oeqa.utils.decorators import *
7
8def setUpModule():
9 if not oeRuntimeTest.hasPackage("logrotate"):
10 skipModule("No logrotate package in image")
11
12
13class LogrotateTest(oeRuntimeTest):
14
15 @skipUnlessPassed("test_ssh")
16 def test_1_logrotate_setup(self):
17 (status, output) = self.target.run('mkdir /home/root/logrotate_dir')
18 self.assertEqual(status, 0, msg = "Could not create logrotate_dir. Output: %s" % output)
19 (status, output) = self.target.run("sed -i 's#wtmp {#wtmp {\\n olddir /home/root/logrotate_dir#' /etc/logrotate.conf")
20 self.assertEqual(status, 0, msg = "Could not write to logrotate.conf file. Status and output: %s and %s)" % (status, output))
21
22 @testcase(289)
23 @skipUnlessPassed("test_1_logrotate_setup")
24 def test_2_logrotate(self):
25 (status, output) = self.target.run('logrotate -f /etc/logrotate.conf')
26 self.assertEqual(status, 0, msg = "logrotate service could not be reloaded. Status and output: %s and %s" % (status, output))
27 output = self.target.run('ls -la /home/root/logrotate_dir/ | wc -l')[1]
28 self.assertTrue(int(output)>=3, msg = "new logfile could not be created. List of files within log directory: %s" %(self.target.run('ls -la /home/root/logrotate_dir')[1]))
diff --git a/meta/lib/oeqa/runtime/multilib.py b/meta/lib/oeqa/runtime/multilib.py
new file mode 100644
index 0000000000..ab0a6ccd69
--- /dev/null
+++ b/meta/lib/oeqa/runtime/multilib.py
@@ -0,0 +1,18 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 multilibs = oeRuntimeTest.tc.d.getVar("MULTILIBS", True) or ""
7 if "multilib:lib32" not in multilibs:
8 skipModule("this isn't a multilib:lib32 image")
9
10
11class MultilibTest(oeRuntimeTest):
12
13 @testcase('279')
14 @skipUnlessPassed('test_ssh')
15 def test_file_connman(self):
16 self.assertTrue(oeRuntimeTest.hasPackage('connman-gnome'), msg="This test assumes connman-gnome is installed")
17 (status, output) = self.target.run("readelf -h /usr/bin/connman-applet | sed -n '3p' | awk '{print $2}'")
18 self.assertEqual(output, "ELF32", msg="connman-applet isn't an ELF32 binary. readelf says: %s" % self.target.run("readelf -h /usr/bin/connman-applet")[1])
diff --git a/meta/lib/oeqa/runtime/pam.py b/meta/lib/oeqa/runtime/pam.py
new file mode 100644
index 0000000000..c8205c9abc
--- /dev/null
+++ b/meta/lib/oeqa/runtime/pam.py
@@ -0,0 +1,25 @@
1# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=287 testcase
2# Note that the image under test must have "pam" in DISTRO_FEATURES
3
4import unittest
5from oeqa.oetest import oeRuntimeTest, skipModule
6from oeqa.utils.decorators import *
7
8def setUpModule():
9 if not oeRuntimeTest.hasFeature("pam"):
10 skipModule("target doesn't have 'pam' in DISTRO_FEATURES")
11
12
13class PamBasicTest(oeRuntimeTest):
14
15 @testcase(287)
16 @skipUnlessPassed('test_ssh')
17 def test_pam(self):
18 (status, output) = self.target.run('login --help')
19 self.assertEqual(status, 1, msg = "login command does not work as expected. Status and output:%s and %s" %(status, output))
20 (status, output) = self.target.run('passwd --help')
21 self.assertEqual(status, 0, msg = "passwd command does not work as expected. Status and output:%s and %s" %(status, output))
22 (status, output) = self.target.run('su --help')
23 self.assertEqual(status, 0, msg = "su command does not work as expected. Status and output:%s and %s" %(status, output))
24 (status, output) = self.target.run('useradd --help')
25 self.assertEqual(status, 0, msg = "useradd command does not work as expected. Status and output:%s and %s" %(status, output))
diff --git a/meta/lib/oeqa/runtime/parselogs.py b/meta/lib/oeqa/runtime/parselogs.py
new file mode 100644
index 0000000000..42cb1b5e6f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/parselogs.py
@@ -0,0 +1,178 @@
1import os
2import unittest
3from oeqa.oetest import oeRuntimeTest
4from oeqa.utils.decorators import *
5
6#in the future these lists could be moved outside of module
7errors = ["error", "cannot", "can\'t", "failed"]
8
9common_errors = [
10 '(WW) warning, (EE) error, (NI) not implemented, (??) unknown.',
11 'dma timeout',
12 'can\'t add hid device:',
13 'usbhid: probe of ',
14 ]
15
16x86_common = [
17 '[drm:psb_do_init] *ERROR* Debug is',
18 'wrong ELF class',
19 'Could not enable PowerButton event',
20 'probe of LNXPWRBN:00 failed with error -22',
21] + common_errors
22
23qemux86_common = [
24 'Fast TSC calibration',
25 '_OSC failed (AE_NOT_FOUND); disabling ASPM',
26 'Open ACPI failed (/var/run/acpid.socket) (No such file or directory)',
27 'Failed to load module "vesa"',
28 'Failed to load module "modesetting"',
29 'Failed to load module "glx"',
30 'wrong ELF class',
31] + common_errors
32
33ignore_errors = {
34 'default' : common_errors,
35 'qemux86' : [
36 'Failed to access perfctr msr (MSR c1 is 0)',
37 "fail to add MMCONFIG information, can't access extended PCI configuration space under this bridge.",
38 ] + qemux86_common,
39 'qemux86-64' : qemux86_common,
40 'qemumips' : [
41 'Failed to load module "glx"',
42 ] + common_errors,
43 'qemuppc' : [
44 'PCI 0000:00 Cannot reserve Legacy IO [io 0x0000-0x0fff]',
45 'mode "640x480" test failed',
46 'Failed to load module "glx"',
47 ] + common_errors,
48 'qemuarm' : [
49 'mmci-pl18x: probe of fpga:05 failed with error -22',
50 'mmci-pl18x: probe of fpga:0b failed with error -22',
51 'Failed to load module "glx"'
52 ] + common_errors,
53 'emenlow' : x86_common,
54 'crownbay' : x86_common,
55 'genericx86' : x86_common,
56 'genericx86-64' : x86_common,
57}
58
59log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"]
60
61class ParseLogsTest(oeRuntimeTest):
62
63 @classmethod
64 def setUpClass(self):
65 self.errors = errors
66 self.ignore_errors = ignore_errors
67 self.log_locations = log_locations
68 self.msg = ""
69
70 def getMachine(self):
71 (status, output) = self.target.run("uname -n")
72 return output
73
74 #get some information on the CPU of the machine to display at the beginning of the output. This info might be useful in some cases.
75 def getHardwareInfo(self):
76 hwi = ""
77 (status, cpu_name) = self.target.run("cat /proc/cpuinfo | grep \"model name\" | head -n1 | awk 'BEGIN{FS=\":\"}{print $2}'")
78 (status, cpu_physical_cores) = self.target.run("cat /proc/cpuinfo | grep \"cpu cores\" | head -n1 | awk {'print $4'}")
79 (status, cpu_logical_cores) = self.target.run("cat /proc/cpuinfo | grep \"processor\" | wc -l")
80 (status, cpu_arch) = self.target.run("uname -m")
81 hwi += "Machine information: \n"
82 hwi += "*******************************\n"
83 hwi += "Machine name: "+self.getMachine()+"\n"
84 hwi += "CPU: "+str(cpu_name)+"\n"
85 hwi += "Arch: "+str(cpu_arch)+"\n"
86 hwi += "Physical cores: "+str(cpu_physical_cores)+"\n"
87 hwi += "Logical cores: "+str(cpu_logical_cores)+"\n"
88 hwi += "*******************************\n"
89 return hwi
90
91 #go through the log locations provided and if it's a folder create a list with all the .log files in it, if it's a file just add
92 #it to that list
93 def getLogList(self, log_locations):
94 logs = []
95 for location in log_locations:
96 (status, output) = self.target.run("test -f "+str(location))
97 if (status == 0):
98 logs.append(str(location))
99 else:
100 (status, output) = self.target.run("test -d "+str(location))
101 if (status == 0):
102 (status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f")
103 output = output.splitlines()
104 for logfile in output:
105 logs.append(os.path.join(location,str(logfile)))
106 return logs
107
108 #build the grep command to be used with filters and exclusions
109 def build_grepcmd(self, errors, ignore_errors, log):
110 grepcmd = "grep "
111 grepcmd +="-Ei \""
112 for error in errors:
113 grepcmd += error+"|"
114 grepcmd = grepcmd[:-1]
115 grepcmd += "\" "+str(log)+" | grep -Eiv \'"
116 try:
117 errorlist = ignore_errors[self.getMachine()]
118 except KeyError:
119 self.msg += "No ignore list found for this machine, using default\n"
120 errorlist = ignore_errors['default']
121 for ignore_error in errorlist:
122 ignore_error = ignore_error.replace("(", "\(")
123 ignore_error = ignore_error.replace(")", "\)")
124 ignore_error = ignore_error.replace("'", ".")
125 ignore_error = ignore_error.replace("?", "\?")
126 ignore_error = ignore_error.replace("[", "\[")
127 ignore_error = ignore_error.replace("]", "\]")
128 ignore_error = ignore_error.replace("*", "\*")
129 grepcmd += ignore_error+"|"
130 grepcmd = grepcmd[:-1]
131 grepcmd += "\'"
132 return grepcmd
133
134 #grep only the errors so that their context could be collected. Default context is 10 lines before and after the error itself
135 def parse_logs(self, errors, ignore_errors, logs, lines_before = 10, lines_after = 10):
136 results = {}
137 rez = []
138 for log in logs:
139 thegrep = self.build_grepcmd(errors, ignore_errors, log)
140 try:
141 (status, result) = self.target.run(thegrep)
142 except:
143 pass
144 if result:
145 results[log] = {}
146 rez = result.splitlines()
147 for xrez in rez:
148 command = "grep \"\\"+str(xrez)+"\" -B "+str(lines_before)+" -A "+str(lines_after)+" "+str(log)
149 try:
150 (status, yrez) = self.target.run(command)
151 except:
152 pass
153 results[log][xrez]=yrez
154 return results
155
156 #get the output of dmesg and write it in a file. This file is added to log_locations.
157 def write_dmesg(self):
158 (status, dmesg) = self.target.run("dmesg")
159 (status, dmesg2) = self.target.run("echo \""+str(dmesg)+"\" > /tmp/dmesg_output.log")
160
161 @skipUnlessPassed('test_ssh')
162 def test_parselogs(self):
163 self.write_dmesg()
164 log_list = self.getLogList(self.log_locations)
165 result = self.parse_logs(self.errors, self.ignore_errors, log_list)
166 print self.getHardwareInfo()
167 errcount = 0
168 for log in result:
169 self.msg += "Log: "+log+"\n"
170 self.msg += "-----------------------\n"
171 for error in result[log]:
172 errcount += 1
173 self.msg += "Central error: "+str(error)+"\n"
174 self.msg += "***********************\n"
175 self.msg += result[str(log)][str(error)]+"\n"
176 self.msg += "***********************\n"
177 self.msg += "%s errors found in logs." % errcount
178 self.assertEqual(errcount, 0, msg=self.msg)
diff --git a/meta/lib/oeqa/runtime/perl.py b/meta/lib/oeqa/runtime/perl.py
new file mode 100644
index 0000000000..65da028d4b
--- /dev/null
+++ b/meta/lib/oeqa/runtime/perl.py
@@ -0,0 +1,29 @@
1import unittest
2import os
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasPackage("perl"):
8 skipModule("No perl package in the image")
9
10
11class PerlTest(oeRuntimeTest):
12
13 @classmethod
14 def setUpClass(self):
15 oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.pl"), "/tmp/test.pl")
16
17 def test_perl_exists(self):
18 (status, output) = self.target.run('which perl')
19 self.assertEqual(status, 0, msg="Perl binary not in PATH or not on target.")
20
21 @testcase(208)
22 def test_perl_works(self):
23 (status, output) = self.target.run('perl /tmp/test.pl')
24 self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
25 self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
26
27 @classmethod
28 def tearDownClass(self):
29 oeRuntimeTest.tc.target.run("rm /tmp/test.pl")
diff --git a/meta/lib/oeqa/runtime/ping.py b/meta/lib/oeqa/runtime/ping.py
new file mode 100644
index 0000000000..a73c72402a
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ping.py
@@ -0,0 +1,20 @@
1import subprocess
2import unittest
3import sys
4import time
5from oeqa.oetest import oeRuntimeTest
6
7class PingTest(oeRuntimeTest):
8
9 def test_ping(self):
10 output = ''
11 count = 0
12 endtime = time.time() + 60
13 while count < 5 and time.time() < endtime:
14 proc = subprocess.Popen("ping -c 1 %s" % self.target.ip, shell=True, stdout=subprocess.PIPE)
15 output += proc.communicate()[0]
16 if proc.poll() == 0:
17 count += 1
18 else:
19 count = 0
20 self.assertEqual(count, 5, msg = "Expected 5 consecutive replies, got %d.\nping output is:\n%s" % (count,output))
diff --git a/meta/lib/oeqa/runtime/python.py b/meta/lib/oeqa/runtime/python.py
new file mode 100644
index 0000000000..0387b9a03e
--- /dev/null
+++ b/meta/lib/oeqa/runtime/python.py
@@ -0,0 +1,34 @@
1import unittest
2import os
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasPackage("python"):
8 skipModule("No python package in the image")
9
10
11class PythonTest(oeRuntimeTest):
12
13 @classmethod
14 def setUpClass(self):
15 oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.py"), "/tmp/test.py")
16
17 def test_python_exists(self):
18 (status, output) = self.target.run('which python')
19 self.assertEqual(status, 0, msg="Python binary not in PATH or not on target.")
20
21 @testcase(965)
22 def test_python_stdout(self):
23 (status, output) = self.target.run('python /tmp/test.py')
24 self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
25 self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
26
27 def test_python_testfile(self):
28 (status, output) = self.target.run('ls /tmp/testfile.python')
29 self.assertEqual(status, 0, msg="Python test file generate failed.")
30
31
32 @classmethod
33 def tearDownClass(self):
34 oeRuntimeTest.tc.target.run("rm /tmp/test.py /tmp/testfile.python")
diff --git a/meta/lib/oeqa/runtime/rpm.py b/meta/lib/oeqa/runtime/rpm.py
new file mode 100644
index 0000000000..b17e8b46a8
--- /dev/null
+++ b/meta/lib/oeqa/runtime/rpm.py
@@ -0,0 +1,53 @@
1import unittest
2import os
3import fnmatch
4from oeqa.oetest import oeRuntimeTest, skipModule
5from oeqa.utils.decorators import *
6
7def setUpModule():
8 if not oeRuntimeTest.hasFeature("package-management"):
9 skipModule("rpm module skipped: target doesn't have package-management in IMAGE_FEATURES")
10 if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
11 skipModule("rpm module skipped: target doesn't have rpm as primary package manager")
12
13
14class RpmBasicTest(oeRuntimeTest):
15
16 @skipUnlessPassed('test_ssh')
17 def test_rpm_help(self):
18 (status, output) = self.target.run('rpm --help')
19 self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
20
21 @testcase(191)
22 @skipUnlessPassed('test_rpm_help')
23 def test_rpm_query(self):
24 (status, output) = self.target.run('rpm -q rpm')
25 self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
26
27class RpmInstallRemoveTest(oeRuntimeTest):
28
29 @classmethod
30 def setUpClass(self):
31 pkgarch = oeRuntimeTest.tc.d.getVar('TUNE_PKGARCH', True).replace("-", "_")
32 rpmdir = os.path.join(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), "rpm", pkgarch)
33 # pick rpm-doc as a test file to get installed, because it's small and it will always be built for standard targets
34 for f in fnmatch.filter(os.listdir(rpmdir), "rpm-doc-*.%s.rpm" % pkgarch):
35 testrpmfile = f
36 oeRuntimeTest.tc.target.copy_to(os.path.join(rpmdir,testrpmfile), "/tmp/rpm-doc.rpm")
37
38 @testcase(192)
39 @skipUnlessPassed('test_rpm_help')
40 def test_rpm_install(self):
41 (status, output) = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
42 self.assertEqual(status, 0, msg="Failed to install rpm-doc package: %s" % output)
43
44 @testcase(194)
45 @skipUnlessPassed('test_rpm_install')
46 def test_rpm_remove(self):
47 (status,output) = self.target.run('rpm -e rpm-doc')
48 self.assertEqual(status, 0, msg="Failed to remove rpm-doc package: %s" % output)
49
50 @classmethod
51 def tearDownClass(self):
52 oeRuntimeTest.tc.target.run('rm -f /tmp/rpm-doc.rpm')
53
diff --git a/meta/lib/oeqa/runtime/scanelf.py b/meta/lib/oeqa/runtime/scanelf.py
new file mode 100644
index 0000000000..43a024ab9a
--- /dev/null
+++ b/meta/lib/oeqa/runtime/scanelf.py
@@ -0,0 +1,28 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 if not oeRuntimeTest.hasPackage("pax-utils"):
7 skipModule("pax-utils package not installed")
8
9class ScanelfTest(oeRuntimeTest):
10
11 def setUp(self):
12 self.scancmd = 'scanelf --quiet --recursive --mount --ldpath --path'
13
14 @testcase(966)
15 @skipUnlessPassed('test_ssh')
16 def test_scanelf_textrel(self):
17 # print TEXTREL information
18 self.scancmd += " --textrel"
19 (status, output) = self.target.run(self.scancmd)
20 self.assertEqual(output.strip(), "", "\n".join([self.scancmd, output]))
21
22 @testcase(967)
23 @skipUnlessPassed('test_ssh')
24 def test_scanelf_rpath(self):
25 # print RPATH information
26 self.scancmd += " --rpath"
27 (status, output) = self.target.run(self.scancmd)
28 self.assertEqual(output.strip(), "", "\n".join([self.scancmd, output]))
diff --git a/meta/lib/oeqa/runtime/scp.py b/meta/lib/oeqa/runtime/scp.py
new file mode 100644
index 0000000000..48e87d2d0b
--- /dev/null
+++ b/meta/lib/oeqa/runtime/scp.py
@@ -0,0 +1,22 @@
1import os
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import skipUnlessPassed, testcase
4
5def setUpModule():
6 if not (oeRuntimeTest.hasPackage("dropbear") or oeRuntimeTest.hasPackage("openssh-sshd")):
7 skipModule("No ssh package in image")
8
9class ScpTest(oeRuntimeTest):
10
11 @testcase(220)
12 @skipUnlessPassed('test_ssh')
13 def test_scp_file(self):
14 test_log_dir = oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR", True)
15 test_file_path = os.path.join(test_log_dir, 'test_scp_file')
16 with open(test_file_path, 'w') as test_scp_file:
17 test_scp_file.seek(2 ** 22 - 1)
18 test_scp_file.write(os.linesep)
19 (status, output) = self.target.copy_to(test_file_path, '/tmp/test_scp_file')
20 self.assertEqual(status, 0, msg = "File could not be copied. Output: %s" % output)
21 (status, output) = self.target.run("ls -la /tmp/test_scp_file")
22 self.assertEqual(status, 0, msg = "SCP test failed")
diff --git a/meta/lib/oeqa/runtime/skeletoninit.py b/meta/lib/oeqa/runtime/skeletoninit.py
new file mode 100644
index 0000000000..7c7f402e5d
--- /dev/null
+++ b/meta/lib/oeqa/runtime/skeletoninit.py
@@ -0,0 +1,29 @@
1# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=284 testcase
2# Note that the image under test must have meta-skeleton layer in bblayers and IMAGE_INSTALL_append = " service" in local.conf
3
4import unittest
5from oeqa.oetest import oeRuntimeTest, skipModule
6from oeqa.utils.decorators import *
7
8def setUpModule():
9 if not oeRuntimeTest.hasPackage("service"):
10 skipModule("No service package in image")
11
12
13class SkeletonBasicTest(oeRuntimeTest):
14
15 @skipUnlessPassed('test_ssh')
16 @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"), "Not appropiate for systemd image")
17 def test_skeleton_availability(self):
18 (status, output) = self.target.run('ls /etc/init.d/skeleton')
19 self.assertEqual(status, 0, msg = "skeleton init script not found. Output:\n%s " % output)
20 (status, output) = self.target.run('ls /usr/sbin/skeleton-test')
21 self.assertEqual(status, 0, msg = "skeleton-test not found. Output:\n%s" % output)
22
23 @testcase(284)
24 @skipUnlessPassed('test_skeleton_availability')
25 @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"), "Not appropiate for systemd image")
26 def test_skeleton_script(self):
27 output1 = self.target.run("/etc/init.d/skeleton start")[1]
28 (status, output2) = self.target.run(oeRuntimeTest.pscmd + ' | grep [s]keleton-test')
29 self.assertEqual(status, 0, msg = "Skeleton script could not be started:\n%s\n%s" % (output1, output2))
diff --git a/meta/lib/oeqa/runtime/smart.py b/meta/lib/oeqa/runtime/smart.py
new file mode 100644
index 0000000000..3b49314df7
--- /dev/null
+++ b/meta/lib/oeqa/runtime/smart.py
@@ -0,0 +1,121 @@
1import unittest
2import re
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5from oeqa.utils.httpserver import HTTPService
6
7def setUpModule():
8 if not oeRuntimeTest.hasFeature("package-management"):
9 skipModule("Image doesn't have package management feature")
10 if not oeRuntimeTest.hasPackage("smart"):
11 skipModule("Image doesn't have smart installed")
12 if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
13 skipModule("Rpm is not the primary package manager")
14
15class SmartTest(oeRuntimeTest):
16
17 @skipUnlessPassed('test_smart_help')
18 def smart(self, command, expected = 0):
19 command = 'smart %s' % command
20 status, output = self.target.run(command, 1500)
21 message = os.linesep.join([command, output])
22 self.assertEqual(status, expected, message)
23 self.assertFalse("Cannot allocate memory" in output, message)
24 return output
25
26class SmartBasicTest(SmartTest):
27
28 @testcase(716)
29 @skipUnlessPassed('test_ssh')
30 def test_smart_help(self):
31 self.smart('--help')
32
33 def test_smart_version(self):
34 self.smart('--version')
35
36 @testcase(721)
37 def test_smart_info(self):
38 self.smart('info python-smartpm')
39
40 @testcase(421)
41 def test_smart_query(self):
42 self.smart('query python-smartpm')
43
44 @testcase(720)
45 def test_smart_search(self):
46 self.smart('search python-smartpm')
47
48 @testcase(722)
49 def test_smart_stats(self):
50 self.smart('stats')
51
52class SmartRepoTest(SmartTest):
53
54 @classmethod
55 def setUpClass(self):
56 self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
57 self.repo_server.start()
58
59 @classmethod
60 def tearDownClass(self):
61 self.repo_server.stop()
62
63 def test_smart_channel(self):
64 self.smart('channel', 1)
65
66 @testcase(719)
67 def test_smart_channel_add(self):
68 image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
69 deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
70 pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True).replace("-","_").split()
71 for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
72 if arch in pkgarchs:
73 self.smart('channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url))
74 self.smart('update')
75
76 def test_smart_channel_help(self):
77 self.smart('channel --help')
78
79 def test_smart_channel_list(self):
80 self.smart('channel --list')
81
82 def test_smart_channel_show(self):
83 self.smart('channel --show')
84
85 @testcase(717)
86 def test_smart_channel_rpmsys(self):
87 self.smart('channel --show rpmsys')
88 self.smart('channel --disable rpmsys')
89 self.smart('channel --enable rpmsys')
90
91 @skipUnlessPassed('test_smart_channel_add')
92 def test_smart_install(self):
93 self.smart('remove -y psplash-default')
94 self.smart('install -y psplash-default')
95
96 @testcase(728)
97 @skipUnlessPassed('test_smart_install')
98 def test_smart_install_dependency(self):
99 self.smart('remove -y psplash')
100 self.smart('install -y psplash-default')
101
102 @testcase(723)
103 @skipUnlessPassed('test_smart_channel_add')
104 def test_smart_install_from_disk(self):
105 self.smart('remove -y psplash-default')
106 self.smart('download psplash-default')
107 self.smart('install -y ./psplash-default*')
108
109 @testcase(725)
110 @skipUnlessPassed('test_smart_channel_add')
111 def test_smart_install_from_http(self):
112 output = self.smart('download --urls psplash-default')
113 url = re.search('(http://.*/psplash-default.*\.rpm)', output)
114 self.assertTrue(url, msg="Couln't find download url in %s" % output)
115 self.smart('remove -y psplash-default')
116 self.smart('install -y %s' % url.group(0))
117
118 @testcase(729)
119 @skipUnlessPassed('test_smart_install')
120 def test_smart_reinstall(self):
121 self.smart('reinstall -y psplash-default')
diff --git a/meta/lib/oeqa/runtime/ssh.py b/meta/lib/oeqa/runtime/ssh.py
new file mode 100644
index 0000000000..0e76d5d512
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ssh.py
@@ -0,0 +1,19 @@
1import subprocess
2import unittest
3import sys
4from oeqa.oetest import oeRuntimeTest, skipModule
5from oeqa.utils.decorators import *
6
7def setUpModule():
8 if not (oeRuntimeTest.hasPackage("dropbear") or oeRuntimeTest.hasPackage("openssh")):
9 skipModule("No ssh package in image")
10
11class SshTest(oeRuntimeTest):
12
13 @testcase(224)
14 @skipUnlessPassed('test_ping')
15 def test_ssh(self):
16 (status, output) = self.target.run('uname -a')
17 self.assertEqual(status, 0, msg="SSH Test failed: %s" % output)
18 (status, output) = self.target.run('cat /etc/masterimage')
19 self.assertEqual(status, 1, msg="This isn't the right image - /etc/masterimage shouldn't be here %s" % output)
diff --git a/meta/lib/oeqa/runtime/syslog.py b/meta/lib/oeqa/runtime/syslog.py
new file mode 100644
index 0000000000..7fa018e97f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/syslog.py
@@ -0,0 +1,48 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 if not oeRuntimeTest.hasPackage("syslog"):
7 skipModule("No syslog package in image")
8
9class SyslogTest(oeRuntimeTest):
10
11 @skipUnlessPassed("test_ssh")
12 def test_syslog_help(self):
13 (status,output) = self.target.run('/sbin/syslogd --help')
14 self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
15
16 @testcase(201)
17 @skipUnlessPassed("test_syslog_help")
18 def test_syslog_running(self):
19 (status,output) = self.target.run(oeRuntimeTest.pscmd + ' | grep -i [s]yslogd')
20 self.assertEqual(status, 0, msg="no syslogd process, ps output: %s" % self.target.run(oeRuntimeTest.pscmd)[1])
21
22
23class SyslogTestConfig(oeRuntimeTest):
24
25 @skipUnlessPassed("test_syslog_running")
26 def test_syslog_logger(self):
27 (status,output) = self.target.run('logger foobar && test -e /var/log/messages && grep foobar /var/log/messages || logread | grep foobar')
28 self.assertEqual(status, 0, msg="Test log string not found in /var/log/messages. Output: %s " % output)
29
30 @skipUnlessPassed("test_syslog_running")
31 def test_syslog_restart(self):
32 if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"):
33 (status,output) = self.target.run('/etc/init.d/syslog restart')
34 else:
35 (status,output) = self.target.run('systemctl restart syslog.service')
36
37 @testcase(202)
38 @skipUnlessPassed("test_syslog_restart")
39 @skipUnlessPassed("test_syslog_logger")
40 @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"), "Not appropiate for systemd image")
41 def test_syslog_startup_config(self):
42 self.target.run('echo "LOGFILE=/var/log/test" >> /etc/syslog-startup.conf')
43 (status,output) = self.target.run('/etc/init.d/syslog restart')
44 self.assertEqual(status, 0, msg="Could not restart syslog service. Status and output: %s and %s" % (status,output))
45 (status,output) = self.target.run('logger foobar && grep foobar /var/log/test')
46 self.assertEqual(status, 0, msg="Test log string not found. Output: %s " % output)
47 self.target.run("sed -i 's#LOGFILE=/var/log/test##' /etc/syslog-startup.conf")
48 self.target.run('/etc/init.d/syslog restart')
diff --git a/meta/lib/oeqa/runtime/systemd.py b/meta/lib/oeqa/runtime/systemd.py
new file mode 100644
index 0000000000..1451698bb3
--- /dev/null
+++ b/meta/lib/oeqa/runtime/systemd.py
@@ -0,0 +1,88 @@
1import unittest
2import re
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasFeature("systemd"):
8 skipModule("target doesn't have systemd in DISTRO_FEATURES")
9 if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", True):
10 skipModule("systemd is not the init manager for this image")
11
12
13class SystemdTest(oeRuntimeTest):
14
15 def systemctl(self, action = '', target = '', expected = 0, verbose = False):
16 command = 'systemctl %s %s' % (action, target)
17 status, output = self.target.run(command)
18 message = '\n'.join([command, output])
19 if status != expected and verbose:
20 message += self.target.run('systemctl status --full %s' % target)[1]
21 self.assertEqual(status, expected, message)
22 return output
23
24
25class SystemdBasicTests(SystemdTest):
26
27 @skipUnlessPassed('test_ssh')
28 def test_systemd_basic(self):
29 self.systemctl('--version')
30
31 @testcase(551)
32 @skipUnlessPassed('test_system_basic')
33 def test_systemd_list(self):
34 self.systemctl('list-unit-files')
35
36 def settle(self):
37 """
38 Block until systemd has finished activating any units being activated,
39 or until two minutes has elapsed.
40
41 Returns a tuple, either (True, '') if all units have finished
42 activating, or (False, message string) if there are still units
43 activating (generally, failing units that restart).
44 """
45 import time
46 endtime = time.time() + (60 * 2)
47 while True:
48 status, output = self.target.run('systemctl --state=activating')
49 if "0 loaded units listed" in output:
50 return (True, '')
51 if time.time() >= endtime:
52 return (False, output)
53 time.sleep(10)
54
55 @testcase(550)
56 @skipUnlessPassed('test_systemd_basic')
57 def test_systemd_failed(self):
58 settled, output = self.settle()
59 self.assertTrue(settled, msg="Timed out waiting for systemd to settle:\n" + output)
60
61 output = self.systemctl('list-units', '--failed')
62 match = re.search("0 loaded units listed", output)
63 if not match:
64 output += self.systemctl('status --full --failed')
65 self.assertTrue(match, msg="Some systemd units failed:\n%s" % output)
66
67
68class SystemdServiceTests(SystemdTest):
69
70 @skipUnlessPassed('test_systemd_basic')
71 def test_systemd_status(self):
72 self.systemctl('status --full', 'avahi-daemon.service')
73
74 @testcase(695)
75 @skipUnlessPassed('test_systemd_status')
76 def test_systemd_stop_start(self):
77 self.systemctl('stop', 'avahi-daemon.service')
78 self.systemctl('is-active', 'avahi-daemon.service', expected=3, verbose=True)
79 self.systemctl('start','avahi-daemon.service')
80 self.systemctl('is-active', 'avahi-daemon.service', verbose=True)
81
82 @testcase(696)
83 @skipUnlessPassed('test_systemd_basic')
84 def test_systemd_disable_enable(self):
85 self.systemctl('disable', 'avahi-daemon.service')
86 self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
87 self.systemctl('enable', 'avahi-daemon.service')
88 self.systemctl('is-enabled', 'avahi-daemon.service')
diff --git a/meta/lib/oeqa/runtime/vnc.py b/meta/lib/oeqa/runtime/vnc.py
new file mode 100644
index 0000000000..f31deff306
--- /dev/null
+++ b/meta/lib/oeqa/runtime/vnc.py
@@ -0,0 +1,20 @@
1from oeqa.oetest import oeRuntimeTest, skipModuleUnless
2from oeqa.utils.decorators import *
3import re
4
5def setUpModule():
6 skipModuleUnless(oeRuntimeTest.hasPackage('x11vnc'), "No x11vnc package in image")
7
8class VNCTest(oeRuntimeTest):
9
10 @testcase(213)
11 @skipUnlessPassed('test_ssh')
12 def test_vnc(self):
13 (status, output) = self.target.run('x11vnc -display :0 -bg -o x11vnc.log')
14 self.assertEqual(status, 0, msg="x11vnc server failed to start: %s" % output)
15 port = re.search('PORT=[0-9]*', output)
16 self.assertTrue(port, msg="Listening port not specified in command output: %s" %output)
17
18 vncport = port.group(0).split('=')[1]
19 (status, output) = self.target.run('netstat -ntl | grep ":%s"' % vncport)
20 self.assertEqual(status, 0, msg="x11vnc server not running on port %s\n\n%s" % (vncport, self.target.run('netstat -ntl; cat x11vnc.log')[1]))
diff --git a/meta/lib/oeqa/runtime/x32lib.py b/meta/lib/oeqa/runtime/x32lib.py
new file mode 100644
index 0000000000..ce5e214035
--- /dev/null
+++ b/meta/lib/oeqa/runtime/x32lib.py
@@ -0,0 +1,18 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 #check if DEFAULTTUNE is set and it's value is: x86-64-x32
7 defaulttune = oeRuntimeTest.tc.d.getVar("DEFAULTTUNE", True)
8 if "x86-64-x32" not in defaulttune:
9 skipModule("DEFAULTTUNE is not set to x86-64-x32")
10
11class X32libTest(oeRuntimeTest):
12
13 @testcase(281)
14 @skipUnlessPassed("test_ssh")
15 def test_x32_file(self):
16 status1 = self.target.run("readelf -h /bin/ls | grep Class | grep ELF32")[0]
17 status2 = self.target.run("readelf -h /bin/ls | grep Machine | grep X86-64")[0]
18 self.assertTrue(status1 == 0 and status2 == 0, msg="/bin/ls isn't an X86-64 ELF32 binary. readelf says: %s" % self.target.run("readelf -h /bin/ls")[1])
diff --git a/meta/lib/oeqa/runtime/xorg.py b/meta/lib/oeqa/runtime/xorg.py
new file mode 100644
index 0000000000..a07031e5c8
--- /dev/null
+++ b/meta/lib/oeqa/runtime/xorg.py
@@ -0,0 +1,17 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import *
4
5def setUpModule():
6 if not oeRuntimeTest.hasFeature("x11-base"):
7 skipModule("target doesn't have x11 in IMAGE_FEATURES")
8
9
10class XorgTest(oeRuntimeTest):
11
12 @skipUnlessPassed('test_ssh')
13 def test_xorg_running(self):
14 (status, output) = self.target.run(oeRuntimeTest.pscmd + ' | grep -v xinit | grep [X]org')
15 self.assertEqual(status, 0, msg="Xorg does not appear to be running %s" % self.target.run(oeRuntimeTest.pscmd)[1])
16
17
diff --git a/meta/lib/oeqa/sdk/__init__.py b/meta/lib/oeqa/sdk/__init__.py
new file mode 100644
index 0000000000..4cf3fa76b6
--- /dev/null
+++ b/meta/lib/oeqa/sdk/__init__.py
@@ -0,0 +1,3 @@
1# Enable other layers to have tests in the same named directory
2from pkgutil import extend_path
3__path__ = extend_path(__path__, __name__)
diff --git a/meta/lib/oeqa/sdk/buildcvs.py b/meta/lib/oeqa/sdk/buildcvs.py
new file mode 100644
index 0000000000..c7146fa4af
--- /dev/null
+++ b/meta/lib/oeqa/sdk/buildcvs.py
@@ -0,0 +1,25 @@
1from oeqa.oetest import oeSDKTest, skipModule
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import SDKBuildProject
4
5class BuildCvsTest(oeSDKTest):
6
7 @classmethod
8 def setUpClass(self):
9 self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/cvs/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
10 "http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2")
11 self.project.download_archive()
12
13 def test_cvs(self):
14 self.assertEqual(self.project.run_configure(), 0,
15 msg="Running configure failed")
16
17 self.assertEqual(self.project.run_make(), 0,
18 msg="Running make failed")
19
20 self.assertEqual(self.project.run_install(), 0,
21 msg="Running make install failed")
22
23 @classmethod
24 def tearDownClass(self):
25 self.project.clean()
diff --git a/meta/lib/oeqa/sdk/buildiptables.py b/meta/lib/oeqa/sdk/buildiptables.py
new file mode 100644
index 0000000000..062e5316e7
--- /dev/null
+++ b/meta/lib/oeqa/sdk/buildiptables.py
@@ -0,0 +1,26 @@
1from oeqa.oetest import oeSDKTest
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import SDKBuildProject
4
5
6class BuildIptablesTest(oeSDKTest):
7
8 @classmethod
9 def setUpClass(self):
10 self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/iptables/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
11 "http://netfilter.org/projects/iptables/files/iptables-1.4.13.tar.bz2")
12 self.project.download_archive()
13
14 def test_iptables(self):
15 self.assertEqual(self.project.run_configure(), 0,
16 msg="Running configure failed")
17
18 self.assertEqual(self.project.run_make(), 0,
19 msg="Running make failed")
20
21 self.assertEqual(self.project.run_install(), 0,
22 msg="Running make install failed")
23
24 @classmethod
25 def tearDownClass(self):
26 self.project.clean()
diff --git a/meta/lib/oeqa/sdk/buildsudoku.py b/meta/lib/oeqa/sdk/buildsudoku.py
new file mode 100644
index 0000000000..dea77c6599
--- /dev/null
+++ b/meta/lib/oeqa/sdk/buildsudoku.py
@@ -0,0 +1,26 @@
1from oeqa.oetest import oeSDKTest, skipModule
2from oeqa.utils.decorators import *
3from oeqa.utils.targetbuild import SDKBuildProject
4
5def setUpModule():
6 if not oeSDKTest.hasPackage("gtk\+"):
7 skipModule("Image doesn't have gtk+ in manifest")
8
9class SudokuTest(oeSDKTest):
10
11 @classmethod
12 def setUpClass(self):
13 self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/sudoku/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
14 "http://downloads.sourceforge.net/project/sudoku-savant/sudoku-savant/sudoku-savant-1.3/sudoku-savant-1.3.tar.bz2")
15 self.project.download_archive()
16
17 def test_sudoku(self):
18 self.assertEqual(self.project.run_configure(), 0,
19 msg="Running configure failed")
20
21 self.assertEqual(self.project.run_make(), 0,
22 msg="Running make failed")
23
24 @classmethod
25 def tearDownClass(self):
26 self.project.clean()
diff --git a/meta/lib/oeqa/selftest/__init__.py b/meta/lib/oeqa/selftest/__init__.py
new file mode 100644
index 0000000000..3ad9513f40
--- /dev/null
+++ b/meta/lib/oeqa/selftest/__init__.py
@@ -0,0 +1,2 @@
1from pkgutil import extend_path
2__path__ = extend_path(__path__, __name__)
diff --git a/meta/lib/oeqa/selftest/_sstatetests_noauto.py b/meta/lib/oeqa/selftest/_sstatetests_noauto.py
new file mode 100644
index 0000000000..fc9ae7efb9
--- /dev/null
+++ b/meta/lib/oeqa/selftest/_sstatetests_noauto.py
@@ -0,0 +1,95 @@
1import datetime
2import unittest
3import os
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
10from oeqa.selftest.sstate import SStateBase
11
12
13class RebuildFromSState(SStateBase):
14
15 @classmethod
16 def setUpClass(self):
17 self.builddir = os.path.join(os.environ.get('BUILDDIR'))
18
19 def get_dep_targets(self, primary_targets):
20 found_targets = []
21 bitbake("-g " + ' '.join(map(str, primary_targets)))
22 with open(os.path.join(self.builddir, 'pn-buildlist'), 'r') as pnfile:
23 found_targets = pnfile.read().splitlines()
24 return found_targets
25
26 def configure_builddir(self, builddir):
27 os.mkdir(builddir)
28 self.track_for_cleanup(builddir)
29 os.mkdir(os.path.join(builddir, 'conf'))
30 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/local.conf'), os.path.join(builddir, 'conf/local.conf'))
31 config = {}
32 config['default_sstate_dir'] = "SSTATE_DIR ?= \"${TOPDIR}/sstate-cache\""
33 config['null_sstate_mirrors'] = "SSTATE_MIRRORS = \"\""
34 config['default_tmp_dir'] = "TMPDIR = \"${TOPDIR}/tmp\""
35 for key in config:
36 ftools.append_file(os.path.join(builddir, 'conf/selftest.inc'), config[key])
37 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/bblayers.conf'), os.path.join(builddir, 'conf/bblayers.conf'))
38 try:
39 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/auto.conf'), os.path.join(builddir, 'conf/auto.conf'))
40 except:
41 pass
42
43 def hardlink_tree(self, src, dst):
44 os.mkdir(dst)
45 self.track_for_cleanup(dst)
46 for root, dirs, files in os.walk(src):
47 if root == src:
48 continue
49 os.mkdir(os.path.join(dst, root.split(src)[1][1:]))
50 for sstate_file in files:
51 os.link(os.path.join(root, sstate_file), os.path.join(dst, root.split(src)[1][1:], sstate_file))
52
53 def run_test_sstate_rebuild(self, primary_targets, relocate=False, rebuild_dependencies=False):
54 buildA = os.path.join(self.builddir, 'buildA')
55 if relocate:
56 buildB = os.path.join(self.builddir, 'buildB')
57 else:
58 buildB = buildA
59
60 if rebuild_dependencies:
61 rebuild_targets = self.get_dep_targets(primary_targets)
62 else:
63 rebuild_targets = primary_targets
64
65 self.configure_builddir(buildA)
66 runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildA)) + 'bitbake ' + ' '.join(map(str, primary_targets)), shell=True, executable='/bin/bash')
67 self.hardlink_tree(os.path.join(buildA, 'sstate-cache'), os.path.join(self.builddir, 'sstate-cache-buildA'))
68 shutil.rmtree(buildA)
69
70 failed_rebuild = []
71 failed_cleansstate = []
72 for target in rebuild_targets:
73 self.configure_builddir(buildB)
74 self.hardlink_tree(os.path.join(self.builddir, 'sstate-cache-buildA'), os.path.join(buildB, 'sstate-cache'))
75
76 result_cleansstate = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake -ccleansstate ' + target, ignore_status=True, shell=True, executable='/bin/bash')
77 if not result_cleansstate.status == 0:
78 failed_cleansstate.append(target)
79 shutil.rmtree(buildB)
80 continue
81
82 result_build = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake ' + target, ignore_status=True, shell=True, executable='/bin/bash')
83 if not result_build.status == 0:
84 failed_rebuild.append(target)
85
86 shutil.rmtree(buildB)
87
88 self.assertFalse(failed_rebuild, msg="The following recipes have failed to rebuild: %s" % ' '.join(map(str, failed_rebuild)))
89 self.assertFalse(failed_cleansstate, msg="The following recipes have failed cleansstate(all others have passed both cleansstate and rebuild from sstate tests): %s" % ' '.join(map(str, failed_cleansstate)))
90
91 def test_sstate_relocation(self):
92 self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=True, rebuild_dependencies=True)
93
94 def test_sstate_rebuild(self):
95 self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=False, rebuild_dependencies=True)
diff --git a/meta/lib/oeqa/selftest/_toaster.py b/meta/lib/oeqa/selftest/_toaster.py
new file mode 100644
index 0000000000..1cf28a0144
--- /dev/null
+++ b/meta/lib/oeqa/selftest/_toaster.py
@@ -0,0 +1,445 @@
1import unittest
2import os
3import sys
4import shlex, subprocess
5import urllib, commands, time, getpass, re, json, shlex
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd
10
11sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../', 'bitbake/lib/toaster')))
12os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings")
13
14import toastermain.settings
15from django.db.models import Q
16from orm.models import *
17from oeqa.utils.decorators import testcase
18
19class ToasterSetup(oeSelfTest):
20
21 def recipe_parse(self, file_path, var):
22 for line in open(file_path,'r'):
23 if line.find(var) > -1:
24 val = line.split(" = ")[1].replace("\"", "").strip()
25 return val
26
27 def fix_file_path(self, file_path):
28 if ":" in file_path:
29 file_path=file_path.split(":")[2]
30 return file_path
31
32class Toaster_DB_Tests(ToasterSetup):
33
34 # Check if build name is unique - tc_id=795
35 @testcase(795)
36 def test_Build_Unique_Name(self):
37 all_builds = Build.objects.all().count()
38 distinct_builds = Build.objects.values('id').distinct().count()
39 self.assertEqual(distinct_builds, all_builds, msg = 'Build name is not unique')
40
41 # Check if build coocker log path is unique - tc_id=819
42 @testcase(819)
43 def test_Build_Unique_Cooker_Log_Path(self):
44 distinct_path = Build.objects.values('cooker_log_path').distinct().count()
45 total_builds = Build.objects.values('id').count()
46 self.assertEqual(distinct_path, total_builds, msg = 'Build coocker log path is not unique')
47
48 # Check if the number of errors matches the number of orm_logmessage.level entries with value 2 - tc_id=820
49 @testcase(820)
50 def test_Build_Errors_No(self):
51 builds = Build.objects.values('id', 'errors_no')
52 cnt_err = []
53 for build in builds:
54 log_mess_err_no = LogMessage.objects.filter(build = build['id'], level = 2).count()
55 if (build['errors_no'] != log_mess_err_no):
56 cnt_err.append(build['id'])
57 self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
58
59 # Check if the number of warnings matches the number of orm_logmessage.level entries with value 1 - tc=821
60 @testcase(821)
61 def test_Build_Warnings_No(self):
62 builds = Build.objects.values('id', 'warnings_no')
63 cnt_err = []
64 for build in builds:
65 log_mess_warn_no = LogMessage.objects.filter(build = build['id'], level = 1).count()
66 if (build['warnings_no'] != log_mess_warn_no):
67 cnt_err.append(build['id'])
68 self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
69
70 # Check if the build succeeded then the errors_no is 0 - tc_id=822
71 @testcase(822)
72 def test_Build_Suceeded_Errors_No(self):
73 builds = Build.objects.filter(outcome = 0).values('id', 'errors_no')
74 cnt_err = []
75 for build in builds:
76 if (build['errors_no'] != 0):
77 cnt_err.append(build['id'])
78 self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
79
80 # Check if task order is unique for one build - tc=824
81 @testcase(824)
82 def test_Task_Unique_Order(self):
83 builds = Build.objects.values('id')
84 cnt_err = []
85 for build in builds:
86 total_task_order = Task.objects.filter(build = build['id']).values('order').count()
87 distinct_task_order = Task.objects.filter(build = build['id']).values('order').distinct().count()
88 if (total_task_order != distinct_task_order):
89 cnt_err.append(build['id'])
90 self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
91
92 # Check task order sequence for one build - tc=825
93 @testcase(825)
94 def test_Task_Order_Sequence(self):
95 builds = builds = Build.objects.values('id')
96 cnt_err = []
97 for build in builds:
98 tasks = Task.objects.filter(Q(build = build['id']), ~Q(order = None), ~Q(task_name__contains = '_setscene')).values('id', 'order').order_by("order")
99 cnt_tasks = 0
100 for task in tasks:
101 cnt_tasks += 1
102 if (task['order'] != cnt_tasks):
103 cnt_err.append(task['id'])
104 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
105
106 # Check if disk_io matches the difference between EndTimeIO and StartTimeIO in build stats - tc=828
107 ### this needs to be updated ###
108 #def test_Task_Disk_IO_TC828(self):
109
110 # Check if outcome = 2 (SSTATE) then sstate_result must be 3 (RESTORED) - tc=832
111 @testcase(832)
112 def test_Task_If_Outcome_2_Sstate_Result_Must_Be_3(self):
113 tasks = Task.objects.filter(outcome = 2).values('id', 'sstate_result')
114 cnt_err = []
115 for task in tasks:
116 if (row['sstate_result'] != 3):
117 cnt_err.append(task['id'])
118 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
119
120 # Check if outcome = 1 (COVERED) or 3 (EXISTING) then sstate_result must be 0 (SSTATE_NA) - tc=833
121 @testcase(833)
122 def test_Task_If_Outcome_1_3_Sstate_Result_Must_Be_0(self):
123 tasks = Task.objects.filter(outcome__in = (1, 3)).values('id', 'sstate_result')
124 cnt_err = []
125 for task in tasks:
126 if (task['sstate_result'] != 0):
127 cnt_err.append(task['id'])
128 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
129
130 # Check if outcome is 0 (SUCCESS) or 4 (FAILED) then sstate_result must be 0 (NA), 1 (MISS) or 2 (FAILED) - tc=834
131 @testcase(834)
132 def test_Task_If_Outcome_0_4_Sstate_Result_Must_Be_0_1_2(self):
133 tasks = Task.objects.filter(outcome__in = (0, 4)).values('id', 'sstate_result')
134 cnt_err = []
135 for task in tasks:
136 if (task['sstate_result'] not in [0, 1, 2]):
137 cnt_err.append(task['id'])
138 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
139
140 # Check if task_executed = TRUE (1), script_type must be 0 (CODING_NA), 2 (CODING_PYTHON), 3 (CODING_SHELL) - tc=891
141 @testcase(891)
142 def test_Task_If_Task_Executed_True_Script_Type_0_2_3(self):
143 tasks = Task.objects.filter(task_executed = 1).values('id', 'script_type')
144 cnt_err = []
145 for task in tasks:
146 if (task['script_type'] not in [0, 2, 3]):
147 cnt_err.append(task['id'])
148 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
149
150 # Check if task_executed = TRUE (1), outcome must be 0 (SUCCESS) or 4 (FAILED) - tc=836
151 @testcase(836)
152 def test_Task_If_Task_Executed_True_Outcome_0_4(self):
153 tasks = Task.objects.filter(task_executed = 1).values('id', 'outcome')
154 cnt_err = []
155 for task in tasks:
156 if (task['outcome'] not in [0, 4]):
157 cnt_err.append(task['id'])
158 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
159
160 # Check if task_executed = FALSE (0), script_type must be 0 - tc=890
161 @testcase(890)
162 def test_Task_If_Task_Executed_False_Script_Type_0(self):
163 tasks = Task.objects.filter(task_executed = 0).values('id', 'script_type')
164 cnt_err = []
165 for task in tasks:
166 if (task['script_type'] != 0):
167 cnt_err.append(task['id'])
168 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
169
170 # Check if task_executed = FALSE (0) and build outcome = SUCCEEDED (0), task outcome must be 1 (COVERED), 2 (CACHED), 3 (PREBUILT), 5 (EMPTY) - tc=837
171 @testcase(837)
172 def test_Task_If_Task_Executed_False_Outcome_1_2_3_5(self):
173 builds = Build.objects.filter(outcome = 0).values('id')
174 cnt_err = []
175 for build in builds:
176 tasks = Task.objects.filter(build = build['id'], task_executed = 0).values('id', 'outcome')
177 for task in tasks:
178 if (task['outcome'] not in [1, 2, 3, 5]):
179 cnt_err.append(task['id'])
180 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
181
182 # Key verification - tc=888
183 @testcase(888)
184 def test_Target_Installed_Package(self):
185 rows = Target_Installed_Package.objects.values('id', 'target_id', 'package_id')
186 cnt_err = []
187 for row in rows:
188 target = Target.objects.filter(id = row['target_id']).values('id')
189 package = Package.objects.filter(id = row['package_id']).values('id')
190 if (not target or not package):
191 cnt_err.append(row['id'])
192 self.assertEqual(len(cnt_err), 0, msg = 'Errors for target installed package id: %s' % cnt_err)
193
194 # Key verification - tc=889
195 @testcase(889)
196 def test_Task_Dependency(self):
197 rows = Task_Dependency.objects.values('id', 'task_id', 'depends_on_id')
198 cnt_err = []
199 for row in rows:
200 task_id = Task.objects.filter(id = row['task_id']).values('id')
201 depends_on_id = Task.objects.filter(id = row['depends_on_id']).values('id')
202 if (not task_id or not depends_on_id):
203 cnt_err.append(row['id'])
204 self.assertEqual(len(cnt_err), 0, msg = 'Errors for task dependency id: %s' % cnt_err)
205
206 # Check if build target file_name is populated only if is_image=true AND orm_build.outcome=0 then if the file exists and its size matches the file_size value
207 ### Need to add the tc in the test run
208 @testcase(1037)
209 def test_Target_File_Name_Populated(self):
210 builds = Build.objects.filter(outcome = 0).values('id')
211 for build in builds:
212 targets = Target.objects.filter(build_id = build['id'], is_image = 1).values('id')
213 for target in targets:
214 target_files = Target_Image_File.objects.filter(target_id = target['id']).values('id', 'file_name', 'file_size')
215 cnt_err = []
216 for file_info in target_files:
217 target_id = file_info['id']
218 target_file_name = file_info['file_name']
219 target_file_size = file_info['file_size']
220 if (not target_file_name or not target_file_size):
221 cnt_err.append(target_id)
222 else:
223 if (not os.path.exists(target_file_name)):
224 cnt_err.append(target_id)
225 else:
226 if (os.path.getsize(target_file_name) != target_file_size):
227 cnt_err.append(target_id)
228 self.assertEqual(len(cnt_err), 0, msg = 'Errors for target image file id: %s' % cnt_err)
229
230 # Key verification - tc=884
231 @testcase(884)
232 def test_Package_Dependency(self):
233 cnt_err = []
234 deps = Package_Dependency.objects.values('id', 'package_id', 'depends_on_id')
235 for dep in deps:
236 if (dep['package_id'] == dep['depends_on_id']):
237 cnt_err.append(dep['id'])
238 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package dependency id: %s' % cnt_err)
239
240 # Check if recipe name does not start with a number (0-9) - tc=838
241 @testcase(838)
242 def test_Recipe_Name(self):
243 recipes = Recipe.objects.values('id', 'name')
244 cnt_err = []
245 for recipe in recipes:
246 if (recipe['name'][0].isdigit() is True):
247 cnt_err.append(recipe['id'])
248 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
249
250 # Check if recipe section matches the content of the SECTION variable (if set) in file_path - tc=839
251 @testcase(839)
252 def test_Recipe_DB_Section_Match_Recipe_File_Section(self):
253 recipes = Recipe.objects.values('id', 'section', 'file_path')
254 cnt_err = []
255 for recipe in recipes:
256 file_path = self.fix_file_path(recipe['file_path'])
257 file_exists = os.path.isfile(file_path)
258 if (not file_path or (file_exists is False)):
259 cnt_err.append(recipe['id'])
260 else:
261 file_section = self.recipe_parse(file_path, "SECTION = ")
262 db_section = recipe['section']
263 if file_section:
264 if (db_section != file_section):
265 cnt_err.append(recipe['id'])
266 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
267
268 # Check if recipe license matches the content of the LICENSE variable (if set) in file_path - tc=840
269 @testcase(840)
270 def test_Recipe_DB_License_Match_Recipe_File_License(self):
271 recipes = Recipe.objects.values('id', 'license', 'file_path')
272 cnt_err = []
273 for recipe in recipes:
274 file_path = self.fix_file_path(recipe['file_path'])
275 file_exists = os.path.isfile(file_path)
276 if (not file_path or (file_exists is False)):
277 cnt_err.append(recipe['id'])
278 else:
279 file_license = self.recipe_parse(file_path, "LICENSE = ")
280 db_license = recipe['license']
281 if file_license:
282 if (db_license != file_license):
283 cnt_err.append(recipe['id'])
284 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
285
286 # Check if recipe homepage matches the content of the HOMEPAGE variable (if set) in file_path - tc=841
287 @testcase(841)
288 def test_Recipe_DB_Homepage_Match_Recipe_File_Homepage(self):
289 recipes = Recipe.objects.values('id', 'homepage', 'file_path')
290 cnt_err = []
291 for recipe in recipes:
292 file_path = self.fix_file_path(recipe['file_path'])
293 file_exists = os.path.isfile(file_path)
294 if (not file_path or (file_exists is False)):
295 cnt_err.append(recipe['id'])
296 else:
297 file_homepage = self.recipe_parse(file_path, "HOMEPAGE = ")
298 db_homepage = recipe['homepage']
299 if file_homepage:
300 if (db_homepage != file_homepage):
301 cnt_err.append(recipe['id'])
302 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
303
304 # Check if recipe bugtracker matches the content of the BUGTRACKER variable (if set) in file_path - tc=842
305 @testcase(842)
306 def test_Recipe_DB_Bugtracker_Match_Recipe_File_Bugtracker(self):
307 recipes = Recipe.objects.values('id', 'bugtracker', 'file_path')
308 cnt_err = []
309 for recipe in recipes:
310 file_path = self.fix_file_path(recipe['file_path'])
311 file_exists = os.path.isfile(file_path)
312 if (not file_path or (file_exists is False)):
313 cnt_err.append(recipe['id'])
314 else:
315 file_bugtracker = self.recipe_parse(file_path, "BUGTRACKER = ")
316 db_bugtracker = recipe['bugtracker']
317 if file_bugtracker:
318 if (db_bugtracker != file_bugtracker):
319 cnt_err.append(recipe['id'])
320 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe id: %s' % cnt_err)
321
322 # Recipe key verification, recipe name does not depends on a recipe having the same name - tc=883
323 @testcase(883)
324 def test_Recipe_Dependency(self):
325 deps = Recipe_Dependency.objects.values('id', 'recipe_id', 'depends_on_id')
326 cnt_err = []
327 for dep in deps:
328 if (not dep['recipe_id'] or not dep['depends_on_id']):
329 cnt_err.append(dep['id'])
330 else:
331 name = Recipe.objects.filter(id = dep['recipe_id']).values('name')
332 dep_name = Recipe.objects.filter(id = dep['depends_on_id']).values('name')
333 if (name == dep_name):
334 cnt_err.append(dep['id'])
335 self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe dependency id: %s' % cnt_err)
336
337 # Check if package name does not start with a number (0-9) - tc=846
338 @testcase(846)
339 def test_Package_Name_For_Number(self):
340 packages = Package.objects.filter(~Q(size = -1)).values('id', 'name')
341 cnt_err = []
342 for package in packages:
343 if (package['name'][0].isdigit() is True):
344 cnt_err.append(package['id'])
345 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
346
347 # Check if package version starts with a number (0-9) - tc=847
348 @testcase(847)
349 def test_Package_Version_Starts_With_Number(self):
350 packages = Package.objects.filter(~Q(size = -1)).values('id', 'version')
351 cnt_err = []
352 for package in packages:
353 if (package['version'][0].isdigit() is False):
354 cnt_err.append(package['id'])
355 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
356
357 # Check if package revision starts with 'r' - tc=848
358 @testcase(848)
359 def test_Package_Revision_Starts_With_r(self):
360 packages = Package.objects.filter(~Q(size = -1)).values('id', 'revision')
361 cnt_err = []
362 for package in packages:
363 if (package['revision'][0].startswith("r") is False):
364 cnt_err.append(package['id'])
365 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
366
367 # Check the validity of the package build_id
368 ### TC must be added in test run
369 @testcase(1038)
370 def test_Package_Build_Id(self):
371 packages = Package.objects.filter(~Q(size = -1)).values('id', 'build_id')
372 cnt_err = []
373 for package in packages:
374 build_id = Build.objects.filter(id = package['build_id']).values('id')
375 if (not build_id):
376 cnt_err.append(package['id'])
377 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
378
379 # Check the validity of package recipe_id
380 ### TC must be added in test run
381 @testcase(1039)
382 def test_Package_Recipe_Id(self):
383 packages = Package.objects.filter(~Q(size = -1)).values('id', 'recipe_id')
384 cnt_err = []
385 for package in packages:
386 recipe_id = Recipe.objects.filter(id = package['recipe_id']).values('id')
387 if (not recipe_id):
388 cnt_err.append(package['id'])
389 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
390
391 # Check if package installed_size field is not null
392 ### TC must be aded in test run
393 @testcase(1040)
394 def test_Package_Installed_Size_Not_NULL(self):
395 packages = Package.objects.filter(installed_size__isnull = True).values('id')
396 cnt_err = []
397 for package in packages:
398 cnt_err.append(package['id'])
399 self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
400
401 # Check if all layers requests return exit code is 200 - tc=843
402 @testcase(843)
403 def test_Layers_Requests_Exit_Code(self):
404 layers = Layer.objects.values('id', 'layer_index_url')
405 cnt_err = []
406 for layer in layers:
407 resp = urllib.urlopen(layer['layer_index_url'])
408 if (resp.getcode() != 200):
409 cnt_err.append(layer['id'])
410 self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
411
412 # Check if the output of bitbake-layers show_layers matches the info from database - tc=895
413 @testcase(895)
414 def test_Layers_Show_Layers(self):
415 layers = Layer.objects.values('id', 'name', 'local_path')
416 cmd = commands.getoutput('bitbake-layers show_layers')
417 cnt_err = []
418 for layer in layers:
419 if (layer['name'] or layer['local_path']) not in cmd:
420 cnt_err.append(layer['id'])
421 self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
422
423 # Check if django server starts regardless of the timezone set on the machine - tc=905
424 @testcase(905)
425 def test_Start_Django_Timezone(self):
426 current_path = os.getcwd()
427 zonefilelist = []
428 ZONEINFOPATH = '/usr/share/zoneinfo/'
429 os.chdir("../bitbake/lib/toaster/")
430 cnt_err = 0
431 for filename in os.listdir(ZONEINFOPATH):
432 if os.path.isfile(os.path.join(ZONEINFOPATH, filename)):
433 zonefilelist.append(filename)
434 for k in range(len(zonefilelist)):
435 if k <= 5:
436 files = zonefilelist[k]
437 os.system("export TZ="+str(files)+"; python manage.py runserver > /dev/null 2>&1 &")
438 time.sleep(3)
439 pid = subprocess.check_output("ps aux | grep '[/u]sr/bin/python manage.py runserver' | awk '{print $2}'", shell = True)
440 if pid:
441 os.system("kill -9 "+str(pid))
442 else:
443 cnt_err.append(zonefilelist[k])
444 self.assertEqual(cnt_err, 0, msg = 'Errors django server does not start with timezone: %s' % cnt_err)
445 os.chdir(current_path)
diff --git a/meta/lib/oeqa/selftest/base.py b/meta/lib/oeqa/selftest/base.py
new file mode 100644
index 0000000000..80b9b4b312
--- /dev/null
+++ b/meta/lib/oeqa/selftest/base.py
@@ -0,0 +1,131 @@
1# Copyright (c) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5
6# DESCRIPTION
7# Base class inherited by test classes in meta/lib/selftest
8
9import unittest
10import os
11import sys
12import shutil
13import logging
14import errno
15
16import oeqa.utils.ftools as ftools
17from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
18from oeqa.utils.decorators import LogResults
19
20@LogResults
21class oeSelfTest(unittest.TestCase):
22
23 log = logging.getLogger("selftest.base")
24 longMessage = True
25
26 def __init__(self, methodName="runTest"):
27 self.builddir = os.environ.get("BUILDDIR")
28 self.localconf_path = os.path.join(self.builddir, "conf/local.conf")
29 self.testinc_path = os.path.join(self.builddir, "conf/selftest.inc")
30 self.testlayer_path = oeSelfTest.testlayer_path
31 self._extra_tear_down_commands = []
32 self._track_for_cleanup = []
33 super(oeSelfTest, self).__init__(methodName)
34
35 def setUp(self):
36 os.chdir(self.builddir)
37 # we don't know what the previous test left around in config or inc files
38 # if it failed so we need a fresh start
39 try:
40 os.remove(self.testinc_path)
41 except OSError as e:
42 if e.errno != errno.ENOENT:
43 raise
44 for root, _, files in os.walk(self.testlayer_path):
45 for f in files:
46 if f == 'test_recipe.inc':
47 os.remove(os.path.join(root, f))
48 # tests might need their own setup
49 # but if they overwrite this one they have to call
50 # super each time, so let's give them an alternative
51 self.setUpLocal()
52
53 def setUpLocal(self):
54 pass
55
56 def tearDown(self):
57 if self._extra_tear_down_commands:
58 failed_extra_commands = []
59 for command in self._extra_tear_down_commands:
60 result = runCmd(command, ignore_status=True)
61 if not result.status == 0:
62 failed_extra_commands.append(command)
63 if failed_extra_commands:
64 self.log.warning("tearDown commands have failed: %s" % ', '.join(map(str, failed_extra_commands)))
65 self.log.debug("Trying to move on.")
66 self._extra_tear_down_commands = []
67
68 if self._track_for_cleanup:
69 for path in self._track_for_cleanup:
70 if os.path.isdir(path):
71 shutil.rmtree(path)
72 if os.path.isfile(path):
73 os.remove(path)
74 self._track_for_cleanup = []
75
76 self.tearDownLocal()
77
78 def tearDownLocal(self):
79 pass
80
81 # add test specific commands to the tearDown method.
82 def add_command_to_tearDown(self, command):
83 self.log.debug("Adding command '%s' to tearDown for this test." % command)
84 self._extra_tear_down_commands.append(command)
85 # add test specific files or directories to be removed in the tearDown method
86 def track_for_cleanup(self, path):
87 self.log.debug("Adding path '%s' to be cleaned up when test is over" % path)
88 self._track_for_cleanup.append(path)
89
90 # write to <builddir>/conf/selftest.inc
91 def write_config(self, data):
92 self.log.debug("Writing to: %s\n%s\n" % (self.testinc_path, data))
93 ftools.write_file(self.testinc_path, data)
94
95 # append to <builddir>/conf/selftest.inc
96 def append_config(self, data):
97 self.log.debug("Appending to: %s\n%s\n" % (self.testinc_path, data))
98 ftools.append_file(self.testinc_path, data)
99
100 # remove data from <builddir>/conf/selftest.inc
101 def remove_config(self, data):
102 self.log.debug("Removing from: %s\n\%s\n" % (self.testinc_path, data))
103 ftools.remove_from_file(self.testinc_path, data)
104
105 # write to meta-sefltest/recipes-test/<recipe>/test_recipe.inc
106 def write_recipeinc(self, recipe, data):
107 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
108 self.log.debug("Writing to: %s\n%s\n" % (inc_file, data))
109 ftools.write_file(inc_file, data)
110
111 # append data to meta-sefltest/recipes-test/<recipe>/test_recipe.inc
112 def append_recipeinc(self, recipe, data):
113 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
114 self.log.debug("Appending to: %s\n%s\n" % (inc_file, data))
115 ftools.append_file(inc_file, data)
116
117 # remove data from meta-sefltest/recipes-test/<recipe>/test_recipe.inc
118 def remove_recipeinc(self, recipe, data):
119 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
120 self.log.debug("Removing from: %s\n%s\n" % (inc_file, data))
121 ftools.remove_from_file(inc_file, data)
122
123 # delete meta-sefltest/recipes-test/<recipe>/test_recipe.inc file
124 def delete_recipeinc(self, recipe):
125 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
126 self.log.debug("Deleting file: %s" % inc_file)
127 try:
128 os.remove(inc_file)
129 except OSError as e:
130 if e.errno != errno.ENOENT:
131 raise
diff --git a/meta/lib/oeqa/selftest/bblayers.py b/meta/lib/oeqa/selftest/bblayers.py
new file mode 100644
index 0000000000..1ead8e8671
--- /dev/null
+++ b/meta/lib/oeqa/selftest/bblayers.py
@@ -0,0 +1,43 @@
1import unittest
2import os
3import logging
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd
10from oeqa.utils.decorators import testcase
11
12class BitbakeLayers(oeSelfTest):
13
14 @testcase(756)
15 def test_bitbakelayers_showcrossdepends(self):
16 result = runCmd('bitbake-layers show-cross-depends')
17 self.assertTrue('aspell' in result.output)
18
19 @testcase(83)
20 def test_bitbakelayers_showlayers(self):
21 result = runCmd('bitbake-layers show_layers')
22 self.assertTrue('meta-selftest' in result.output)
23
24 @testcase(93)
25 def test_bitbakelayers_showappends(self):
26 result = runCmd('bitbake-layers show_appends')
27 self.assertTrue('xcursor-transparent-theme_0.1.1.bbappend' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
28
29 @testcase(90)
30 def test_bitbakelayers_showoverlayed(self):
31 result = runCmd('bitbake-layers show_overlayed')
32 self.assertTrue('aspell' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
33
34 @testcase(95)
35 def test_bitbakelayers_flatten(self):
36 self.assertFalse(os.path.isdir(os.path.join(self.builddir, 'test')))
37 result = runCmd('bitbake-layers flatten test')
38 bb_file = os.path.join(self.builddir, 'test/recipes-graphics/xcursor-transparent-theme/xcursor-transparent-theme_0.1.1.bb')
39 self.assertTrue(os.path.isfile(bb_file))
40 contents = ftools.read_file(bb_file)
41 find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents)
42 shutil.rmtree(os.path.join(self.builddir, 'test'))
43 self.assertTrue(find_in_contents)
diff --git a/meta/lib/oeqa/selftest/bbtests.py b/meta/lib/oeqa/selftest/bbtests.py
new file mode 100644
index 0000000000..68f97bd8e3
--- /dev/null
+++ b/meta/lib/oeqa/selftest/bbtests.py
@@ -0,0 +1,178 @@
1import unittest
2import os
3import logging
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd, bitbake, get_bb_var
10from oeqa.utils.decorators import testcase
11
12class BitbakeTests(oeSelfTest):
13
14 @testcase(789)
15 def test_run_bitbake_from_dir_1(self):
16 os.chdir(os.path.join(self.builddir, 'conf'))
17 bitbake('-e')
18
19 @testcase(790)
20 def test_run_bitbake_from_dir_2(self):
21 my_env = os.environ.copy()
22 my_env['BBPATH'] = my_env['BUILDDIR']
23 os.chdir(os.path.dirname(os.environ['BUILDDIR']))
24 bitbake('-e', env=my_env)
25
26 @testcase(806)
27 def test_event_handler(self):
28 self.write_config("INHERIT += \"test_events\"")
29 result = bitbake('m4-native')
30 find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Preparing runqueue", result.output)
31 find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output)
32 self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output)
33 self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output)
34 self.assertFalse('Test for bb.event.InvalidEvent' in result.output)
35
36 @testcase(103)
37 def test_local_sstate(self):
38 bitbake('m4-native -ccleansstate')
39 bitbake('m4-native')
40 bitbake('m4-native -cclean')
41 result = bitbake('m4-native')
42 find_setscene = re.search("m4-native.*do_.*_setscene", result.output)
43 self.assertTrue(find_setscene)
44
45 @testcase(105)
46 def test_bitbake_invalid_recipe(self):
47 result = bitbake('-b asdf', ignore_status=True)
48 self.assertTrue("ERROR: Unable to find any recipe file matching 'asdf'" in result.output)
49
50 @testcase(107)
51 def test_bitbake_invalid_target(self):
52 result = bitbake('asdf', ignore_status=True)
53 self.assertTrue("ERROR: Nothing PROVIDES 'asdf'" in result.output)
54
55 @testcase(106)
56 def test_warnings_errors(self):
57 result = bitbake('-b asdf', ignore_status=True)
58 find_warnings = re.search("Summary: There w.{2,3}? [1-9][0-9]* WARNING messages* shown", result.output)
59 find_errors = re.search("Summary: There w.{2,3}? [1-9][0-9]* ERROR messages* shown", result.output)
60 self.assertTrue(find_warnings, msg="Did not find the mumber of warnings at the end of the build:\n" + result.output)
61 self.assertTrue(find_errors, msg="Did not find the mumber of errors at the end of the build:\n" + result.output)
62
63 @testcase(108)
64 def test_invalid_patch(self):
65 self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"')
66 result = bitbake('man -c patch', ignore_status=True)
67 self.delete_recipeinc('man')
68 bitbake('-cclean man')
69 self.assertTrue("ERROR: Function failed: patch_do_patch" in result.output)
70
71 @testcase(163)
72 def test_force_task(self):
73 bitbake('m4-native')
74 result = bitbake('-C compile m4-native')
75 look_for_tasks = ['do_compile', 'do_install', 'do_populate_sysroot']
76 for task in look_for_tasks:
77 find_task = re.search("m4-native.*%s" % task, result.output)
78 self.assertTrue(find_task)
79
80 @testcase(167)
81 def test_bitbake_g(self):
82 result = bitbake('-g core-image-full-cmdline')
83 self.assertTrue('NOTE: PN build list saved to \'pn-buildlist\'' in result.output)
84 self.assertTrue('openssh' in ftools.read_file(os.path.join(self.builddir, 'pn-buildlist')))
85 for f in ['pn-buildlist', 'pn-depends.dot', 'package-depends.dot', 'task-depends.dot']:
86 os.remove(f)
87
88 @testcase(899)
89 def test_image_manifest(self):
90 bitbake('core-image-minimal')
91 deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
92 imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
93 manifest = os.path.join(deploydir, imagename + ".manifest")
94 self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image")
95
96 @testcase(168)
97 def test_invalid_recipe_src_uri(self):
98 data = 'SRC_URI = "file://invalid"'
99 self.write_recipeinc('man', data)
100 bitbake('-ccleanall man')
101 result = bitbake('-c fetch man', ignore_status=True)
102 bitbake('-ccleanall man')
103 self.delete_recipeinc('man')
104 self.assertEqual(result.status, 1, msg='Command succeded when it should have failed')
105 self.assertTrue('Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output)
106 self.assertTrue('ERROR: Function failed: Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.' in result.output)
107
108 @testcase(171)
109 def test_rename_downloaded_file(self):
110 data = 'SRC_URI_append = ";downloadfilename=test-aspell.tar.gz"'
111 self.write_recipeinc('aspell', data)
112 bitbake('-ccleanall aspell')
113 result = bitbake('-c fetch aspell', ignore_status=True)
114 self.delete_recipeinc('aspell')
115 self.assertEqual(result.status, 0)
116 self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz')))
117 self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz.done')))
118 bitbake('-ccleanall aspell')
119
120 @testcase(1028)
121 def test_environment(self):
122 self.append_config("TEST_ENV=\"localconf\"")
123 result = runCmd('bitbake -e | grep TEST_ENV=')
124 self.assertTrue('localconf' in result.output)
125 self.remove_config("TEST_ENV=\"localconf\"")
126
127 @testcase(1029)
128 def test_dry_run(self):
129 result = runCmd('bitbake -n m4-native')
130 self.assertEqual(0, result.status)
131
132 @testcase(1030)
133 def test_just_parse(self):
134 result = runCmd('bitbake -p')
135 self.assertEqual(0, result.status)
136
137 @testcase(1031)
138 def test_version(self):
139 result = runCmd('bitbake -s | grep wget')
140 find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output)
141 self.assertTrue(find)
142
143 @testcase(1032)
144 def test_prefile(self):
145 preconf = os.path.join(self.builddir, 'conf/prefile.conf')
146 self.track_for_cleanup(preconf)
147 ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"")
148 result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
149 self.assertTrue('prefile' in result.output)
150 self.append_config("TEST_PREFILE=\"localconf\"")
151 result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
152 self.assertTrue('localconf' in result.output)
153 self.remove_config("TEST_PREFILE=\"localconf\"")
154
155 @testcase(1033)
156 def test_postfile(self):
157 postconf = os.path.join(self.builddir, 'conf/postfile.conf')
158 self.track_for_cleanup(postconf)
159 ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"")
160 self.append_config("TEST_POSTFILE=\"localconf\"")
161 result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=')
162 self.assertTrue('postfile' in result.output)
163 self.remove_config("TEST_POSTFILE=\"localconf\"")
164
165 @testcase(1034)
166 def test_checkuri(self):
167 result = runCmd('bitbake -c checkuri m4')
168 self.assertEqual(0, result.status)
169
170 @testcase(1035)
171 def test_continue(self):
172 self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
173 runCmd('bitbake -c cleanall man xcursor-transparent-theme')
174 result = runCmd('bitbake man xcursor-transparent-theme -k', ignore_status=True)
175 errorpos = result.output.find('ERROR: Function failed: do_fail_task')
176 manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output)
177 continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1))
178 self.assertLess(errorpos,continuepos)
diff --git a/meta/lib/oeqa/selftest/buildhistory.py b/meta/lib/oeqa/selftest/buildhistory.py
new file mode 100644
index 0000000000..d8cae4664b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/buildhistory.py
@@ -0,0 +1,45 @@
1import unittest
2import os
3import re
4import shutil
5import datetime
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer
10
11
12class BuildhistoryBase(oeSelfTest):
13
14 def config_buildhistory(self, tmp_bh_location=False):
15 if (not 'buildhistory' in get_bb_var('USER_CLASSES')) and (not 'buildhistory' in get_bb_var('INHERIT')):
16 add_buildhistory_config = 'INHERIT += "buildhistory"\nBUILDHISTORY_COMMIT = "1"'
17 self.append_config(add_buildhistory_config)
18
19 if tmp_bh_location:
20 # Using a temporary buildhistory location for testing
21 tmp_bh_dir = os.path.join(self.builddir, "tmp_buildhistory_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
22 buildhistory_dir_config = "BUILDHISTORY_DIR = \"%s\"" % tmp_bh_dir
23 self.append_config(buildhistory_dir_config)
24 self.track_for_cleanup(tmp_bh_dir)
25
26 def run_buildhistory_operation(self, target, global_config='', target_config='', change_bh_location=False, expect_error=False, error_regex=''):
27 if change_bh_location:
28 tmp_bh_location = True
29 else:
30 tmp_bh_location = False
31 self.config_buildhistory(tmp_bh_location)
32
33 self.append_config(global_config)
34 self.append_recipeinc(target, target_config)
35 bitbake("-cclean %s" % target)
36 result = bitbake(target, ignore_status=True)
37 self.remove_config(global_config)
38 self.remove_recipeinc(target, target_config)
39
40 if expect_error:
41 self.assertEqual(result.status, 1, msg="Error expected for global config '%s' and target config '%s'" % (global_config, target_config))
42 search_for_error = re.search(error_regex, result.output)
43 self.assertTrue(search_for_error, msg="Could not find desired error in output: %s" % error_regex)
44 else:
45 self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output))
diff --git a/meta/lib/oeqa/selftest/buildoptions.py b/meta/lib/oeqa/selftest/buildoptions.py
new file mode 100644
index 0000000000..a250cae0e1
--- /dev/null
+++ b/meta/lib/oeqa/selftest/buildoptions.py
@@ -0,0 +1,120 @@
1import unittest
2import os
3import logging
4import re
5
6from oeqa.selftest.base import oeSelfTest
7from oeqa.selftest.buildhistory import BuildhistoryBase
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var
9import oeqa.utils.ftools as ftools
10from oeqa.utils.decorators import testcase
11
12class ImageOptionsTests(oeSelfTest):
13
14 @testcase(761)
15 def test_incremental_image_generation(self):
16 bitbake("-c cleanall core-image-minimal")
17 self.write_config('INC_RPM_IMAGE_GEN = "1"')
18 self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
19 bitbake("core-image-minimal")
20 res = runCmd("grep 'Installing openssh-sshd' %s" % (os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")), ignore_status=True)
21 self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
22 self.assertEqual(0, res.status, msg="No match for openssh-sshd in log.do_rootfs")
23 bitbake("core-image-minimal")
24 res = runCmd("grep 'Removing openssh-sshd' %s" %(os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")),ignore_status=True)
25 self.assertEqual(0, res.status, msg="openssh-sshd was not removed from image")
26
27 @testcase(925)
28 def test_rm_old_image(self):
29 bitbake("core-image-minimal")
30 deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
31 imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
32 deploydir_files = os.listdir(deploydir)
33 track_original_files = []
34 for image_file in deploydir_files:
35 if imagename in image_file and os.path.islink(os.path.join(deploydir, image_file)):
36 track_original_files.append(os.path.realpath(os.path.join(deploydir, image_file)))
37 self.append_config("RM_OLD_IMAGE = \"1\"")
38 bitbake("-C rootfs core-image-minimal")
39 deploydir_files = os.listdir(deploydir)
40 remaining_not_expected = [path for path in track_original_files if os.path.basename(path) in deploydir_files]
41 self.assertFalse(remaining_not_expected, msg="\nThe following image files ware not removed: %s" % ', '.join(map(str, remaining_not_expected)))
42
43 @testcase(286)
44 def test_ccache_tool(self):
45 bitbake("ccache-native")
46 self.assertTrue(os.path.isfile(os.path.join(get_bb_var('STAGING_BINDIR_NATIVE', 'ccache-native'), "ccache")))
47 self.write_config('INHERIT += "ccache"')
48 bitbake("m4 -c cleansstate")
49 bitbake("m4 -c compile")
50 res = runCmd("grep ccache %s" % (os.path.join(get_bb_var("WORKDIR","m4"),"temp/log.do_compile")), ignore_status=True)
51 self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile")
52 bitbake("ccache-native -ccleansstate")
53
54
55class DiskMonTest(oeSelfTest):
56
57 @testcase(277)
58 def test_stoptask_behavior(self):
59 self.write_config('BB_DISKMON_DIRS = "STOPTASKS,${TMPDIR},100000G,100K"')
60 res = bitbake("m4", ignore_status = True)
61 self.assertTrue('ERROR: No new tasks can be executed since the disk space monitor action is "STOPTASKS"!' in res.output)
62 self.assertEqual(res.status, 1)
63 self.write_config('BB_DISKMON_DIRS = "ABORT,${TMPDIR},100000G,100K"')
64 res = bitbake("m4", ignore_status = True)
65 self.assertTrue('ERROR: Immediately abort since the disk space monitor action is "ABORT"!' in res.output)
66 self.assertEqual(res.status, 1)
67 self.write_config('BB_DISKMON_DIRS = "WARN,${TMPDIR},100000G,100K"')
68 res = bitbake("m4")
69 self.assertTrue('WARNING: The free space' in res.output)
70
71class SanityOptionsTest(oeSelfTest):
72
73 @testcase(927)
74 def test_options_warnqa_errorqa_switch(self):
75 bitbake("xcursor-transparent-theme -ccleansstate")
76
77 if "packages-list" not in get_bb_var("ERROR_QA"):
78 self.write_config("ERROR_QA_append = \" packages-list\"")
79
80 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
81 res = bitbake("xcursor-transparent-theme", ignore_status=True)
82 self.delete_recipeinc('xcursor-transparent-theme')
83 self.assertTrue("ERROR: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output, msg=res.output)
84 self.assertEqual(res.status, 1)
85 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
86 self.append_config('ERROR_QA_remove = "packages-list"')
87 self.append_config('WARN_QA_append = " packages-list"')
88 bitbake("xcursor-transparent-theme -ccleansstate")
89 res = bitbake("xcursor-transparent-theme")
90 self.delete_recipeinc('xcursor-transparent-theme')
91 self.assertTrue("WARNING: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output, msg=res.output)
92
93 @testcase(278)
94 def test_sanity_userspace_dependency(self):
95 self.append_config('WARN_QA_append = " unsafe-references-in-binaries unsafe-references-in-scripts"')
96 bitbake("-ccleansstate gzip nfs-utils")
97 res = bitbake("gzip nfs-utils")
98 self.assertTrue("WARNING: QA Issue: gzip" in res.output)
99 self.assertTrue("WARNING: QA Issue: nfs-utils" in res.output)
100
101class BuildhistoryTests(BuildhistoryBase):
102
103 @testcase(293)
104 def test_buildhistory_basic(self):
105 self.run_buildhistory_operation('xcursor-transparent-theme')
106 self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')))
107
108 @testcase(294)
109 def test_buildhistory_buildtime_pr_backwards(self):
110 self.add_command_to_tearDown('cleanup-workdir')
111 target = 'xcursor-transparent-theme'
112 error = "ERROR: QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1 to .*-r0)" % target
113 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
114 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error)
115
116
117
118
119
120
diff --git a/meta/lib/oeqa/selftest/oescripts.py b/meta/lib/oeqa/selftest/oescripts.py
new file mode 100644
index 0000000000..31cd50809c
--- /dev/null
+++ b/meta/lib/oeqa/selftest/oescripts.py
@@ -0,0 +1,54 @@
1import datetime
2import unittest
3import os
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.selftest.buildhistory import BuildhistoryBase
10from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer
11from oeqa.utils.decorators import testcase
12
13class TestScripts(oeSelfTest):
14
15 @testcase(300)
16 def test_cleanup_workdir(self):
17 path = os.path.dirname(get_bb_var('WORKDIR', 'gzip'))
18 old_version_recipe = os.path.join(get_bb_var('COREBASE'), 'meta/recipes-extended/gzip/gzip_1.3.12.bb')
19 old_version = '1.3.12'
20 bitbake("-ccleansstate gzip")
21 bitbake("-ccleansstate -b %s" % old_version_recipe)
22 if os.path.exists(get_bb_var('WORKDIR', "-b %s" % old_version_recipe)):
23 shutil.rmtree(get_bb_var('WORKDIR', "-b %s" % old_version_recipe))
24 if os.path.exists(get_bb_var('WORKDIR', 'gzip')):
25 shutil.rmtree(get_bb_var('WORKDIR', 'gzip'))
26
27 if os.path.exists(path):
28 initial_contents = os.listdir(path)
29 else:
30 initial_contents = []
31
32 bitbake('gzip')
33 intermediary_contents = os.listdir(path)
34 bitbake("-b %s" % old_version_recipe)
35 runCmd('cleanup-workdir')
36 remaining_contents = os.listdir(path)
37
38 expected_contents = [x for x in intermediary_contents if x not in initial_contents]
39 remaining_not_expected = [x for x in remaining_contents if x not in expected_contents]
40 self.assertFalse(remaining_not_expected, msg="Not all necessary content has been deleted from %s: %s" % (path, ', '.join(map(str, remaining_not_expected))))
41 expected_not_remaining = [x for x in expected_contents if x not in remaining_contents]
42 self.assertFalse(expected_not_remaining, msg="The script removed extra contents from %s: %s" % (path, ', '.join(map(str, expected_not_remaining))))
43
44class BuildhistoryDiffTests(BuildhistoryBase):
45
46 @testcase(295)
47 def test_buildhistory_diff(self):
48 self.add_command_to_tearDown('cleanup-workdir')
49 target = 'xcursor-transparent-theme'
50 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
51 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True)
52 result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR'))
53 expected_output = 'PR changed from "r1" to "r0"'
54 self.assertTrue(expected_output in result.output, msg="Did not find expected output: %s" % result.output)
diff --git a/meta/lib/oeqa/selftest/prservice.py b/meta/lib/oeqa/selftest/prservice.py
new file mode 100644
index 0000000000..fb6d68d3bf
--- /dev/null
+++ b/meta/lib/oeqa/selftest/prservice.py
@@ -0,0 +1,121 @@
1import unittest
2import os
3import logging
4import re
5import shutil
6import datetime
7
8import oeqa.utils.ftools as ftools
9from oeqa.selftest.base import oeSelfTest
10from oeqa.utils.commands import runCmd, bitbake, get_bb_var
11from oeqa.utils.decorators import testcase
12
13class BitbakePrTests(oeSelfTest):
14
15 def get_pr_version(self, package_name):
16 pkgdata_dir = get_bb_var('PKGDATA_DIR')
17 package_data_file = os.path.join(pkgdata_dir, 'runtime', package_name)
18 package_data = ftools.read_file(package_data_file)
19 find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data)
20 self.assertTrue(find_pr)
21 return int(find_pr.group(1))
22
23 def get_task_stamp(self, package_name, recipe_task):
24 stampdata = get_bb_var('STAMP', target=package_name).split('/')
25 prefix = stampdata[-1]
26 package_stamps_path = "/".join(stampdata[:-1])
27 stamps = []
28 for stamp in os.listdir(package_stamps_path):
29 find_stamp = re.match("%s\.%s\.([a-z0-9]{32})" % (prefix, recipe_task), stamp)
30 if find_stamp:
31 stamps.append(find_stamp.group(1))
32 self.assertFalse(len(stamps) == 0, msg="Cound not find stamp for task %s for recipe %s" % (recipe_task, package_name))
33 self.assertFalse(len(stamps) > 1, msg="Found multiple %s stamps for the %s recipe in the %s directory." % (recipe_task, package_name, package_stamps_path))
34 return str(stamps[0])
35
36 def increment_package_pr(self, package_name):
37 inc_data = "do_package_append() {\nbb.build.exec_func('do_test_prserv', d)\n}\ndo_test_prserv() {\necho \"The current date is: %s\"\n}" % datetime.datetime.now()
38 self.write_recipeinc(package_name, inc_data)
39 bitbake("-ccleansstate %s" % package_name)
40 res = bitbake(package_name, ignore_status=True)
41 self.delete_recipeinc(package_name)
42 self.assertEqual(res.status, 0, msg=res.output)
43 self.assertTrue("NOTE: Started PRServer with DBfile" in res.output, msg=res.output)
44
45 def config_pr_tests(self, package_name, package_type='rpm', pr_socket='localhost:0'):
46 config_package_data = 'PACKAGE_CLASSES = "package_%s"' % package_type
47 self.write_config(config_package_data)
48 config_server_data = 'PRSERV_HOST = "%s"' % pr_socket
49 self.append_config(config_server_data)
50
51 def run_test_pr_service(self, package_name, package_type='rpm', track_task='do_package', pr_socket='localhost:0'):
52 self.config_pr_tests(package_name, package_type, pr_socket)
53
54 self.increment_package_pr(package_name)
55 pr_1 = self.get_pr_version(package_name)
56 stamp_1 = self.get_task_stamp(package_name, track_task)
57
58 self.increment_package_pr(package_name)
59 pr_2 = self.get_pr_version(package_name)
60 stamp_2 = self.get_task_stamp(package_name, track_task)
61
62 bitbake("-ccleansstate %s" % package_name)
63 self.assertTrue(pr_2 - pr_1 == 1)
64 self.assertTrue(stamp_1 != stamp_2)
65
66 def run_test_pr_export_import(self, package_name, replace_current_db=True):
67 self.config_pr_tests(package_name)
68
69 self.increment_package_pr(package_name)
70 pr_1 = self.get_pr_version(package_name)
71
72 exported_db_path = os.path.join(self.builddir, 'export.inc')
73 export_result = runCmd("bitbake-prserv-tool export %s" % exported_db_path, ignore_status=True)
74 self.assertEqual(export_result.status, 0, msg="PR Service database export failed: %s" % export_result.output)
75
76 if replace_current_db:
77 current_db_path = os.path.join(get_bb_var('PERSISTENT_DIR'), 'prserv.sqlite3')
78 self.assertTrue(os.path.exists(current_db_path), msg="Path to current PR Service database is invalid: %s" % current_db_path)
79 os.remove(current_db_path)
80
81 import_result = runCmd("bitbake-prserv-tool import %s" % exported_db_path, ignore_status=True)
82 os.remove(exported_db_path)
83 self.assertEqual(import_result.status, 0, msg="PR Service database import failed: %s" % import_result.output)
84
85 self.increment_package_pr(package_name)
86 pr_2 = self.get_pr_version(package_name)
87
88 bitbake("-ccleansstate %s" % package_name)
89 self.assertTrue(pr_2 - pr_1 == 1)
90
91 @testcase(930)
92 def test_import_export_replace_db(self):
93 self.run_test_pr_export_import('m4')
94
95 @testcase(931)
96 def test_import_export_override_db(self):
97 self.run_test_pr_export_import('m4', replace_current_db=False)
98
99 @testcase(932)
100 def test_pr_service_rpm_arch_dep(self):
101 self.run_test_pr_service('m4', 'rpm', 'do_package')
102
103 @testcase(934)
104 def test_pr_service_deb_arch_dep(self):
105 self.run_test_pr_service('m4', 'deb', 'do_package')
106
107 @testcase(933)
108 def test_pr_service_ipk_arch_dep(self):
109 self.run_test_pr_service('m4', 'ipk', 'do_package')
110
111 @testcase(935)
112 def test_pr_service_rpm_arch_indep(self):
113 self.run_test_pr_service('xcursor-transparent-theme', 'rpm', 'do_package')
114
115 @testcase(937)
116 def test_pr_service_deb_arch_indep(self):
117 self.run_test_pr_service('xcursor-transparent-theme', 'deb', 'do_package')
118
119 @testcase(936)
120 def test_pr_service_ipk_arch_indep(self):
121 self.run_test_pr_service('xcursor-transparent-theme', 'ipk', 'do_package')
diff --git a/meta/lib/oeqa/selftest/sstate.py b/meta/lib/oeqa/selftest/sstate.py
new file mode 100644
index 0000000000..5989724432
--- /dev/null
+++ b/meta/lib/oeqa/selftest/sstate.py
@@ -0,0 +1,53 @@
1import datetime
2import unittest
3import os
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
10
11
12class SStateBase(oeSelfTest):
13
14 def setUpLocal(self):
15 self.temp_sstate_location = None
16 self.sstate_path = get_bb_var('SSTATE_DIR')
17 self.distro = get_bb_var('NATIVELSBSTRING')
18 self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro)
19
20 # Creates a special sstate configuration with the option to add sstate mirrors
21 def config_sstate(self, temp_sstate_location=False, add_local_mirrors=[]):
22 self.temp_sstate_location = temp_sstate_location
23
24 if self.temp_sstate_location:
25 temp_sstate_path = os.path.join(self.builddir, "temp_sstate_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
26 config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path
27 self.append_config(config_temp_sstate)
28 self.track_for_cleanup(temp_sstate_path)
29 self.sstate_path = get_bb_var('SSTATE_DIR')
30 self.distro = get_bb_var('NATIVELSBSTRING')
31 self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro)
32
33 if add_local_mirrors:
34 config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""'
35 self.append_config(config_set_sstate_if_not_set)
36 for local_mirror in add_local_mirrors:
37 self.assertFalse(os.path.join(local_mirror) == os.path.join(self.sstate_path), msg='Cannot add the current sstate path as a sstate mirror')
38 config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror
39 self.append_config(config_sstate_mirror)
40
41 # Returns a list containing sstate files
42 def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True):
43 result = []
44 for root, dirs, files in os.walk(self.sstate_path):
45 if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.distro, root):
46 for f in files:
47 if re.search(filename_regex, f):
48 result.append(f)
49 if distro_nonspecific and re.search("%s/[a-z0-9]{2}$" % self.sstate_path, root):
50 for f in files:
51 if re.search(filename_regex, f):
52 result.append(f)
53 return result
diff --git a/meta/lib/oeqa/selftest/sstatetests.py b/meta/lib/oeqa/selftest/sstatetests.py
new file mode 100644
index 0000000000..d578ddd489
--- /dev/null
+++ b/meta/lib/oeqa/selftest/sstatetests.py
@@ -0,0 +1,204 @@
1import datetime
2import unittest
3import os
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.base import oeSelfTest
9from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
10from oeqa.selftest.sstate import SStateBase
11from oeqa.utils.decorators import testcase
12
13class SStateTests(SStateBase):
14
15 # Test sstate files creation and their location
16 def run_test_sstate_creation(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True, should_pass=True):
17 self.config_sstate(temp_sstate_location)
18
19 if self.temp_sstate_location:
20 bitbake(['-cclean'] + targets)
21 else:
22 bitbake(['-ccleansstate'] + targets)
23
24 bitbake(targets)
25 file_tracker = self.search_sstate('|'.join(map(str, targets)), distro_specific, distro_nonspecific)
26 if should_pass:
27 self.assertTrue(file_tracker , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets)))
28 else:
29 self.assertTrue(not file_tracker , msg="Found sstate files in the wrong place for: %s" % ', '.join(map(str, targets)))
30
31 @testcase(975)
32 def test_sstate_creation_distro_specific_pass(self):
33 targetarch = get_bb_var('TUNE_ARCH')
34 self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
35
36 @testcase(975)
37 def test_sstate_creation_distro_specific_fail(self):
38 targetarch = get_bb_var('TUNE_ARCH')
39 self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False)
40
41 @testcase(976)
42 def test_sstate_creation_distro_nonspecific_pass(self):
43 self.run_test_sstate_creation(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
44
45 @testcase(976)
46 def test_sstate_creation_distro_nonspecific_fail(self):
47 self.run_test_sstate_creation(['glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
48
49
50 # Test the sstate files deletion part of the do_cleansstate task
51 def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True):
52 self.config_sstate(temp_sstate_location)
53
54 bitbake(['-ccleansstate'] + targets)
55
56 bitbake(targets)
57 tgz_created = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
58 self.assertTrue(tgz_created, msg="Could not find sstate .tgz files for: %s" % ', '.join(map(str, targets)))
59
60 siginfo_created = self.search_sstate('|'.join(map(str, [s + '.*?\.siginfo$' for s in targets])), distro_specific, distro_nonspecific)
61 self.assertTrue(siginfo_created, msg="Could not find sstate .siginfo files for: %s" % ', '.join(map(str, targets)))
62
63 bitbake(['-ccleansstate'] + targets)
64 tgz_removed = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
65 self.assertTrue(not tgz_removed, msg="do_cleansstate didn't remove .tgz sstate files for: %s" % ', '.join(map(str, targets)))
66
67 @testcase(977)
68 def test_cleansstate_task_distro_specific_nonspecific(self):
69 targetarch = get_bb_var('TUNE_ARCH')
70 self.run_test_cleansstate_task(['binutils-cross-' + targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
71
72 @testcase(977)
73 def test_cleansstate_task_distro_nonspecific(self):
74 self.run_test_cleansstate_task(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
75
76 @testcase(977)
77 def test_cleansstate_task_distro_specific(self):
78 targetarch = get_bb_var('TUNE_ARCH')
79 self.run_test_cleansstate_task(['binutils-cross-'+ targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
80
81
82 # Test rebuilding of distro-specific sstate files
83 def run_test_rebuild_distro_specific_sstate(self, targets, temp_sstate_location=True):
84 self.config_sstate(temp_sstate_location)
85
86 bitbake(['-ccleansstate'] + targets)
87
88 bitbake(targets)
89 self.assertTrue(self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=False, distro_nonspecific=True) == [], msg="Found distro non-specific sstate for: %s" % ', '.join(map(str, targets)))
90 file_tracker_1 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
91 self.assertTrue(len(file_tracker_1) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
92
93 self.track_for_cleanup(self.distro_specific_sstate + "_old")
94 shutil.copytree(self.distro_specific_sstate, self.distro_specific_sstate + "_old")
95 shutil.rmtree(self.distro_specific_sstate)
96
97 bitbake(['-cclean'] + targets)
98 bitbake(targets)
99 file_tracker_2 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
100 self.assertTrue(len(file_tracker_2) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
101
102 not_recreated = [x for x in file_tracker_1 if x not in file_tracker_2]
103 self.assertTrue(not_recreated == [], msg="The following sstate files ware not recreated: %s" % ', '.join(map(str, not_recreated)))
104
105 created_once = [x for x in file_tracker_2 if x not in file_tracker_1]
106 self.assertTrue(created_once == [], msg="The following sstate files ware created only in the second run: %s" % ', '.join(map(str, created_once)))
107
108 @testcase(175)
109 def test_rebuild_distro_specific_sstate_cross_native_targets(self):
110 targetarch = get_bb_var('TUNE_ARCH')
111 self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch, 'binutils-native'], temp_sstate_location=True)
112
113 @testcase(175)
114 def test_rebuild_distro_specific_sstate_cross_target(self):
115 targetarch = get_bb_var('TUNE_ARCH')
116 self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch], temp_sstate_location=True)
117
118 @testcase(175)
119 def test_rebuild_distro_specific_sstate_native_target(self):
120 self.run_test_rebuild_distro_specific_sstate(['binutils-native'], temp_sstate_location=True)
121
122
123 # Test the sstate-cache-management script. Each element in the global_config list is used with the corresponding element in the target_config list
124 # global_config elements are expected to not generate any sstate files that would be removed by sstate-cache-management.sh (such as changing the value of MACHINE)
125 def run_test_sstate_cache_management_script(self, target, global_config=[''], target_config=[''], ignore_patterns=[]):
126 self.assertTrue(global_config)
127 self.assertTrue(target_config)
128 self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements')
129 self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path])
130
131 # If buildhistory is enabled, we need to disable version-going-backwards QA checks for this test. It may report errors otherwise.
132 if ('buildhistory' in get_bb_var('USER_CLASSES')) or ('buildhistory' in get_bb_var('INHERIT')):
133 remove_errors_config = 'ERROR_QA_remove = "version-going-backwards"'
134 self.append_config(remove_errors_config)
135
136 # For not this only checks if random sstate tasks are handled correctly as a group.
137 # In the future we should add control over what tasks we check for.
138
139 sstate_archs_list = []
140 expected_remaining_sstate = []
141 for idx in range(len(target_config)):
142 self.append_config(global_config[idx])
143 self.append_recipeinc(target, target_config[idx])
144 sstate_arch = get_bb_var('SSTATE_PKGARCH', target)
145 if not sstate_arch in sstate_archs_list:
146 sstate_archs_list.append(sstate_arch)
147 if target_config[idx] == target_config[-1]:
148 target_sstate_before_build = self.search_sstate(target + '.*?\.tgz$')
149 bitbake("-cclean %s" % target)
150 result = bitbake(target, ignore_status=True)
151 if target_config[idx] == target_config[-1]:
152 target_sstate_after_build = self.search_sstate(target + '.*?\.tgz$')
153 expected_remaining_sstate += [x for x in target_sstate_after_build if x not in target_sstate_before_build if not any(pattern in x for pattern in ignore_patterns)]
154 self.remove_config(global_config[idx])
155 self.remove_recipeinc(target, target_config[idx])
156 self.assertEqual(result.status, 0)
157
158 runCmd("sstate-cache-management.sh -y --cache-dir=%s --remove-duplicated --extra-archs=%s" % (self.sstate_path, ','.join(map(str, sstate_archs_list))))
159 actual_remaining_sstate = [x for x in self.search_sstate(target + '.*?\.tgz$') if not any(pattern in x for pattern in ignore_patterns)]
160
161 actual_not_expected = [x for x in actual_remaining_sstate if x not in expected_remaining_sstate]
162 self.assertFalse(actual_not_expected, msg="Files should have been removed but ware not: %s" % ', '.join(map(str, actual_not_expected)))
163 expected_not_actual = [x for x in expected_remaining_sstate if x not in actual_remaining_sstate]
164 self.assertFalse(expected_not_actual, msg="Extra files ware removed: %s" ', '.join(map(str, expected_not_actual)))
165
166 @testcase(973)
167 def test_sstate_cache_management_script_using_pr_1(self):
168 global_config = []
169 target_config = []
170 global_config.append('')
171 target_config.append('PR = "0"')
172 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
173
174 @testcase(978)
175 def test_sstate_cache_management_script_using_pr_2(self):
176 global_config = []
177 target_config = []
178 global_config.append('')
179 target_config.append('PR = "0"')
180 global_config.append('')
181 target_config.append('PR = "1"')
182 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
183
184 @testcase(979)
185 def test_sstate_cache_management_script_using_pr_3(self):
186 global_config = []
187 target_config = []
188 global_config.append('MACHINE = "qemux86-64"')
189 target_config.append('PR = "0"')
190 global_config.append(global_config[0])
191 target_config.append('PR = "1"')
192 global_config.append('MACHINE = "qemux86"')
193 target_config.append('PR = "1"')
194 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
195
196 @testcase(974)
197 def test_sstate_cache_management_script_using_machine(self):
198 global_config = []
199 target_config = []
200 global_config.append('MACHINE = "qemux86-64"')
201 target_config.append('')
202 global_config.append('MACHINE = "qemux86"')
203 target_config.append('')
204 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
diff --git a/meta/lib/oeqa/targetcontrol.py b/meta/lib/oeqa/targetcontrol.py
new file mode 100644
index 0000000000..cc582dd1ad
--- /dev/null
+++ b/meta/lib/oeqa/targetcontrol.py
@@ -0,0 +1,199 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module is used by testimage.bbclass for setting up and controlling a target machine.
6
7import os
8import shutil
9import subprocess
10import bb
11import traceback
12import sys
13from oeqa.utils.sshcontrol import SSHControl
14from oeqa.utils.qemurunner import QemuRunner
15from oeqa.controllers.testtargetloader import TestTargetLoader
16from abc import ABCMeta, abstractmethod
17
18def get_target_controller(d):
19 testtarget = d.getVar("TEST_TARGET", True)
20 # old, simple names
21 if testtarget == "qemu":
22 return QemuTarget(d)
23 elif testtarget == "simpleremote":
24 return SimpleRemoteTarget(d)
25 else:
26 # use the class name
27 try:
28 # is it a core class defined here?
29 controller = getattr(sys.modules[__name__], testtarget)
30 except AttributeError:
31 # nope, perhaps a layer defined one
32 try:
33 bbpath = d.getVar("BBPATH", True).split(':')
34 testtargetloader = TestTargetLoader()
35 controller = testtargetloader.get_controller_module(testtarget, bbpath)
36 except ImportError as e:
37 bb.fatal("Failed to import {0} from available controller modules:\n{1}".format(testtarget,traceback.format_exc()))
38 except AttributeError as e:
39 bb.fatal("Invalid TEST_TARGET - " + str(e))
40 return controller(d)
41
42
43class BaseTarget(object):
44
45 __metaclass__ = ABCMeta
46
47 supported_image_fstypes = []
48
49 def __init__(self, d):
50 self.connection = None
51 self.ip = None
52 self.server_ip = None
53 self.datetime = d.getVar('DATETIME', True)
54 self.testdir = d.getVar("TEST_LOG_DIR", True)
55 self.pn = d.getVar("PN", True)
56
57 @abstractmethod
58 def deploy(self):
59
60 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
61 sshloglink = os.path.join(self.testdir, "ssh_target_log")
62 if os.path.islink(sshloglink):
63 os.unlink(sshloglink)
64 os.symlink(self.sshlog, sshloglink)
65 bb.note("SSH log file: %s" % self.sshlog)
66
67 @abstractmethod
68 def start(self, params=None):
69 pass
70
71 @abstractmethod
72 def stop(self):
73 pass
74
75 @classmethod
76 def get_extra_files(self):
77 return None
78
79 @classmethod
80 def match_image_fstype(self, d, image_fstypes=None):
81 if not image_fstypes:
82 image_fstypes = d.getVar('IMAGE_FSTYPES', True).split(' ')
83 possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes]
84 if possible_image_fstypes:
85 return possible_image_fstypes[0]
86 else:
87 return None
88
89 def get_image_fstype(self, d):
90 image_fstype = self.match_image_fstype(d)
91 if image_fstype:
92 return image_fstype
93 else:
94 bb.fatal("IMAGE_FSTYPES should contain a Target Controller supported image fstype: %s " % ', '.join(map(str, self.supported_image_fstypes)))
95
96 def restart(self, params=None):
97 self.stop()
98 self.start(params)
99
100 def run(self, cmd, timeout=None):
101 return self.connection.run(cmd, timeout)
102
103 def copy_to(self, localpath, remotepath):
104 return self.connection.copy_to(localpath, remotepath)
105
106 def copy_from(self, remotepath, localpath):
107 return self.connection.copy_from(remotepath, localpath)
108
109
110
111class QemuTarget(BaseTarget):
112
113 supported_image_fstypes = ['ext3']
114
115 def __init__(self, d):
116
117 super(QemuTarget, self).__init__(d)
118
119 self.image_fstype = self.get_image_fstype(d)
120 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
121 self.origrootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
122 self.rootfs = os.path.join(self.testdir, d.getVar("IMAGE_LINK_NAME", True) + '-testimage.' + self.image_fstype)
123
124 self.runner = QemuRunner(machine=d.getVar("MACHINE", True),
125 rootfs=self.rootfs,
126 tmpdir = d.getVar("TMPDIR", True),
127 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
128 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
129 logfile = self.qemulog,
130 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)))
131
132 def deploy(self):
133 try:
134 shutil.copyfile(self.origrootfs, self.rootfs)
135 except Exception as e:
136 bb.fatal("Error copying rootfs: %s" % e)
137
138 qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
139 if os.path.islink(qemuloglink):
140 os.unlink(qemuloglink)
141 os.symlink(self.qemulog, qemuloglink)
142
143 bb.note("rootfs file: %s" % self.rootfs)
144 bb.note("Qemu log file: %s" % self.qemulog)
145 super(QemuTarget, self).deploy()
146
147 def start(self, params=None):
148 if self.runner.start(params):
149 self.ip = self.runner.ip
150 self.server_ip = self.runner.server_ip
151 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
152 else:
153 self.stop()
154 raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
155
156 def stop(self):
157 self.runner.stop()
158 self.connection = None
159 self.ip = None
160 self.server_ip = None
161
162 def restart(self, params=None):
163 if self.runner.restart(params):
164 self.ip = self.runner.ip
165 self.server_ip = self.runner.server_ip
166 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
167 else:
168 raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
169
170
171class SimpleRemoteTarget(BaseTarget):
172
173 def __init__(self, d):
174 super(SimpleRemoteTarget, self).__init__(d)
175 addr = d.getVar("TEST_TARGET_IP", True) or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
176 self.ip = addr.split(":")[0]
177 try:
178 self.port = addr.split(":")[1]
179 except IndexError:
180 self.port = None
181 bb.note("Target IP: %s" % self.ip)
182 self.server_ip = d.getVar("TEST_SERVER_IP", True)
183 if not self.server_ip:
184 try:
185 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
186 except Exception as e:
187 bb.fatal("Failed to determine the host IP address (alternatively you can set TEST_SERVER_IP with the IP address of this machine): %s" % e)
188 bb.note("Server IP: %s" % self.server_ip)
189
190 def deploy(self):
191 super(SimpleRemoteTarget, self).deploy()
192
193 def start(self, params=None):
194 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
195
196 def stop(self):
197 self.connection = None
198 self.ip = None
199 self.server_ip = None
diff --git a/meta/lib/oeqa/utils/__init__.py b/meta/lib/oeqa/utils/__init__.py
new file mode 100644
index 0000000000..2260046026
--- /dev/null
+++ b/meta/lib/oeqa/utils/__init__.py
@@ -0,0 +1,15 @@
1# Enable other layers to have modules in the same named directory
2from pkgutil import extend_path
3__path__ = extend_path(__path__, __name__)
4
5
6# Borrowed from CalledProcessError
7
8class CommandError(Exception):
9 def __init__(self, retcode, cmd, output = None):
10 self.retcode = retcode
11 self.cmd = cmd
12 self.output = output
13 def __str__(self):
14 return "Command '%s' returned non-zero exit status %d with output: %s" % (self.cmd, self.retcode, self.output)
15
diff --git a/meta/lib/oeqa/utils/commands.py b/meta/lib/oeqa/utils/commands.py
new file mode 100644
index 0000000000..802bc2f208
--- /dev/null
+++ b/meta/lib/oeqa/utils/commands.py
@@ -0,0 +1,154 @@
1# Copyright (c) 2013-2014 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# DESCRIPTION
6# This module is mainly used by scripts/oe-selftest and modules under meta/oeqa/selftest
7# It provides a class and methods for running commands on the host in a convienent way for tests.
8
9
10
11import os
12import sys
13import signal
14import subprocess
15import threading
16import logging
17from oeqa.utils import CommandError
18from oeqa.utils import ftools
19
20class Command(object):
21 def __init__(self, command, bg=False, timeout=None, data=None, **options):
22
23 self.defaultopts = {
24 "stdout": subprocess.PIPE,
25 "stderr": subprocess.STDOUT,
26 "stdin": None,
27 "shell": False,
28 "bufsize": -1,
29 }
30
31 self.cmd = command
32 self.bg = bg
33 self.timeout = timeout
34 self.data = data
35
36 self.options = dict(self.defaultopts)
37 if isinstance(self.cmd, basestring):
38 self.options["shell"] = True
39 if self.data:
40 self.options['stdin'] = subprocess.PIPE
41 self.options.update(options)
42
43 self.status = None
44 self.output = None
45 self.error = None
46 self.thread = None
47
48 self.log = logging.getLogger("utils.commands")
49
50 def run(self):
51 self.process = subprocess.Popen(self.cmd, **self.options)
52
53 def commThread():
54 self.output, self.error = self.process.communicate(self.data)
55
56 self.thread = threading.Thread(target=commThread)
57 self.thread.start()
58
59 self.log.debug("Running command '%s'" % self.cmd)
60
61 if not self.bg:
62 self.thread.join(self.timeout)
63 self.stop()
64
65 def stop(self):
66 if self.thread.isAlive():
67 self.process.terminate()
68 # let's give it more time to terminate gracefully before killing it
69 self.thread.join(5)
70 if self.thread.isAlive():
71 self.process.kill()
72 self.thread.join()
73
74 self.output = self.output.rstrip()
75 self.status = self.process.poll()
76
77 self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status))
78 # logging the complete output is insane
79 # bitbake -e output is really big
80 # and makes the log file useless
81 if self.status:
82 lout = "\n".join(self.output.splitlines()[-20:])
83 self.log.debug("Last 20 lines:\n%s" % lout)
84
85
86class Result(object):
87 pass
88
89
90def runCmd(command, ignore_status=False, timeout=None, assert_error=True, **options):
91 result = Result()
92
93 cmd = Command(command, timeout=timeout, **options)
94 cmd.run()
95
96 result.command = command
97 result.status = cmd.status
98 result.output = cmd.output
99 result.pid = cmd.process.pid
100
101 if result.status and not ignore_status:
102 if assert_error:
103 raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, result.output))
104 else:
105 raise CommandError(result.status, command, result.output)
106
107 return result
108
109
110def bitbake(command, ignore_status=False, timeout=None, postconfig=None, **options):
111
112 if postconfig:
113 postconfig_file = os.path.join(os.environ.get('BUILDDIR'), 'oeqa-post.conf')
114 ftools.write_file(postconfig_file, postconfig)
115 extra_args = "-R %s" % postconfig_file
116 else:
117 extra_args = ""
118
119 if isinstance(command, basestring):
120 cmd = "bitbake " + extra_args + " " + command
121 else:
122 cmd = [ "bitbake" ] + [a for a in (command + extra_args.split(" ")) if a not in [""]]
123
124 try:
125 return runCmd(cmd, ignore_status, timeout, **options)
126 finally:
127 if postconfig:
128 os.remove(postconfig_file)
129
130
131def get_bb_env(target=None, postconfig=None):
132 if target:
133 return bitbake("-e %s" % target, postconfig=postconfig).output
134 else:
135 return bitbake("-e", postconfig=postconfig).output
136
137def get_bb_var(var, target=None, postconfig=None):
138 val = None
139 bbenv = get_bb_env(target, postconfig=postconfig)
140 for line in bbenv.splitlines():
141 if line.startswith(var + "="):
142 val = line.split('=')[1]
143 val = val.replace('\"','')
144 break
145 return val
146
147def get_test_layer():
148 layers = get_bb_var("BBLAYERS").split()
149 testlayer = None
150 for l in layers:
151 if "/meta-selftest" in l and os.path.isdir(l):
152 testlayer = l
153 break
154 return testlayer
diff --git a/meta/lib/oeqa/utils/decorators.py b/meta/lib/oeqa/utils/decorators.py
new file mode 100644
index 0000000000..40bd4ef2db
--- /dev/null
+++ b/meta/lib/oeqa/utils/decorators.py
@@ -0,0 +1,158 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Some custom decorators that can be used by unittests
6# Most useful is skipUnlessPassed which can be used for
7# creating dependecies between two test methods.
8
9import os
10import logging
11import sys
12import unittest
13
14#get the "result" object from one of the upper frames provided that one of these upper frames is a unittest.case frame
15class getResults(object):
16 def __init__(self):
17 #dynamically determine the unittest.case frame and use it to get the name of the test method
18 upperf = sys._current_frames().values()[0]
19 while (upperf.f_globals['__name__'] != 'unittest.case'):
20 upperf = upperf.f_back
21
22 def handleList(items):
23 ret = []
24 # items is a list of tuples, (test, failure) or (_ErrorHandler(), Exception())
25 for i in items:
26 s = i[0].id()
27 #Handle the _ErrorHolder objects from skipModule failures
28 if "setUpModule (" in s:
29 ret.append(s.replace("setUpModule (", "").replace(")",""))
30 else:
31 ret.append(s)
32 return ret
33 self.faillist = handleList(upperf.f_locals['result'].failures)
34 self.errorlist = handleList(upperf.f_locals['result'].errors)
35 self.skiplist = handleList(upperf.f_locals['result'].skipped)
36
37 def getFailList(self):
38 return self.faillist
39
40 def getErrorList(self):
41 return self.errorlist
42
43 def getSkipList(self):
44 return self.skiplist
45
46class skipIfFailure(object):
47
48 def __init__(self,testcase):
49 self.testcase = testcase
50
51 def __call__(self,f):
52 def wrapped_f(*args):
53 res = getResults()
54 if self.testcase in (res.getFailList() or res.getErrorList()):
55 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
56 return f(*args)
57 wrapped_f.__name__ = f.__name__
58 return wrapped_f
59
60class skipIfSkipped(object):
61
62 def __init__(self,testcase):
63 self.testcase = testcase
64
65 def __call__(self,f):
66 def wrapped_f(*args):
67 res = getResults()
68 if self.testcase in res.getSkipList():
69 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
70 return f(*args)
71 wrapped_f.__name__ = f.__name__
72 return wrapped_f
73
74class skipUnlessPassed(object):
75
76 def __init__(self,testcase):
77 self.testcase = testcase
78
79 def __call__(self,f):
80 def wrapped_f(*args):
81 res = getResults()
82 if self.testcase in res.getSkipList() or \
83 self.testcase in res.getFailList() or \
84 self.testcase in res.getErrorList():
85 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
86 return f(*args)
87 wrapped_f.__name__ = f.__name__
88 return wrapped_f
89
90class testcase(object):
91
92 def __init__(self, test_case):
93 self.test_case = test_case
94
95 def __call__(self, func):
96 def wrapped_f(*args):
97 return func(*args)
98 wrapped_f.test_case = self.test_case
99 return wrapped_f
100
101class NoParsingFilter(logging.Filter):
102 def filter(self, record):
103 return record.levelno == 100
104
105def LogResults(original_class):
106 orig_method = original_class.run
107
108 #rewrite the run method of unittest.TestCase to add testcase logging
109 def run(self, result, *args, **kws):
110 orig_method(self, result, *args, **kws)
111 passed = True
112 testMethod = getattr(self, self._testMethodName)
113
114 #if test case is decorated then use it's number, else use it's name
115 try:
116 test_case = testMethod.test_case
117 except AttributeError:
118 test_case = self._testMethodName
119
120 #create custom logging level for filtering.
121 custom_log_level = 100
122 logging.addLevelName(custom_log_level, 'RESULTS')
123 caller = os.path.basename(sys.argv[0])
124
125 def results(self, message, *args, **kws):
126 if self.isEnabledFor(custom_log_level):
127 self.log(custom_log_level, message, *args, **kws)
128 logging.Logger.results = results
129
130 logging.basicConfig(filename=os.path.join(os.getcwd(),'results-'+caller+'.log'),
131 filemode='w',
132 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
133 datefmt='%H:%M:%S',
134 level=custom_log_level)
135 for handler in logging.root.handlers:
136 handler.addFilter(NoParsingFilter())
137 local_log = logging.getLogger(caller)
138
139 #check status of tests and record it
140 for (name, msg) in result.errors:
141 if self._testMethodName == str(name).split(' ')[0]:
142 local_log.results("Testcase "+str(test_case)+": ERROR")
143 local_log.results("Testcase "+str(test_case)+":\n"+msg)
144 passed = False
145 for (name, msg) in result.failures:
146 if self._testMethodName == str(name).split(' ')[0]:
147 local_log.results("Testcase "+str(test_case)+": FAILED")
148 local_log.results("Testcase "+str(test_case)+":\n"+msg)
149 passed = False
150 for (name, msg) in result.skipped:
151 if self._testMethodName == str(name).split(' ')[0]:
152 local_log.results("Testcase "+str(test_case)+": SKIPPED")
153 passed = False
154 if passed:
155 local_log.results("Testcase "+str(test_case)+": PASSED")
156
157 original_class.run = run
158 return original_class
diff --git a/meta/lib/oeqa/utils/ftools.py b/meta/lib/oeqa/utils/ftools.py
new file mode 100644
index 0000000000..64ebe3d217
--- /dev/null
+++ b/meta/lib/oeqa/utils/ftools.py
@@ -0,0 +1,27 @@
1import os
2import re
3
4def write_file(path, data):
5 wdata = data.rstrip() + "\n"
6 with open(path, "w") as f:
7 f.write(wdata)
8
9def append_file(path, data):
10 wdata = data.rstrip() + "\n"
11 with open(path, "a") as f:
12 f.write(wdata)
13
14def read_file(path):
15 data = None
16 with open(path) as f:
17 data = f.read()
18 return data
19
20def remove_from_file(path, data):
21 lines = read_file(path).splitlines()
22 rmdata = data.strip().splitlines()
23 for l in rmdata:
24 for c in range(0, lines.count(l)):
25 i = lines.index(l)
26 del(lines[i])
27 write_file(path, "\n".join(lines))
diff --git a/meta/lib/oeqa/utils/httpserver.py b/meta/lib/oeqa/utils/httpserver.py
new file mode 100644
index 0000000000..76518d8ef9
--- /dev/null
+++ b/meta/lib/oeqa/utils/httpserver.py
@@ -0,0 +1,35 @@
1import SimpleHTTPServer
2import multiprocessing
3import os
4
5class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer):
6
7 def server_start(self, root_dir):
8 import signal
9 signal.signal(signal.SIGTERM, signal.SIG_DFL)
10 os.chdir(root_dir)
11 self.serve_forever()
12
13class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
14
15 def log_message(self, format_str, *args):
16 pass
17
18class HTTPService(object):
19
20 def __init__(self, root_dir, host=''):
21 self.root_dir = root_dir
22 self.host = host
23 self.port = 0
24
25 def start(self):
26 self.server = HTTPServer((self.host, self.port), HTTPRequestHandler)
27 if self.port == 0:
28 self.port = self.server.server_port
29 self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir])
30 self.process.start()
31
32 def stop(self):
33 self.server.server_close()
34 self.process.terminate()
35 self.process.join()
diff --git a/meta/lib/oeqa/utils/logparser.py b/meta/lib/oeqa/utils/logparser.py
new file mode 100644
index 0000000000..87b50354cd
--- /dev/null
+++ b/meta/lib/oeqa/utils/logparser.py
@@ -0,0 +1,125 @@
1#!/usr/bin/env python
2
3import sys
4import os
5import re
6import ftools
7
8
9# A parser that can be used to identify weather a line is a test result or a section statement.
10class Lparser(object):
11
12 def __init__(self, test_0_pass_regex, test_0_fail_regex, section_0_begin_regex=None, section_0_end_regex=None, **kwargs):
13 # Initialize the arguments dictionary
14 if kwargs:
15 self.args = kwargs
16 else:
17 self.args = {}
18
19 # Add the default args to the dictionary
20 self.args['test_0_pass_regex'] = test_0_pass_regex
21 self.args['test_0_fail_regex'] = test_0_fail_regex
22 if section_0_begin_regex:
23 self.args['section_0_begin_regex'] = section_0_begin_regex
24 if section_0_end_regex:
25 self.args['section_0_end_regex'] = section_0_end_regex
26
27 self.test_possible_status = ['pass', 'fail', 'error']
28 self.section_possible_status = ['begin', 'end']
29
30 self.initialized = False
31
32
33 # Initialize the parser with the current configuration
34 def init(self):
35
36 # extra arguments can be added by the user to define new test and section categories. They must follow a pre-defined pattern: <type>_<category_name>_<status>_regex
37 self.test_argument_pattern = "^test_(.+?)_(%s)_regex" % '|'.join(map(str, self.test_possible_status))
38 self.section_argument_pattern = "^section_(.+?)_(%s)_regex" % '|'.join(map(str, self.section_possible_status))
39
40 # Initialize the test and section regex dictionaries
41 self.test_regex = {}
42 self.section_regex ={}
43
44 for arg, value in self.args.items():
45 if not value:
46 raise Exception('The value of provided argument %s is %s. Should have a valid value.' % (key, value))
47 is_test = re.search(self.test_argument_pattern, arg)
48 is_section = re.search(self.section_argument_pattern, arg)
49 if is_test:
50 if not is_test.group(1) in self.test_regex:
51 self.test_regex[is_test.group(1)] = {}
52 self.test_regex[is_test.group(1)][is_test.group(2)] = re.compile(value)
53 elif is_section:
54 if not is_section.group(1) in self.section_regex:
55 self.section_regex[is_section.group(1)] = {}
56 self.section_regex[is_section.group(1)][is_section.group(2)] = re.compile(value)
57 else:
58 # TODO: Make these call a traceback instead of a simple exception..
59 raise Exception("The provided argument name does not correspond to any valid type. Please give one of the following types:\nfor tests: %s\nfor sections: %s" % (self.test_argument_pattern, self.section_argument_pattern))
60
61 self.initialized = True
62
63 # Parse a line and return a tuple containing the type of result (test/section) and its category, status and name
64 def parse_line(self, line):
65 if not self.initialized:
66 raise Exception("The parser is not initialized..")
67
68 for test_category, test_status_list in self.test_regex.items():
69 for test_status, status_regex in test_status_list.items():
70 test_name = status_regex.search(line)
71 if test_name:
72 return ['test', test_category, test_status, test_name.group(1)]
73
74 for section_category, section_status_list in self.section_regex.items():
75 for section_status, status_regex in section_status_list.items():
76 section_name = status_regex.search(line)
77 if section_name:
78 return ['section', section_category, section_status, section_name.group(1)]
79 return None
80
81
82class Result(object):
83
84 def __init__(self):
85 self.result_dict = {}
86
87 def store(self, section, test, status):
88 if not section in self.result_dict:
89 self.result_dict[section] = []
90
91 self.result_dict[section].append((test, status))
92
93 # sort tests by the test name(the first element of the tuple), for each section. This can be helpful when using git to diff for changes by making sure they are always in the same order.
94 def sort_tests(self):
95 for package in self.result_dict:
96 sorted_results = sorted(self.result_dict[package], key=lambda tup: tup[0])
97 self.result_dict[package] = sorted_results
98
99 # Log the results as files. The file name is the section name and the contents are the tests in that section.
100 def log_as_files(self, target_dir, test_status):
101 status_regex = re.compile('|'.join(map(str, test_status)))
102 if not type(test_status) == type([]):
103 raise Exception("test_status should be a list. Got " + str(test_status) + " instead.")
104 if not os.path.exists(target_dir):
105 raise Exception("Target directory does not exist: %s" % target_dir)
106
107 for section, test_results in self.result_dict.items():
108 prefix = ''
109 for x in test_status:
110 prefix +=x+'.'
111 if (section != ''):
112 prefix += section
113 section_file = os.path.join(target_dir, prefix)
114 # purge the file contents if it exists
115 open(section_file, 'w').close()
116 for test_result in test_results:
117 (test_name, status) = test_result
118 # we log only the tests with status in the test_status list
119 match_status = status_regex.search(status)
120 if match_status:
121 ftools.append_file(section_file, status + ": " + test_name)
122
123 # Not yet implemented!
124 def log_to_lava(self):
125 pass
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
new file mode 100644
index 0000000000..f1a7e24ab7
--- /dev/null
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -0,0 +1,237 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module provides a class for starting qemu images using runqemu.
6# It's used by testimage.bbclass.
7
8import subprocess
9import os
10import time
11import signal
12import re
13import socket
14import select
15import bb
16
17class QemuRunner:
18
19 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime):
20
21 # Popen object for runqemu
22 self.runqemu = None
23 # pid of the qemu process that runqemu will start
24 self.qemupid = None
25 # target ip - from the command line
26 self.ip = None
27 # host ip - where qemu is running
28 self.server_ip = None
29
30 self.machine = machine
31 self.rootfs = rootfs
32 self.display = display
33 self.tmpdir = tmpdir
34 self.deploy_dir_image = deploy_dir_image
35 self.logfile = logfile
36 self.boottime = boottime
37
38 self.runqemutime = 60
39
40 self.create_socket()
41
42
43 def create_socket(self):
44
45 self.bootlog = ''
46 self.qemusock = None
47
48 try:
49 self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
50 self.server_socket.setblocking(0)
51 self.server_socket.bind(("127.0.0.1",0))
52 self.server_socket.listen(2)
53 self.serverport = self.server_socket.getsockname()[1]
54 bb.note("Created listening socket for qemu serial console on: 127.0.0.1:%s" % self.serverport)
55 except socket.error, msg:
56 self.server_socket.close()
57 bb.fatal("Failed to create listening socket: %s" %msg[1])
58
59
60 def log(self, msg):
61 if self.logfile:
62 with open(self.logfile, "a") as f:
63 f.write("%s" % msg)
64
65 def start(self, qemuparams = None):
66
67 if self.display:
68 os.environ["DISPLAY"] = self.display
69 else:
70 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
71 return False
72 if not os.path.exists(self.rootfs):
73 bb.error("Invalid rootfs %s" % self.rootfs)
74 return False
75 if not os.path.exists(self.tmpdir):
76 bb.error("Invalid TMPDIR path %s" % self.tmpdir)
77 return False
78 else:
79 os.environ["OE_TMPDIR"] = self.tmpdir
80 if not os.path.exists(self.deploy_dir_image):
81 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
82 return False
83 else:
84 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
85
86 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
87 # badly with screensavers.
88 os.environ["QEMU_DONT_GRAB"] = "1"
89 self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport
90 if qemuparams:
91 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
92
93 launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams)
94 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
95
96 bb.note("runqemu started, pid is %s" % self.runqemu.pid)
97 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
98 endtime = time.time() + self.runqemutime
99 while not self.is_alive() and time.time() < endtime:
100 time.sleep(1)
101
102 if self.is_alive():
103 bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
104 cmdline = ''
105 with open('/proc/%s/cmdline' % self.qemupid) as p:
106 cmdline = p.read()
107 ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
108 if not ips or len(ips) != 3:
109 bb.note("Couldn't get ip from qemu process arguments! Here is the qemu command line used: %s" % cmdline)
110 self.stop()
111 return False
112 else:
113 self.ip = ips[0]
114 self.server_ip = ips[1]
115 bb.note("Target IP: %s" % self.ip)
116 bb.note("Server IP: %s" % self.server_ip)
117 bb.note("Waiting at most %d seconds for login banner" % self.boottime )
118 endtime = time.time() + self.boottime
119 socklist = [self.server_socket]
120 reachedlogin = False
121 stopread = False
122 while time.time() < endtime and not stopread:
123 sread, swrite, serror = select.select(socklist, [], [], 5)
124 for sock in sread:
125 if sock is self.server_socket:
126 self.qemusock, addr = self.server_socket.accept()
127 self.qemusock.setblocking(0)
128 socklist.append(self.qemusock)
129 socklist.remove(self.server_socket)
130 bb.note("Connection from %s:%s" % addr)
131 else:
132 data = sock.recv(1024)
133 if data:
134 self.log(data)
135 self.bootlog += data
136 if re.search("qemu.* login:", self.bootlog):
137 stopread = True
138 reachedlogin = True
139 bb.note("Reached login banner")
140 else:
141 socklist.remove(sock)
142 sock.close()
143 stopread = True
144
145 if not reachedlogin:
146 bb.note("Target didn't reached login boot in %d seconds" % self.boottime)
147 lines = "\n".join(self.bootlog.splitlines()[-5:])
148 bb.note("Last 5 lines of text:\n%s" % lines)
149 bb.note("Check full boot log: %s" % self.logfile)
150 self.stop()
151 return False
152 else:
153 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
154 output = self.runqemu.stdout
155 self.stop()
156 bb.note("Output from runqemu:\n%s" % output.read())
157 return False
158
159 return self.is_alive()
160
161 def stop(self):
162
163 if self.runqemu:
164 bb.note("Sending SIGTERM to runqemu")
165 os.killpg(self.runqemu.pid, signal.SIGTERM)
166 endtime = time.time() + self.runqemutime
167 while self.runqemu.poll() is None and time.time() < endtime:
168 time.sleep(1)
169 if self.runqemu.poll() is None:
170 bb.note("Sending SIGKILL to runqemu")
171 os.killpg(self.runqemu.pid, signal.SIGKILL)
172 self.runqemu = None
173 if self.server_socket:
174 self.server_socket.close()
175 self.server_socket = None
176 self.qemupid = None
177 self.ip = None
178
179 def restart(self, qemuparams = None):
180 bb.note("Restarting qemu process")
181 if self.runqemu.poll() is None:
182 self.stop()
183 self.create_socket()
184 if self.start(qemuparams):
185 return True
186 return False
187
188 def is_alive(self):
189 qemu_child = self.find_child(str(self.runqemu.pid))
190 if qemu_child:
191 self.qemupid = qemu_child[0]
192 if os.path.exists("/proc/" + str(self.qemupid)):
193 return True
194 return False
195
196 def find_child(self,parent_pid):
197 #
198 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
199 #
200 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
201 processes = ps.split('\n')
202 nfields = len(processes[0].split()) - 1
203 pids = {}
204 commands = {}
205 for row in processes[1:]:
206 data = row.split(None, nfields)
207 if len(data) != 3:
208 continue
209 if data[1] not in pids:
210 pids[data[1]] = []
211
212 pids[data[1]].append(data[0])
213 commands[data[0]] = data[2]
214
215 if parent_pid not in pids:
216 return []
217
218 parents = []
219 newparents = pids[parent_pid]
220 while newparents:
221 next = []
222 for p in newparents:
223 if p in pids:
224 for n in pids[p]:
225 if n not in parents and n not in next:
226 next.append(n)
227 if p not in parents:
228 parents.append(p)
229 newparents = next
230 #print "Children matching %s:" % str(parents)
231 for p in parents:
232 # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
233 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
234 basecmd = commands[p].split()[0]
235 basecmd = os.path.basename(basecmd)
236 if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
237 return [int(p),commands[p]]
diff --git a/meta/lib/oeqa/utils/sshcontrol.py b/meta/lib/oeqa/utils/sshcontrol.py
new file mode 100644
index 0000000000..1c81795a87
--- /dev/null
+++ b/meta/lib/oeqa/utils/sshcontrol.py
@@ -0,0 +1,138 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Provides a class for setting up ssh connections,
6# running commands and copying files to/from a target.
7# It's used by testimage.bbclass and tests in lib/oeqa/runtime.
8
9import subprocess
10import time
11import os
12import select
13
14
15class SSHProcess(object):
16 def __init__(self, **options):
17
18 self.defaultopts = {
19 "stdout": subprocess.PIPE,
20 "stderr": subprocess.STDOUT,
21 "stdin": None,
22 "shell": False,
23 "bufsize": -1,
24 "preexec_fn": os.setsid,
25 }
26 self.options = dict(self.defaultopts)
27 self.options.update(options)
28 self.status = None
29 self.output = None
30 self.process = None
31 self.starttime = None
32 self.logfile = None
33
34 def log(self, msg):
35 if self.logfile:
36 with open(self.logfile, "a") as f:
37 f.write("%s" % msg)
38
39 def run(self, command, timeout=None, logfile=None):
40 self.logfile = logfile
41 self.starttime = time.time()
42 output = ''
43 self.process = subprocess.Popen(command, **self.options)
44 if timeout:
45 endtime = self.starttime + timeout
46 eof = False
47 while time.time() < endtime and not eof:
48 if select.select([self.process.stdout], [], [], 5)[0] != []:
49 data = os.read(self.process.stdout.fileno(), 1024)
50 if not data:
51 self.process.stdout.close()
52 eof = True
53 else:
54 output += data
55 self.log(data)
56 endtime = time.time() + timeout
57
58
59 # process hasn't returned yet
60 if not eof:
61 self.process.terminate()
62 time.sleep(5)
63 try:
64 self.process.kill()
65 except OSError:
66 pass
67 lastline = "\nProcess killed - no output for %d seconds. Total running time: %d seconds." % (timeout, time.time() - self.starttime)
68 self.log(lastline)
69 output += lastline
70 else:
71 output = self.process.communicate()[0]
72 self.log(output.rstrip())
73
74 self.status = self.process.wait()
75 self.output = output.rstrip()
76 return (self.status, self.output)
77
78
79class SSHControl(object):
80 def __init__(self, ip, logfile=None, timeout=300, user='root', port=None):
81 self.ip = ip
82 self.defaulttimeout = timeout
83 self.ignore_status = True
84 self.logfile = logfile
85 self.user = user
86 self.ssh_options = [
87 '-o', 'UserKnownHostsFile=/dev/null',
88 '-o', 'StrictHostKeyChecking=no',
89 '-o', 'LogLevel=ERROR'
90 ]
91 self.ssh = ['ssh', '-l', self.user ] + self.ssh_options
92 self.scp = ['scp'] + self.ssh_options
93 if port:
94 self.ssh = self.ssh + [ '-p', port ]
95 self.scp = self.scp + [ '-P', port ]
96
97 def log(self, msg):
98 if self.logfile:
99 with open(self.logfile, "a") as f:
100 f.write("%s\n" % msg)
101
102 def _internal_run(self, command, timeout=None, ignore_status = True):
103 self.log("[Running]$ %s" % " ".join(command))
104
105 proc = SSHProcess()
106 status, output = proc.run(command, timeout, logfile=self.logfile)
107
108 self.log("[Command returned '%d' after %.2f seconds]" % (status, time.time() - proc.starttime))
109
110 if status and not ignore_status:
111 raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, status, output))
112
113 return (status, output)
114
115 def run(self, command, timeout=None):
116 """
117 command - ssh command to run
118 timeout=<val> - kill command if there is no output after <val> seconds
119 timeout=None - kill command if there is no output after a default value seconds
120 timeout=0 - no timeout, let command run until it returns
121 """
122
123 # We need to source /etc/profile for a proper PATH on the target
124 command = self.ssh + [self.ip, ' . /etc/profile; ' + command]
125
126 if timeout is None:
127 return self._internal_run(command, self.defaulttimeout, self.ignore_status)
128 if timeout == 0:
129 return self._internal_run(command, None, self.ignore_status)
130 return self._internal_run(command, timeout, self.ignore_status)
131
132 def copy_to(self, localpath, remotepath):
133 command = self.scp + [localpath, '%s@%s:%s' % (self.user, self.ip, remotepath)]
134 return self._internal_run(command, ignore_status=False)
135
136 def copy_from(self, remotepath, localpath):
137 command = self.scp + ['%s@%s:%s' % (self.user, self.ip, remotepath), localpath]
138 return self._internal_run(command, ignore_status=False)
diff --git a/meta/lib/oeqa/utils/targetbuild.py b/meta/lib/oeqa/utils/targetbuild.py
new file mode 100644
index 0000000000..eeb08ba716
--- /dev/null
+++ b/meta/lib/oeqa/utils/targetbuild.py
@@ -0,0 +1,132 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Provides a class for automating build tests for projects
6
7import os
8import re
9import bb.utils
10import subprocess
11from abc import ABCMeta, abstractmethod
12
13class BuildProject():
14
15 __metaclass__ = ABCMeta
16
17 def __init__(self, d, uri, foldername=None, tmpdir="/tmp/"):
18 self.d = d
19 self.uri = uri
20 self.archive = os.path.basename(uri)
21 self.localarchive = os.path.join(tmpdir,self.archive)
22 self.fname = re.sub(r'.tar.bz2|tar.gz$', '', self.archive)
23 if foldername:
24 self.fname = foldername
25
26 # Download self.archive to self.localarchive
27 def _download_archive(self):
28
29 exportvars = ['HTTP_PROXY', 'http_proxy',
30 'HTTPS_PROXY', 'https_proxy',
31 'FTP_PROXY', 'ftp_proxy',
32 'FTPS_PROXY', 'ftps_proxy',
33 'NO_PROXY', 'no_proxy',
34 'ALL_PROXY', 'all_proxy',
35 'SOCKS5_USER', 'SOCKS5_PASSWD']
36
37 cmd = ''
38 for var in exportvars:
39 val = self.d.getVar(var, True)
40 if val:
41 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
42
43 cmd = cmd + "wget -O %s %s" % (self.localarchive, self.uri)
44 subprocess.check_call(cmd, shell=True)
45
46 # This method should provide a way to run a command in the desired environment.
47 @abstractmethod
48 def _run(self, cmd):
49 pass
50
51 # The timeout parameter of target.run is set to 0 to make the ssh command
52 # run with no timeout.
53 def run_configure(self, configure_args=''):
54 return self._run('cd %s; ./configure %s' % (self.targetdir, configure_args))
55
56 def run_make(self, make_args=''):
57 return self._run('cd %s; make %s' % (self.targetdir, make_args))
58
59 def run_install(self, install_args=''):
60 return self._run('cd %s; make install %s' % (self.targetdir, install_args))
61
62 def clean(self):
63 self._run('rm -rf %s' % self.targetdir)
64 subprocess.call('rm -f %s' % self.localarchive, shell=True)
65 pass
66
67class TargetBuildProject(BuildProject):
68
69 def __init__(self, target, d, uri, foldername=None):
70 self.target = target
71 self.targetdir = "~/"
72 BuildProject.__init__(self, d, uri, foldername, tmpdir="/tmp")
73
74 def download_archive(self):
75
76 self._download_archive()
77
78 (status, output) = self.target.copy_to(self.localarchive, self.targetdir)
79 if status != 0:
80 raise Exception("Failed to copy archive to target, output: %s" % output)
81
82 (status, output) = self.target.run('tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir))
83 if status != 0:
84 raise Exception("Failed to extract archive, output: %s" % output)
85
86 #Change targetdir to project folder
87 self.targetdir = self.targetdir + self.fname
88
89 # The timeout parameter of target.run is set to 0 to make the ssh command
90 # run with no timeout.
91 def _run(self, cmd):
92 return self.target.run(cmd, 0)[0]
93
94
95class SDKBuildProject(BuildProject):
96
97 def __init__(self, testpath, sdkenv, d, uri, foldername=None):
98 self.sdkenv = sdkenv
99 self.testdir = testpath
100 self.targetdir = testpath
101 bb.utils.mkdirhier(testpath)
102 self.datetime = d.getVar('DATETIME', True)
103 self.testlogdir = d.getVar("TEST_LOG_DIR", True)
104 bb.utils.mkdirhier(self.testlogdir)
105 self.logfile = os.path.join(self.testlogdir, "sdk_target_log.%s" % self.datetime)
106 BuildProject.__init__(self, d, uri, foldername, tmpdir=testpath)
107
108 def download_archive(self):
109
110 self._download_archive()
111
112 cmd = 'tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir)
113 subprocess.check_call(cmd, shell=True)
114
115 #Change targetdir to project folder
116 self.targetdir = self.targetdir + self.fname
117
118 def run_configure(self, configure_args=''):
119 return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'))
120
121 def run_install(self, install_args=''):
122 return super(SDKBuildProject, self).run_install(install_args=(install_args or "DESTDIR=%s/../install" % self.targetdir))
123
124 def log(self, msg):
125 if self.logfile:
126 with open(self.logfile, "a") as f:
127 f.write("%s\n" % msg)
128
129 def _run(self, cmd):
130 self.log("Running source %s; " % self.sdkenv + cmd)
131 return subprocess.call("source %s; " % self.sdkenv + cmd, shell=True)
132