summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa
diff options
context:
space:
mode:
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.py133
-rw-r--r--meta/lib/oeqa/controllers/testtargetloader.py69
-rw-r--r--meta/lib/oeqa/oetest.py107
-rwxr-xr-xmeta/lib/oeqa/runexported.py140
-rw-r--r--meta/lib/oeqa/runtime/__init__.py3
-rw-r--r--meta/lib/oeqa/runtime/buildcvs.py30
-rw-r--r--meta/lib/oeqa/runtime/buildiptables.py30
-rw-r--r--meta/lib/oeqa/runtime/buildsudoku.py27
-rw-r--r--meta/lib/oeqa/runtime/connman.py30
-rw-r--r--meta/lib/oeqa/runtime/date.py22
-rw-r--r--meta/lib/oeqa/runtime/df.py11
-rw-r--r--meta/lib/oeqa/runtime/dmesg.py11
-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.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.py36
-rw-r--r--meta/lib/oeqa/runtime/kernelmodule.py33
-rw-r--r--meta/lib/oeqa/runtime/ldd.py19
-rw-r--r--meta/lib/oeqa/runtime/logrotate.py27
-rw-r--r--meta/lib/oeqa/runtime/multilib.py17
-rw-r--r--meta/lib/oeqa/runtime/pam.py24
-rw-r--r--meta/lib/oeqa/runtime/perl.py28
-rw-r--r--meta/lib/oeqa/runtime/ping.py20
-rw-r--r--meta/lib/oeqa/runtime/python.py33
-rw-r--r--meta/lib/oeqa/runtime/rpm.py50
-rw-r--r--meta/lib/oeqa/runtime/scanelf.py26
-rw-r--r--meta/lib/oeqa/runtime/scp.py21
-rw-r--r--meta/lib/oeqa/runtime/skeletoninit.py28
-rw-r--r--meta/lib/oeqa/runtime/smart.py110
-rw-r--r--meta/lib/oeqa/runtime/ssh.py18
-rw-r--r--meta/lib/oeqa/runtime/syslog.py46
-rw-r--r--meta/lib/oeqa/runtime/systemd.py84
-rw-r--r--meta/lib/oeqa/runtime/vnc.py19
-rw-r--r--meta/lib/oeqa/runtime/x32lib.py17
-rw-r--r--meta/lib/oeqa/runtime/xorg.py21
-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/base.py129
-rw-r--r--meta/lib/oeqa/selftest/bblayers.py37
-rw-r--r--meta/lib/oeqa/selftest/bbtests.py104
-rw-r--r--meta/lib/oeqa/selftest/buildhistory.py45
-rw-r--r--meta/lib/oeqa/selftest/buildoptions.py113
-rw-r--r--meta/lib/oeqa/selftest/oescripts.py60
-rw-r--r--meta/lib/oeqa/selftest/prservice.py113
-rw-r--r--meta/lib/oeqa/selftest/sstate.py53
-rw-r--r--meta/lib/oeqa/selftest/sstatetests.py193
-rw-r--r--meta/lib/oeqa/targetcontrol.py175
-rw-r--r--meta/lib/oeqa/utils/__init__.py3
-rw-r--r--meta/lib/oeqa/utils/commands.py137
-rw-r--r--meta/lib/oeqa/utils/decorators.py50
-rw-r--r--meta/lib/oeqa/utils/ftools.py27
-rw-r--r--meta/lib/oeqa/utils/httpserver.py33
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py237
-rw-r--r--meta/lib/oeqa/utils/sshcontrol.py127
-rw-r--r--meta/lib/oeqa/utils/targetbuild.py68
60 files changed, 3160 insertions, 0 deletions
diff --git a/meta/lib/oeqa/__init__.py b/meta/lib/oeqa/__init__.py
new file mode 100644
index 0000000..e69de29
--- /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 0000000..8eda927
--- /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 0000000..188c630
--- /dev/null
+++ b/meta/lib/oeqa/controllers/masterimage.py
@@ -0,0 +1,133 @@
1import os
2import bb
3import traceback
4import time
5
6import oeqa.targetcontrol
7import oeqa.utils.sshcontrol as sshcontrol
8import oeqa.utils.commands as commands
9
10class GummibootTarget(oeqa.targetcontrol.SimpleRemoteTarget):
11
12 def __init__(self, d):
13 # let our base class do the ip thing
14 super(GummibootTarget, self).__init__(d)
15
16 # test rootfs + kernel
17 self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.tar.gz')
18 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE"))
19 if not os.path.isfile(self.rootfs):
20 # we could've checked that IMAGE_FSTYPES contains tar.gz but the config for running testimage might not be
21 # the same as the config with which the image was build, ie
22 # you bitbake core-image-sato with IMAGE_FSTYPES += "tar.gz"
23 # and your autobuilder overwrites the config, adds the test bits and runs bitbake core-image-sato -c testimage
24 bb.fatal("No rootfs found. Did you build the image ?\nIf yes, did you build it with IMAGE_FSTYPES += \"tar.gz\" ? \
25 \nExpected path: %s" % self.rootfs)
26 if not os.path.isfile(self.kernel):
27 bb.fatal("No kernel found. Expected path: %s" % self.kernel)
28
29 # if the user knows what he's doing, then by all means...
30 # test-rootfs.tar.gz and test-kernel are hardcoded names in other places
31 # they really have to be used like that in commands though
32 cmds = d.getVar("TEST_DEPLOY_CMDS", True)
33
34 # this the value we need to set in the LoaderEntryOneShot EFI variable
35 # so the system boots the 'test' bootloader label and not the default
36 # The first four bytes are EFI bits, and the rest is an utf-16le string
37 # (EFI vars values need to be utf-16)
38 # $ echo -en "test\0" | iconv -f ascii -t utf-16le | hexdump -C
39 # 00000000 74 00 65 00 73 00 74 00 00 00 |t.e.s.t...|
40 self.efivarvalue = r'\x07\x00\x00\x00\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00'
41
42 if cmds:
43 self.deploy_cmds = cmds.split("\n")
44 else:
45 self.deploy_cmds = [
46 'mount -L boot /boot',
47 'mkdir -p /mnt/testrootfs',
48 'mount -L testrootfs /mnt/testrootfs',
49 'modprobe efivarfs',
50 'mount -t efivarfs efivarfs /sys/firmware/efi/efivars',
51 'cp ~/test-kernel /boot',
52 'rm -rf /mnt/testrootfs/*',
53 'tar xzvf ~/test-rootfs.tar.gz -C /mnt/testrootfs',
54 'printf "%s" > /sys/firmware/efi/efivars/LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f' % self.efivarvalue
55 ]
56
57 # master ssh connection
58 self.master = None
59
60 # this is the name of the command that controls the power for a board
61 # e.g: TEST_POWERCONTROL_CMD = "/home/user/myscripts/powercontrol.py ${MACHINE} what-ever-other-args-the-script-wants"
62 # the command should take as the last argument "off" and "on" and "cycle" (off, on)
63 self.powercontrol_cmd = d.getVar("TEST_POWERCONTROL_CMD", True) or None
64 self.powercontrol_args = d.getVar("TEST_POWERCONTROL_EXTRA_ARGS") or ""
65 self.origenv = os.environ
66 if self.powercontrol_cmd:
67 if self.powercontrol_args:
68 self.powercontrol_cmd = "%s %s" % (self.powercontrol_cmd, self.powercontrol_args)
69 # the external script for controlling power might use ssh
70 # ssh + keys means we need the original user env
71 bborigenv = d.getVar("BB_ORIGENV", False) or {}
72 for key in bborigenv:
73 val = bborigenv.getVar(key, True)
74 if val is not None:
75 self.origenv[key] = str(val)
76 self.power_ctl("on")
77
78 def power_ctl(self, msg):
79 if self.powercontrol_cmd:
80 cmd = "%s %s" % (self.powercontrol_cmd, msg)
81 commands.runCmd(cmd, preexec_fn=os.setsid, env=self.origenv)
82
83 def power_cycle(self, conn):
84 if self.powercontrol_cmd:
85 # be nice, don't just cut power
86 conn.run("shutdown -h now")
87 time.sleep(10)
88 self.power_ctl("cycle")
89 else:
90 status, output = conn.run("reboot")
91 if status != 0:
92 bb.error("Failed rebooting target and no power control command defined. You need to manually reset the device.\n%s" % output)
93
94 def deploy(self):
95 bb.plain("%s - deploying image on target" % self.pn)
96 # base class just sets the ssh log file for us
97 super(GummibootTarget, self).deploy()
98 self.master = sshcontrol.SSHControl(ip=self.ip, logfile=self.sshlog, timeout=600, port=self.port)
99 try:
100 self._deploy()
101 except Exception as e:
102 bb.fatal("Failed deploying test image: %s" % e)
103
104 def _deploy(self):
105 # make sure we are in the right image
106 status, output = self.master.run("cat /etc/masterimage")
107 if status != 0:
108 raise Exception("No ssh connectivity or target isn't running a master image.\n%s" % output)
109
110 # make sure these aren't mounted
111 self.master.run("umount /boot; umount /mnt/testrootfs; umount /sys/firmware/efi/efivars;")
112
113 # from now on, every deploy cmd should return 0
114 # else an exception will be thrown by sshcontrol
115 self.master.ignore_status = False
116 self.master.copy_to(self.rootfs, "~/test-rootfs.tar.gz")
117 self.master.copy_to(self.kernel, "~/test-kernel")
118 for cmd in self.deploy_cmds:
119 self.master.run(cmd)
120
121
122 def start(self, params=None):
123 bb.plain("%s - boot test image on target" % self.pn)
124 self.power_cycle(self.master)
125 # there are better ways than a timeout but this should work for now
126 time.sleep(120)
127 # set the ssh object for the target/test image
128 self.connection = sshcontrol.SSHControl(self.ip, logfile=self.sshlog, port=self.port)
129 bb.plain("%s - start running tests" % self.pn)
130
131 def stop(self):
132 bb.plain("%s - reboot/powercycle target" % self.pn)
133 self.power_cycle(self.connection)
diff --git a/meta/lib/oeqa/controllers/testtargetloader.py b/meta/lib/oeqa/controllers/testtargetloader.py
new file mode 100644
index 0000000..019bbfd
--- /dev/null
+++ b/meta/lib/oeqa/controllers/testtargetloader.py
@@ -0,0 +1,69 @@
1import types
2import bb
3
4# This class is responsible for loading a test target controller
5class TestTargetLoader:
6
7 # Search oeqa.controllers module directory for and return a controller
8 # corresponding to the given target name.
9 # AttributeError raised if not found.
10 # ImportError raised if a provided module can not be imported.
11 def get_controller_module(self, target, bbpath):
12 controllerslist = self.get_controller_modulenames(bbpath)
13 bb.note("Available controller modules: %s" % str(controllerslist))
14 controller = self.load_controller_from_name(target, controllerslist)
15 return controller
16
17 # Return a list of all python modules in lib/oeqa/controllers for each
18 # layer in bbpath
19 def get_controller_modulenames(self, bbpath):
20
21 controllerslist = []
22
23 def add_controller_list(path):
24 if not os.path.exists(os.path.join(path, '__init__.py')):
25 bb.fatal('Controllers directory %s exists but is missing __init__.py' % path)
26 files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')])
27 for f in files:
28 module = 'oeqa.controllers.' + f[:-3]
29 if module not in controllerslist:
30 controllerslist.append(module)
31 else:
32 bb.warn("Duplicate controller module found for %s, only one added. Layers should create unique controller module names" % module)
33
34 for p in bbpath:
35 controllerpath = os.path.join(p, 'lib', 'oeqa', 'controllers')
36 bb.debug(2, 'Searching for target controllers in %s' % controllerpath)
37 if os.path.exists(controllerpath):
38 add_controller_list(controllerpath)
39 return controllerslist
40
41 # Search for and return a controller from given target name and
42 # set of module names.
43 # Raise AttributeError if not found.
44 # Raise ImportError if a provided module can not be imported
45 def load_controller_from_name(self, target, modulenames):
46 for name in modulenames:
47 obj = self.load_controller_from_module(target, name)
48 if obj:
49 return obj
50 raise AttributeError("Unable to load {0} from available modules: {1}".format(target, str(modulenames)))
51
52 # Search for and return a controller or None from given module name
53 def load_controller_from_module(self, target, modulename):
54 obj = None
55 # import module, allowing it to raise import exception
56 module = __import__(modulename, globals(), locals(), [target])
57 # look for target class in the module, catching any exceptions as it
58 # is valid that a module may not have the target class.
59 try:
60 obj = getattr(module, target)
61 if obj:
62 from oeqa.targetcontrol import BaseTarget
63 if (not isinstance(obj, (type, types.ClassType))):
64 bb.warn("Target {0} found, but not of type Class".format(target))
65 if( not issubclass(obj, BaseTarget)):
66 bb.warn("Target {0} found, but subclass is not BaseTarget".format(target))
67 except:
68 obj = None
69 return obj
diff --git a/meta/lib/oeqa/oetest.py b/meta/lib/oeqa/oetest.py
new file mode 100644
index 0000000..0db6cb8
--- /dev/null
+++ b/meta/lib/oeqa/oetest.py
@@ -0,0 +1,107 @@
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
13
14
15def loadTests(tc):
16
17 # set the context object passed from the test class
18 setattr(oeTest, "tc", tc)
19 # set ps command to use
20 setattr(oeRuntimeTest, "pscmd", "ps -ef" if oeTest.hasPackage("procps") else "ps")
21 # prepare test suite, loader and runner
22 suite = unittest.TestSuite()
23 testloader = unittest.TestLoader()
24 testloader.sortTestMethodsUsing = None
25 suite = testloader.loadTestsFromNames(tc.testslist)
26
27 return suite
28
29def runTests(tc):
30
31 suite = loadTests(tc)
32 print("Test modules %s" % tc.testslist)
33 print("Found %s tests" % suite.countTestCases())
34 runner = unittest.TextTestRunner(verbosity=2)
35 result = runner.run(suite)
36
37 return result
38
39
40class oeTest(unittest.TestCase):
41
42 longMessage = True
43 testFailures = []
44 testSkipped = []
45 testErrors = []
46
47 def run(self, result=None):
48 super(oeTest, self).run(result)
49
50 # we add to our own lists the results, we use those for decorators
51 if len(result.failures) > len(oeTest.testFailures):
52 oeTest.testFailures.append(str(result.failures[-1][0]).split()[0])
53 if len(result.skipped) > len(oeTest.testSkipped):
54 oeTest.testSkipped.append(str(result.skipped[-1][0]).split()[0])
55 if len(result.errors) > len(oeTest.testErrors):
56 oeTest.testErrors.append(str(result.errors[-1][0]).split()[0])
57
58 @classmethod
59 def hasPackage(self, pkg):
60
61 if re.search(pkg, oeTest.tc.pkgmanifest):
62 return True
63 return False
64
65 @classmethod
66 def hasFeature(self,feature):
67
68 if feature in oeTest.tc.imagefeatures or \
69 feature in oeTest.tc.distrofeatures:
70 return True
71 else:
72 return False
73
74
75class oeRuntimeTest(oeTest):
76
77 def __init__(self, methodName='runTest'):
78 self.target = oeRuntimeTest.tc.target
79 super(oeRuntimeTest, self).__init__(methodName)
80
81
82def getmodule(pos=2):
83 # stack returns a list of tuples containg frame information
84 # First element of the list the is current frame, caller is 1
85 frameinfo = inspect.stack()[pos]
86 modname = inspect.getmodulename(frameinfo[1])
87 #modname = inspect.getmodule(frameinfo[0]).__name__
88 return modname
89
90def skipModule(reason, pos=2):
91 modname = getmodule(pos)
92 if modname not in oeTest.tc.testsrequired:
93 raise unittest.SkipTest("%s: %s" % (modname, reason))
94 else:
95 raise Exception("\nTest %s wants to be skipped.\nReason is: %s" \
96 "\nTest was required in TEST_SUITES, so either the condition for skipping is wrong" \
97 "\nor the image really doesn't have the required feature/package when it should." % (modname, reason))
98
99def skipModuleIf(cond, reason):
100
101 if cond:
102 skipModule(reason, 3)
103
104def skipModuleUnless(cond, reason):
105
106 if not cond:
107 skipModule(reason, 3)
diff --git a/meta/lib/oeqa/runexported.py b/meta/lib/oeqa/runexported.py
new file mode 100755
index 0000000..e1b6642
--- /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 0000000..4cf3fa7
--- /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/buildcvs.py b/meta/lib/oeqa/runtime/buildcvs.py
new file mode 100644
index 0000000..f1fbf19
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildcvs.py
@@ -0,0 +1,30 @@
1from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed("test_ssh")
18 def test_cvs(self):
19 self.assertEqual(self.project.run_configure(), 0,
20 msg="Running configure failed")
21
22 self.assertEqual(self.project.run_make(), 0,
23 msg="Running make failed")
24
25 self.assertEqual(self.project.run_install(), 0,
26 msg="Running make install failed")
27
28 @classmethod
29 def tearDownClass(self):
30 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/buildiptables.py b/meta/lib/oeqa/runtime/buildiptables.py
new file mode 100644
index 0000000..f6061a7
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildiptables.py
@@ -0,0 +1,30 @@
1from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed("test_ssh")
18 def test_iptables(self):
19 self.assertEqual(self.project.run_configure(), 0,
20 msg="Running configure failed")
21
22 self.assertEqual(self.project.run_make(), 0,
23 msg="Running make failed")
24
25 self.assertEqual(self.project.run_install(), 0,
26 msg="Running make install failed")
27
28 @classmethod
29 def tearDownClass(self):
30 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/buildsudoku.py b/meta/lib/oeqa/runtime/buildsudoku.py
new file mode 100644
index 0000000..a754f1d
--- /dev/null
+++ b/meta/lib/oeqa/runtime/buildsudoku.py
@@ -0,0 +1,27 @@
1from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed("test_ssh")
18 def test_sudoku(self):
19 self.assertEqual(self.project.run_configure(), 0,
20 msg="Running configure failed")
21
22 self.assertEqual(self.project.run_make(), 0,
23 msg="Running make failed")
24
25 @classmethod
26 def tearDownClass(self):
27 self.project.clean()
diff --git a/meta/lib/oeqa/runtime/connman.py b/meta/lib/oeqa/runtime/connman.py
new file mode 100644
index 0000000..c036882
--- /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
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 0000000..a208e29
--- /dev/null
+++ b/meta/lib/oeqa/runtime/date.py
@@ -0,0 +1,22 @@
1from oeqa.oetest import oeRuntimeTest
2from oeqa.utils.decorators import *
3import re
4
5class DateTest(oeRuntimeTest):
6
7 @skipUnlessPassed("test_ssh")
8 def test_date(self):
9 (status, output) = self.target.run('date +"%Y-%m-%d %T"')
10 self.assertEqual(status, 0, msg="Failed to get initial date, output: %s" % output)
11 oldDate = output
12
13 sampleDate = '"2016-08-09 10:00:00"'
14 (status, output) = self.target.run("date -s %s" % sampleDate)
15 self.assertEqual(status, 0, msg="Date set failed, output: %s" % output)
16
17 (status, output) = self.target.run("date -R")
18 p = re.match('Tue, 09 Aug 2016 10:00:.. \+0000', output)
19 self.assertTrue(p, msg="The date was not set correctly, output: %s" % output)
20
21 (status, output) = self.target.run('date -s "%s"' % oldDate)
22 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 0000000..b6da350
--- /dev/null
+++ b/meta/lib/oeqa/runtime/df.py
@@ -0,0 +1,11 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest
3from oeqa.utils.decorators import *
4
5
6class DfTest(oeRuntimeTest):
7
8 @skipUnlessPassed("test_ssh")
9 def test_df(self):
10 (status,output) = self.target.run("df / | sed -n '2p' | awk '{print $4}'")
11 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 0000000..64247ea
--- /dev/null
+++ b/meta/lib/oeqa/runtime/dmesg.py
@@ -0,0 +1,11 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest
3from oeqa.utils.decorators import *
4
5
6class DmesgTest(oeRuntimeTest):
7
8 @skipUnlessPassed('test_ssh')
9 def test_dmesg(self):
10 (status, output) = self.target.run('dmesg | grep -v mmci-pl18x | grep -v "error changing net interface name" | grep -iv "dma timeout" | grep -i error')
11 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 0000000..a383397
--- /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 0000000..b92d5c8
--- /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 0000000..2d8389c
--- /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.pl b/meta/lib/oeqa/runtime/files/test.pl
new file mode 100644
index 0000000..689c8f1
--- /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 0000000..f3a2273
--- /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 0000000..ca1844e
--- /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 0000000..b63badd
--- /dev/null
+++ b/meta/lib/oeqa/runtime/gcc.py
@@ -0,0 +1,36 @@
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
18 def test_gcc_compile(self):
19 (status, output) = self.target.run('gcc /tmp/test.c -o /tmp/test -lm')
20 self.assertEqual(status, 0, msg="gcc compile failed, output: %s" % output)
21 (status, output) = self.target.run('/tmp/test')
22 self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
23
24 def test_gpp_compile(self):
25 (status, output) = self.target.run('g++ /tmp/test.c -o /tmp/test -lm')
26 self.assertEqual(status, 0, msg="g++ compile failed, output: %s" % output)
27 (status, output) = self.target.run('/tmp/test')
28 self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
29
30 def test_make(self):
31 (status, output) = self.target.run('cd /tmp; make -f testmakefile')
32 self.assertEqual(status, 0, msg="running make failed, output %s" % output)
33
34 @classmethod
35 def tearDownClass(self):
36 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 0000000..cbc5742
--- /dev/null
+++ b/meta/lib/oeqa/runtime/kernelmodule.py
@@ -0,0 +1,33 @@
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 @skipUnlessPassed('test_ssh')
18 @skipUnlessPassed('test_gcc_compile')
19 def test_kernel_module(self):
20 cmds = [
21 'cd /usr/src/kernel && make scripts',
22 'cd /tmp && make',
23 'cd /tmp && insmod hellomod.ko',
24 'lsmod | grep hellomod',
25 'dmesg | grep Hello',
26 'rmmod hellomod', 'dmesg | grep "Cleaning up hellomod"'
27 ]
28 for cmd in cmds:
29 (status, output) = self.target.run(cmd, 900)
30 self.assertEqual(status, 0, msg="\n".join([cmd, output]))
31
32 def tearDown(self):
33 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 0000000..4374530
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ldd.py
@@ -0,0 +1,19 @@
1import unittest
2from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed('test_ldd_exists')
17 def test_ldd_rtldlist_check(self):
18 (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')
19 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 0000000..80489a3
--- /dev/null
+++ b/meta/lib/oeqa/runtime/logrotate.py
@@ -0,0 +1,27 @@
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 @skipUnlessPassed("test_1_logrotate_setup")
23 def test_2_logrotate(self):
24 (status, output) = self.target.run('logrotate -f /etc/logrotate.conf')
25 self.assertEqual(status, 0, msg = "logrotate service could not be reloaded. Status and output: %s and %s" % (status, output))
26 output = self.target.run('ls -la /home/root/logrotate_dir/ | wc -l')[1]
27 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 0000000..13a3b54
--- /dev/null
+++ b/meta/lib/oeqa/runtime/multilib.py
@@ -0,0 +1,17 @@
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 @skipUnlessPassed('test_ssh')
14 def test_file_connman(self):
15 self.assertTrue(oeRuntimeTest.hasPackage('connman-gnome'), msg="This test assumes connman-gnome is installed")
16 (status, output) = self.target.run("readelf -h /usr/bin/connman-applet | sed -n '3p' | awk '{print $2}'")
17 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 0000000..52e1eb8
--- /dev/null
+++ b/meta/lib/oeqa/runtime/pam.py
@@ -0,0 +1,24 @@
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
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 @skipUnlessPassed('test_ssh')
16 def test_pam(self):
17 (status, output) = self.target.run('login --help')
18 self.assertEqual(status, 1, msg = "login command does not work as expected. Status and output:%s and %s" %(status, output))
19 (status, output) = self.target.run('passwd --help')
20 self.assertEqual(status, 6, msg = "passwd command does not work as expected. Status and output:%s and %s" %(status, output))
21 (status, output) = self.target.run('su --help')
22 self.assertEqual(status, 2, msg = "su command does not work as expected. Status and output:%s and %s" %(status, output))
23 (status, output) = self.target.run('useradd --help')
24 self.assertEqual(status, 2, msg = "useradd command does not work as expected. Status and output:%s and %s" %(status, output))
diff --git a/meta/lib/oeqa/runtime/perl.py b/meta/lib/oeqa/runtime/perl.py
new file mode 100644
index 0000000..c9bb684
--- /dev/null
+++ b/meta/lib/oeqa/runtime/perl.py
@@ -0,0 +1,28 @@
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 def test_perl_works(self):
22 (status, output) = self.target.run('perl /tmp/test.pl')
23 self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
24 self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
25
26 @classmethod
27 def tearDownClass(self):
28 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 0000000..a73c724
--- /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 0000000..c037ab2
--- /dev/null
+++ b/meta/lib/oeqa/runtime/python.py
@@ -0,0 +1,33 @@
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 def test_python_stdout(self):
22 (status, output) = self.target.run('python /tmp/test.py')
23 self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
24 self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
25
26 def test_python_testfile(self):
27 (status, output) = self.target.run('ls /tmp/testfile.python')
28 self.assertEqual(status, 0, msg="Python test file generate failed.")
29
30
31 @classmethod
32 def tearDownClass(self):
33 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 0000000..084d22f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/rpm.py
@@ -0,0 +1,50 @@
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 @skipUnlessPassed('test_rpm_help')
22 def test_rpm_query(self):
23 (status, output) = self.target.run('rpm -q rpm')
24 self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
25
26class RpmInstallRemoveTest(oeRuntimeTest):
27
28 @classmethod
29 def setUpClass(self):
30 pkgarch = oeRuntimeTest.tc.d.getVar('TUNE_PKGARCH', True).replace("-", "_")
31 rpmdir = os.path.join(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), "rpm", pkgarch)
32 # pick rpm-doc as a test file to get installed, because it's small and it will always be built for standard targets
33 for f in fnmatch.filter(os.listdir(rpmdir), "rpm-doc-*.%s.rpm" % pkgarch):
34 testrpmfile = f
35 oeRuntimeTest.tc.target.copy_to(os.path.join(rpmdir,testrpmfile), "/tmp/rpm-doc.rpm")
36
37 @skipUnlessPassed('test_rpm_help')
38 def test_rpm_install(self):
39 (status, output) = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
40 self.assertEqual(status, 0, msg="Failed to install rpm-doc package: %s" % output)
41
42 @skipUnlessPassed('test_rpm_install')
43 def test_rpm_remove(self):
44 (status,output) = self.target.run('rpm -e rpm-doc')
45 self.assertEqual(status, 0, msg="Failed to remove rpm-doc package: %s" % output)
46
47 @classmethod
48 def tearDownClass(self):
49 oeRuntimeTest.tc.target.run('rm -f /tmp/rpm-doc.rpm')
50
diff --git a/meta/lib/oeqa/runtime/scanelf.py b/meta/lib/oeqa/runtime/scanelf.py
new file mode 100644
index 0000000..b9abf24
--- /dev/null
+++ b/meta/lib/oeqa/runtime/scanelf.py
@@ -0,0 +1,26 @@
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 @skipUnlessPassed('test_ssh')
15 def test_scanelf_textrel(self):
16 # print TEXTREL information
17 self.scancmd += " --textrel"
18 (status, output) = self.target.run(self.scancmd)
19 self.assertEqual(output.strip(), "", "\n".join([self.scancmd, output]))
20
21 @skipUnlessPassed('test_ssh')
22 def test_scanelf_rpath(self):
23 # print RPATH information
24 self.scancmd += " --rpath"
25 (status, output) = self.target.run(self.scancmd)
26 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 0000000..03095bf
--- /dev/null
+++ b/meta/lib/oeqa/runtime/scp.py
@@ -0,0 +1,21 @@
1import os
2from oeqa.oetest import oeRuntimeTest, skipModule
3from oeqa.utils.decorators import skipUnlessPassed
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 @skipUnlessPassed('test_ssh')
12 def test_scp_file(self):
13 test_log_dir = oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR", True)
14 test_file_path = os.path.join(test_log_dir, 'test_scp_file')
15 with open(test_file_path, 'w') as test_scp_file:
16 test_scp_file.seek(2 ** 22 - 1)
17 test_scp_file.write(os.linesep)
18 (status, output) = self.target.copy_to(test_file_path, '/tmp/test_scp_file')
19 self.assertEqual(status, 0, msg = "File could not be copied. Output: %s" % output)
20 (status, output) = self.target.run("ls -la /tmp/test_scp_file")
21 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 0000000..557e715
--- /dev/null
+++ b/meta/lib/oeqa/runtime/skeletoninit.py
@@ -0,0 +1,28 @@
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
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 @skipUnlessPassed('test_skeleton_availability')
24 @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"), "Not appropiate for systemd image")
25 def test_skeleton_script(self):
26 output1 = self.target.run("/etc/init.d/skeleton start")[1]
27 (status, output2) = self.target.run(oeRuntimeTest.pscmd + ' | grep [s]keleton-test')
28 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 0000000..195f117
--- /dev/null
+++ b/meta/lib/oeqa/runtime/smart.py
@@ -0,0 +1,110 @@
1import unittest
2import re
3from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed('test_ssh')
29 def test_smart_help(self):
30 self.smart('--help')
31
32 def test_smart_version(self):
33 self.smart('--version')
34
35 def test_smart_info(self):
36 self.smart('info python-smartpm')
37
38 def test_smart_query(self):
39 self.smart('query python-smartpm')
40
41 def test_smart_search(self):
42 self.smart('search python-smartpm')
43
44 def test_smart_stats(self):
45 self.smart('stats')
46
47class SmartRepoTest(SmartTest):
48
49 @classmethod
50 def setUpClass(self):
51 self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
52 self.repo_server.start()
53
54 @classmethod
55 def tearDownClass(self):
56 self.repo_server.stop()
57
58 def test_smart_channel(self):
59 self.smart('channel', 1)
60
61 def test_smart_channel_add(self):
62 image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
63 deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
64 pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True).replace("-","_").split()
65 for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
66 if arch in pkgarchs:
67 self.smart('channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url))
68 self.smart('update')
69
70 def test_smart_channel_help(self):
71 self.smart('channel --help')
72
73 def test_smart_channel_list(self):
74 self.smart('channel --list')
75
76 def test_smart_channel_show(self):
77 self.smart('channel --show')
78
79 def test_smart_channel_rpmsys(self):
80 self.smart('channel --show rpmsys')
81 self.smart('channel --disable rpmsys')
82 self.smart('channel --enable rpmsys')
83
84 @skipUnlessPassed('test_smart_channel_add')
85 def test_smart_install(self):
86 self.smart('remove -y psplash-default')
87 self.smart('install -y psplash-default')
88
89 @skipUnlessPassed('test_smart_install')
90 def test_smart_install_dependency(self):
91 self.smart('remove -y psplash')
92 self.smart('install -y psplash-default')
93
94 @skipUnlessPassed('test_smart_channel_add')
95 def test_smart_install_from_disk(self):
96 self.smart('remove -y psplash-default')
97 self.smart('download psplash-default')
98 self.smart('install -y ./psplash-default*')
99
100 @skipUnlessPassed('test_smart_channel_add')
101 def test_smart_install_from_http(self):
102 output = self.smart('download --urls psplash-default')
103 url = re.search('(http://.*/psplash-default.*\.rpm)', output)
104 self.assertTrue(url, msg="Couln't find download url in %s" % output)
105 self.smart('remove -y psplash-default')
106 self.smart('install -y %s' % url.group(0))
107
108 @skipUnlessPassed('test_smart_install')
109 def test_smart_reinstall(self):
110 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 0000000..e648660
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ssh.py
@@ -0,0 +1,18 @@
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 @skipUnlessPassed('test_ping')
14 def test_ssh(self):
15 (status, output) = self.target.run('uname -a')
16 self.assertEqual(status, 0, msg="SSH Test failed: %s" % output)
17 (status, output) = self.target.run('cat /etc/masterimage')
18 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 0000000..b95b361
--- /dev/null
+++ b/meta/lib/oeqa/runtime/syslog.py
@@ -0,0 +1,46 @@
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 @skipUnlessPassed("test_syslog_help")
17 def test_syslog_running(self):
18 (status,output) = self.target.run(oeRuntimeTest.pscmd + ' | grep -i [s]yslogd')
19 self.assertEqual(status, 0, msg="no syslogd process, ps output: %s" % self.target.run(oeRuntimeTest.pscmd)[1])
20
21
22class SyslogTestConfig(oeRuntimeTest):
23
24 @skipUnlessPassed("test_syslog_running")
25 def test_syslog_logger(self):
26 (status,output) = self.target.run('logger foobar && test -e /var/log/messages && grep foobar /var/log/messages || logread | grep foobar')
27 self.assertEqual(status, 0, msg="Test log string not found in /var/log/messages. Output: %s " % output)
28
29 @skipUnlessPassed("test_syslog_running")
30 def test_syslog_restart(self):
31 if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"):
32 (status,output) = self.target.run('/etc/init.d/syslog restart')
33 else:
34 (status,output) = self.target.run('systemctl restart syslog.service')
35
36 @skipUnlessPassed("test_syslog_restart")
37 @skipUnlessPassed("test_syslog_logger")
38 @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager"), "Not appropiate for systemd image")
39 def test_syslog_startup_config(self):
40 self.target.run('echo "LOGFILE=/var/log/test" >> /etc/syslog-startup.conf')
41 (status,output) = self.target.run('/etc/init.d/syslog restart')
42 self.assertEqual(status, 0, msg="Could not restart syslog service. Status and output: %s and %s" % (status,output))
43 (status,output) = self.target.run('logger foobar && grep foobar /var/log/test')
44 self.assertEqual(status, 0, msg="Test log string not found. Output: %s " % output)
45 self.target.run("sed -i 's#LOGFILE=/var/log/test##' /etc/syslog-startup.conf")
46 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 0000000..6de84f8
--- /dev/null
+++ b/meta/lib/oeqa/runtime/systemd.py
@@ -0,0 +1,84 @@
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 @skipUnlessPassed('test_system_basic')
32 def test_systemd_list(self):
33 self.systemctl('list-unit-files')
34
35 def settle(self):
36 """
37 Block until systemd has finished activating any units being activated,
38 or until two minutes has elapsed.
39
40 Returns a tuple, either (True, '') if all units have finished
41 activating, or (False, message string) if there are still units
42 activating (generally, failing units that restart).
43 """
44 import time
45 endtime = time.time() + (60 * 2)
46 while True:
47 status, output = self.target.run('systemctl --state=activating')
48 if "0 loaded units listed" in output:
49 return (True, '')
50 if time.time() >= endtime:
51 return (False, output)
52 time.sleep(10)
53
54 @skipUnlessPassed('test_systemd_basic')
55 def test_systemd_failed(self):
56 settled, output = self.settle()
57 self.assertTrue(settled, msg="Timed out waiting for systemd to settle:\n" + output)
58
59 output = self.systemctl('list-units', '--failed')
60 match = re.search("0 loaded units listed", output)
61 if not match:
62 output += self.systemctl('status --full --failed')
63 self.assertTrue(match, msg="Some systemd units failed:\n%s" % output)
64
65
66class SystemdServiceTests(SystemdTest):
67
68 @skipUnlessPassed('test_systemd_basic')
69 def test_systemd_status(self):
70 self.systemctl('status --full', 'avahi-daemon.service')
71
72 @skipUnlessPassed('test_systemd_status')
73 def test_systemd_stop_start(self):
74 self.systemctl('stop', 'avahi-daemon.service')
75 self.systemctl('is-active', 'avahi-daemon.service', expected=3, verbose=True)
76 self.systemctl('start','avahi-daemon.service')
77 self.systemctl('is-active', 'avahi-daemon.service', verbose=True)
78
79 @skipUnlessPassed('test_systemd_basic')
80 def test_systemd_disable_enable(self):
81 self.systemctl('disable', 'avahi-daemon.service')
82 self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
83 self.systemctl('enable', 'avahi-daemon.service')
84 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 0000000..5ed1072
--- /dev/null
+++ b/meta/lib/oeqa/runtime/vnc.py
@@ -0,0 +1,19 @@
1from oeqa.oetest import oeRuntimeTest
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 @skipUnlessPassed('test_ssh')
11 def test_vnc(self):
12 (status, output) = self.target.run('x11vnc -display :0 -bg -o x11vnc.log')
13 self.assertEqual(status, 0, msg="x11vnc server failed to start: %s" % output)
14 port = re.search('PORT=[0-9]*', output)
15 self.assertTrue(port, msg="Listening port not specified in command output: %s" %output)
16
17 vncport = port.group(0).split('=')[1]
18 (status, output) = self.target.run('netstat -ntl | grep ":%s"' % vncport)
19 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 0000000..6bad201
--- /dev/null
+++ b/meta/lib/oeqa/runtime/x32lib.py
@@ -0,0 +1,17 @@
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 @skipUnlessPassed("test_ssh")
14 def test_x32_file(self):
15 status1 = self.target.run("readelf -h /bin/ls | grep Class | grep ELF32")[0]
16 status2 = self.target.run("readelf -h /bin/ls | grep Machine | grep X86-64")[0]
17 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 0000000..12dccd8
--- /dev/null
+++ b/meta/lib/oeqa/runtime/xorg.py
@@ -0,0 +1,21 @@
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 @skipUnlessPassed('test_ssh')
18 def test_xorg_error(self):
19 (status, output) = self.target.run('cat /var/log/Xorg.0.log | grep -v "(EE) error," | grep -v "PreInit" | grep -v "evdev:" | grep -v "glx" | grep "(EE)"')
20 self.assertEqual(status, 1, msg="Errors in Xorg log: %s" % output)
21
diff --git a/meta/lib/oeqa/selftest/__init__.py b/meta/lib/oeqa/selftest/__init__.py
new file mode 100644
index 0000000..3ad9513
--- /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 0000000..fc9ae7e
--- /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/base.py b/meta/lib/oeqa/selftest/base.py
new file mode 100644
index 0000000..fc880e9
--- /dev/null
+++ b/meta/lib/oeqa/selftest/base.py
@@ -0,0 +1,129 @@
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
18
19class oeSelfTest(unittest.TestCase):
20
21 log = logging.getLogger("selftest.base")
22 longMessage = True
23
24 def __init__(self, methodName="runTest"):
25 self.builddir = os.environ.get("BUILDDIR")
26 self.localconf_path = os.path.join(self.builddir, "conf/local.conf")
27 self.testinc_path = os.path.join(self.builddir, "conf/selftest.inc")
28 self.testlayer_path = oeSelfTest.testlayer_path
29 self._extra_tear_down_commands = []
30 self._track_for_cleanup = []
31 super(oeSelfTest, self).__init__(methodName)
32
33 def setUp(self):
34 os.chdir(self.builddir)
35 # we don't know what the previous test left around in config or inc files
36 # if it failed so we need a fresh start
37 try:
38 os.remove(self.testinc_path)
39 except OSError as e:
40 if e.errno != errno.ENOENT:
41 raise
42 for root, _, files in os.walk(self.testlayer_path):
43 for f in files:
44 if f == 'test_recipe.inc':
45 os.remove(os.path.join(root, f))
46 # tests might need their own setup
47 # but if they overwrite this one they have to call
48 # super each time, so let's give them an alternative
49 self.setUpLocal()
50
51 def setUpLocal(self):
52 pass
53
54 def tearDown(self):
55 if self._extra_tear_down_commands:
56 failed_extra_commands = []
57 for command in self._extra_tear_down_commands:
58 result = runCmd(command, ignore_status=True)
59 if not result.status == 0:
60 failed_extra_commands.append(command)
61 if failed_extra_commands:
62 self.log.warning("tearDown commands have failed: %s" % ', '.join(map(str, failed_extra_commands)))
63 self.log.debug("Trying to move on.")
64 self._extra_tear_down_commands = []
65
66 if self._track_for_cleanup:
67 for path in self._track_for_cleanup:
68 if os.path.isdir(path):
69 shutil.rmtree(path)
70 if os.path.isfile(path):
71 os.remove(path)
72 self._track_for_cleanup = []
73
74 self.tearDownLocal()
75
76 def tearDownLocal(self):
77 pass
78
79 # add test specific commands to the tearDown method.
80 def add_command_to_tearDown(self, command):
81 self.log.debug("Adding command '%s' to tearDown for this test." % command)
82 self._extra_tear_down_commands.append(command)
83 # add test specific files or directories to be removed in the tearDown method
84 def track_for_cleanup(self, path):
85 self.log.debug("Adding path '%s' to be cleaned up when test is over" % path)
86 self._track_for_cleanup.append(path)
87
88 # write to <builddir>/conf/selftest.inc
89 def write_config(self, data):
90 self.log.debug("Writing to: %s\n%s\n" % (self.testinc_path, data))
91 ftools.write_file(self.testinc_path, data)
92
93 # append to <builddir>/conf/selftest.inc
94 def append_config(self, data):
95 self.log.debug("Appending to: %s\n%s\n" % (self.testinc_path, data))
96 ftools.append_file(self.testinc_path, data)
97
98 # remove data from <builddir>/conf/selftest.inc
99 def remove_config(self, data):
100 self.log.debug("Removing from: %s\n\%s\n" % (self.testinc_path, data))
101 ftools.remove_from_file(self.testinc_path, data)
102
103 # write to meta-sefltest/recipes-test/<recipe>/test_recipe.inc
104 def write_recipeinc(self, recipe, data):
105 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
106 self.log.debug("Writing to: %s\n%s\n" % (inc_file, data))
107 ftools.write_file(inc_file, data)
108
109 # append data to meta-sefltest/recipes-test/<recipe>/test_recipe.inc
110 def append_recipeinc(self, recipe, data):
111 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
112 self.log.debug("Appending to: %s\n%s\n" % (inc_file, data))
113 ftools.append_file(inc_file, data)
114
115 # remove data from meta-sefltest/recipes-test/<recipe>/test_recipe.inc
116 def remove_recipeinc(self, recipe, data):
117 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
118 self.log.debug("Removing from: %s\n%s\n" % (inc_file, data))
119 ftools.remove_from_file(inc_file, data)
120
121 # delete meta-sefltest/recipes-test/<recipe>/test_recipe.inc file
122 def delete_recipeinc(self, recipe):
123 inc_file = os.path.join(self.testlayer_path, 'recipes-test', recipe, 'test_recipe.inc')
124 self.log.debug("Deleting file: %s" % inc_file)
125 try:
126 os.remove(inc_file)
127 except OSError as e:
128 if e.errno != errno.ENOENT:
129 raise
diff --git a/meta/lib/oeqa/selftest/bblayers.py b/meta/lib/oeqa/selftest/bblayers.py
new file mode 100644
index 0000000..52aa4f8
--- /dev/null
+++ b/meta/lib/oeqa/selftest/bblayers.py
@@ -0,0 +1,37 @@
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
10
11class BitbakeLayers(oeSelfTest):
12
13 def test_bitbakelayers_showcrossdepends(self):
14 result = runCmd('bitbake-layers show-cross-depends')
15 self.assertTrue('aspell' in result.output)
16
17 def test_bitbakelayers_showlayers(self):
18 result = runCmd('bitbake-layers show_layers')
19 self.assertTrue('meta-selftest' in result.output)
20
21 def test_bitbakelayers_showappends(self):
22 result = runCmd('bitbake-layers show_appends')
23 self.assertTrue('xcursor-transparent-theme_0.1.1.bbappend' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
24
25 def test_bitbakelayers_showoverlayed(self):
26 result = runCmd('bitbake-layers show_overlayed')
27 self.assertTrue('aspell' in result.output, msg='xcursor-transparent-theme_0.1.1.bbappend file was not recognised')
28
29 def test_bitbakelayers_flatten(self):
30 self.assertFalse(os.path.isdir(os.path.join(self.builddir, 'test')))
31 result = runCmd('bitbake-layers flatten test')
32 bb_file = os.path.join(self.builddir, 'test/recipes-graphics/xcursor-transparent-theme/xcursor-transparent-theme_0.1.1.bb')
33 self.assertTrue(os.path.isfile(bb_file))
34 contents = ftools.read_file(bb_file)
35 find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents)
36 shutil.rmtree(os.path.join(self.builddir, 'test'))
37 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 0000000..6815ecf
--- /dev/null
+++ b/meta/lib/oeqa/selftest/bbtests.py
@@ -0,0 +1,104 @@
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
10
11class BitbakeTests(oeSelfTest):
12
13 def test_run_bitbake_from_dir_1(self):
14 os.chdir(os.path.join(self.builddir, 'conf'))
15 bitbake('-e')
16
17 def test_run_bitbake_from_dir_2(self):
18 my_env = os.environ.copy()
19 my_env['BBPATH'] = my_env['BUILDDIR']
20 os.chdir(os.path.dirname(os.environ['BUILDDIR']))
21 bitbake('-e', env=my_env)
22
23 def test_event_handler(self):
24 self.write_config("INHERIT += \"test_events\"")
25 result = bitbake('m4-native')
26 find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Preparing runqueue", result.output)
27 find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output)
28 self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output)
29 self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output)
30 self.assertFalse('Test for bb.event.InvalidEvent' in result.output)
31
32 def test_local_sstate(self):
33 bitbake('m4-native -ccleansstate')
34 bitbake('m4-native')
35 bitbake('m4-native -cclean')
36 result = bitbake('m4-native')
37 find_setscene = re.search("m4-native.*do_.*_setscene", result.output)
38 self.assertTrue(find_setscene)
39
40 def test_bitbake_invalid_recipe(self):
41 result = bitbake('-b asdf', ignore_status=True)
42 self.assertTrue("ERROR: Unable to find any recipe file matching 'asdf'" in result.output)
43
44 def test_bitbake_invalid_target(self):
45 result = bitbake('asdf', ignore_status=True)
46 self.assertTrue("ERROR: Nothing PROVIDES 'asdf'" in result.output)
47
48 def test_warnings_errors(self):
49 result = bitbake('-b asdf', ignore_status=True)
50 find_warnings = re.search("Summary: There w.{2,3}? [1-9][0-9]* WARNING messages* shown", result.output)
51 find_errors = re.search("Summary: There w.{2,3}? [1-9][0-9]* ERROR messages* shown", result.output)
52 self.assertTrue(find_warnings, msg="Did not find the mumber of warnings at the end of the build:\n" + result.output)
53 self.assertTrue(find_errors, msg="Did not find the mumber of errors at the end of the build:\n" + result.output)
54
55 def test_invalid_patch(self):
56 self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"')
57 result = bitbake('man -c patch', ignore_status=True)
58 self.delete_recipeinc('man')
59 bitbake('-cclean man')
60 self.assertTrue("ERROR: Function failed: patch_do_patch" in result.output)
61
62 def test_force_task(self):
63 bitbake('m4-native')
64 result = bitbake('-C compile m4-native')
65 look_for_tasks = ['do_compile', 'do_install', 'do_populate_sysroot']
66 for task in look_for_tasks:
67 find_task = re.search("m4-native.*%s" % task, result.output)
68 self.assertTrue(find_task)
69
70 def test_bitbake_g(self):
71 result = bitbake('-g core-image-full-cmdline')
72 self.assertTrue('NOTE: PN build list saved to \'pn-buildlist\'' in result.output)
73 self.assertTrue('openssh' in ftools.read_file(os.path.join(self.builddir, 'pn-buildlist')))
74 for f in ['pn-buildlist', 'pn-depends.dot', 'package-depends.dot', 'task-depends.dot']:
75 os.remove(f)
76
77 def test_image_manifest(self):
78 bitbake('core-image-minimal')
79 deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
80 imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
81 manifest = os.path.join(deploydir, imagename + ".manifest")
82 self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image")
83
84 def test_invalid_recipe_src_uri(self):
85 data = 'SRC_URI = "file://invalid"'
86 self.write_recipeinc('man', data)
87 bitbake('-ccleanall man')
88 result = bitbake('-c fetch man', ignore_status=True)
89 bitbake('-ccleanall man')
90 self.delete_recipeinc('man')
91 self.assertEqual(result.status, 1, msg='Command succeded when it should have failed')
92 self.assertTrue('ERROR: Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output)
93 self.assertTrue('ERROR: Function failed: Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.' in result.output)
94
95 def test_rename_downloaded_file(self):
96 data = 'SRC_URI_append = ";downloadfilename=test-aspell.tar.gz"'
97 self.write_recipeinc('aspell', data)
98 bitbake('-ccleanall aspell')
99 result = bitbake('-c fetch aspell', ignore_status=True)
100 self.delete_recipeinc('aspell')
101 self.assertEqual(result.status, 0)
102 self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz')))
103 self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz.done')))
104 bitbake('-ccleanall aspell')
diff --git a/meta/lib/oeqa/selftest/buildhistory.py b/meta/lib/oeqa/selftest/buildhistory.py
new file mode 100644
index 0000000..d8cae46
--- /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 0000000..8ff40ba
--- /dev/null
+++ b/meta/lib/oeqa/selftest/buildoptions.py
@@ -0,0 +1,113 @@
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
10
11class ImageOptionsTests(oeSelfTest):
12
13 def test_incremental_image_generation(self):
14 bitbake("-c cleanall core-image-minimal")
15 self.write_config('INC_RPM_IMAGE_GEN = "1"')
16 self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
17 bitbake("core-image-minimal")
18 res = runCmd("grep 'Installing openssh-sshd' %s" % (os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")), ignore_status=True)
19 self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
20 self.assertEqual(0, res.status, msg="No match for openssh-sshd in log.do_rootfs")
21 bitbake("core-image-minimal")
22 res = runCmd("grep 'Removing openssh-sshd' %s" %(os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")),ignore_status=True)
23 self.assertEqual(0, res.status, msg="openssh-sshd was not removed from image")
24
25 def test_rm_old_image(self):
26 bitbake("core-image-minimal")
27 deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
28 imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
29 deploydir_files = os.listdir(deploydir)
30 track_original_files = []
31 for image_file in deploydir_files:
32 if imagename in image_file and os.path.islink(os.path.join(deploydir, image_file)):
33 track_original_files.append(os.path.realpath(os.path.join(deploydir, image_file)))
34 self.append_config("RM_OLD_IMAGE = \"1\"")
35 bitbake("-C rootfs core-image-minimal")
36 deploydir_files = os.listdir(deploydir)
37 remaining_not_expected = [path for path in track_original_files if os.path.basename(path) in deploydir_files]
38 self.assertFalse(remaining_not_expected, msg="\nThe following image files ware not removed: %s" % ', '.join(map(str, remaining_not_expected)))
39
40 def test_ccache_tool(self):
41 bitbake("ccache-native")
42 self.assertTrue(os.path.isfile(os.path.join(get_bb_var('STAGING_BINDIR_NATIVE', 'ccache-native'), "ccache")))
43 self.write_config('INHERIT += "ccache"')
44 bitbake("m4 -c cleansstate")
45 bitbake("m4 -c compile")
46 res = runCmd("grep ccache %s" % (os.path.join(get_bb_var("WORKDIR","m4"),"temp/log.do_compile")), ignore_status=True)
47 self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile")
48 bitbake("ccache-native -ccleansstate")
49
50
51class DiskMonTest(oeSelfTest):
52
53 def test_stoptask_behavior(self):
54 result = runCmd("df -Pk %s" % os.getcwd())
55 size = result.output.split("\n")[1].split()[3]
56 self.write_config('BB_DISKMON_DIRS = "STOPTASKS,${TMPDIR},%sK,4510K"' % size)
57 res = bitbake("m4", ignore_status = True)
58 self.assertTrue('ERROR: No new tasks can be executed since the disk space monitor action is "STOPTASKS"!' in res.output)
59 self.assertEqual(res.status, 1)
60 self.write_config('BB_DISKMON_DIRS = "ABORT,${TMPDIR},%sK,4510K"' % size)
61 res = bitbake("m4", ignore_status = True)
62 self.assertTrue('ERROR: Immediately abort since the disk space monitor action is "ABORT"!' in res.output)
63 self.assertEqual(res.status, 1)
64 self.write_config('BB_DISKMON_DIRS = "WARN,${TMPDIR},%sK,4510K"' % size)
65 res = bitbake("m4")
66 self.assertTrue('WARNING: The free space' in res.output)
67
68class SanityOptionsTest(oeSelfTest):
69
70 def test_options_warnqa_errorqa_switch(self):
71 bitbake("xcursor-transparent-theme -ccleansstate")
72
73 if "packages-list" not in get_bb_var("ERROR_QA"):
74 self.write_config("ERROR_QA_append = \" packages-list\"")
75
76 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
77 res = bitbake("xcursor-transparent-theme", ignore_status=True)
78 self.delete_recipeinc('xcursor-transparent-theme')
79 self.assertTrue("ERROR: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output)
80 self.assertEqual(res.status, 1)
81 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
82 self.append_config('ERROR_QA_remove = "packages-list"')
83 self.append_config('WARN_QA_append = " packages-list"')
84 res = bitbake("xcursor-transparent-theme")
85 bitbake("xcursor-transparent-theme -ccleansstate")
86 self.delete_recipeinc('xcursor-transparent-theme')
87 self.assertTrue("WARNING: QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors." in res.output)
88
89 def test_sanity_userspace_dependency(self):
90 self.append_config('WARN_QA_append = " unsafe-references-in-binaries unsafe-references-in-scripts"')
91 bitbake("-ccleansstate gzip nfs-utils")
92 res = bitbake("gzip nfs-utils")
93 self.assertTrue("WARNING: QA Issue: gzip" in res.output)
94 self.assertTrue("WARNING: QA Issue: nfs-utils" in res.output)
95
96class BuildhistoryTests(BuildhistoryBase):
97
98 def test_buildhistory_basic(self):
99 self.run_buildhistory_operation('xcursor-transparent-theme')
100 self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')))
101
102 def test_buildhistory_buildtime_pr_backwards(self):
103 self.add_command_to_tearDown('cleanup-workdir')
104 target = 'xcursor-transparent-theme'
105 error = "ERROR: QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1 to .*-r0)" % target
106 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
107 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error)
108
109
110
111
112
113
diff --git a/meta/lib/oeqa/selftest/oescripts.py b/meta/lib/oeqa/selftest/oescripts.py
new file mode 100644
index 0000000..4aab2ed
--- /dev/null
+++ b/meta/lib/oeqa/selftest/oescripts.py
@@ -0,0 +1,60 @@
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
11
12class TestScripts(oeSelfTest):
13
14 def test_cleanup_workdir(self):
15 path = os.path.dirname(get_bb_var('WORKDIR', 'gzip'))
16 old_version_recipe = os.path.join(get_bb_var('COREBASE'), 'meta/recipes-extended/gzip/gzip_1.3.12.bb')
17 old_version = '1.3.12'
18 bitbake("-ccleansstate gzip")
19 bitbake("-ccleansstate -b %s" % old_version_recipe)
20 if os.path.exists(get_bb_var('WORKDIR', "-b %s" % old_version_recipe)):
21 shutil.rmtree(get_bb_var('WORKDIR', "-b %s" % old_version_recipe))
22 if os.path.exists(get_bb_var('WORKDIR', 'gzip')):
23 shutil.rmtree(get_bb_var('WORKDIR', 'gzip'))
24
25 if os.path.exists(path):
26 initial_contents = os.listdir(path)
27 else:
28 initial_contents = []
29
30 bitbake('gzip')
31 intermediary_contents = os.listdir(path)
32 bitbake("-b %s" % old_version_recipe)
33 runCmd('cleanup-workdir')
34 remaining_contents = os.listdir(path)
35
36 expected_contents = [x for x in intermediary_contents if x not in initial_contents]
37 remaining_not_expected = [x for x in remaining_contents if x not in expected_contents]
38 self.assertFalse(remaining_not_expected, msg="Not all necessary content has been deleted from %s: %s" % (path, ', '.join(map(str, remaining_not_expected))))
39 expected_not_remaining = [x for x in expected_contents if x not in remaining_contents]
40 self.assertFalse(expected_not_remaining, msg="The script removed extra contents from %s: %s" % (path, ', '.join(map(str, expected_not_remaining))))
41
42class BuildhistoryDiffTests(BuildhistoryBase):
43
44 def test_buildhistory_diff(self):
45 self.add_command_to_tearDown('cleanup-workdir')
46 target = 'xcursor-transparent-theme'
47 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
48 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True)
49 result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR'))
50 expected_output = 'PR changed from "r1" to "r0"'
51 self.assertTrue(expected_output in result.output, msg="Did not find expected output: %s" % result.output)
52
53
54
55
56
57
58
59
60
diff --git a/meta/lib/oeqa/selftest/prservice.py b/meta/lib/oeqa/selftest/prservice.py
new file mode 100644
index 0000000..789c05f
--- /dev/null
+++ b/meta/lib/oeqa/selftest/prservice.py
@@ -0,0 +1,113 @@
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
11
12class BitbakePrTests(oeSelfTest):
13
14 def get_pr_version(self, package_name):
15 pkgdata_dir = get_bb_var('PKGDATA_DIR')
16 package_data_file = os.path.join(pkgdata_dir, 'runtime', package_name)
17 package_data = ftools.read_file(package_data_file)
18 find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data)
19 self.assertTrue(find_pr)
20 return int(find_pr.group(1))
21
22 def get_task_stamp(self, package_name, recipe_task):
23 stampdata = get_bb_var('STAMP', target=package_name).split('/')
24 prefix = stampdata[-1]
25 package_stamps_path = "/".join(stampdata[:-1])
26 stamps = []
27 for stamp in os.listdir(package_stamps_path):
28 find_stamp = re.match("%s\.%s\.([a-z0-9]{32})" % (prefix, recipe_task), stamp)
29 if find_stamp:
30 stamps.append(find_stamp.group(1))
31 self.assertFalse(len(stamps) == 0, msg="Cound not find stamp for task %s for recipe %s" % (recipe_task, package_name))
32 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))
33 return str(stamps[0])
34
35 def increment_package_pr(self, package_name):
36 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()
37 self.write_recipeinc(package_name, inc_data)
38 bitbake("-ccleansstate %s" % package_name)
39 res = bitbake(package_name, ignore_status=True)
40 self.delete_recipeinc(package_name)
41 self.assertEqual(res.status, 0, msg=res.output)
42 self.assertTrue("NOTE: Started PRServer with DBfile" in res.output, msg=res.output)
43
44 def config_pr_tests(self, package_name, package_type='rpm', pr_socket='localhost:0'):
45 config_package_data = 'PACKAGE_CLASSES = "package_%s"' % package_type
46 self.write_config(config_package_data)
47 config_server_data = 'PRSERV_HOST = "%s"' % pr_socket
48 self.append_config(config_server_data)
49
50 def run_test_pr_service(self, package_name, package_type='rpm', track_task='do_package', pr_socket='localhost:0'):
51 self.config_pr_tests(package_name, package_type, pr_socket)
52
53 self.increment_package_pr(package_name)
54 pr_1 = self.get_pr_version(package_name)
55 stamp_1 = self.get_task_stamp(package_name, track_task)
56
57 self.increment_package_pr(package_name)
58 pr_2 = self.get_pr_version(package_name)
59 stamp_2 = self.get_task_stamp(package_name, track_task)
60
61 bitbake("-ccleansstate %s" % package_name)
62 self.assertTrue(pr_2 - pr_1 == 1)
63 self.assertTrue(stamp_1 != stamp_2)
64
65 def run_test_pr_export_import(self, package_name, replace_current_db=True):
66 self.config_pr_tests(package_name)
67
68 self.increment_package_pr(package_name)
69 pr_1 = self.get_pr_version(package_name)
70
71 exported_db_path = os.path.join(self.builddir, 'export.inc')
72 export_result = runCmd("bitbake-prserv-tool export %s" % exported_db_path, ignore_status=True)
73 self.assertEqual(export_result.status, 0, msg="PR Service database export failed: %s" % export_result.output)
74
75 if replace_current_db:
76 current_db_path = os.path.join(get_bb_var('PERSISTENT_DIR'), 'prserv.sqlite3')
77 self.assertTrue(os.path.exists(current_db_path), msg="Path to current PR Service database is invalid: %s" % current_db_path)
78 os.remove(current_db_path)
79
80 import_result = runCmd("bitbake-prserv-tool import %s" % exported_db_path, ignore_status=True)
81 os.remove(exported_db_path)
82 self.assertEqual(import_result.status, 0, msg="PR Service database import failed: %s" % import_result.output)
83
84 self.increment_package_pr(package_name)
85 pr_2 = self.get_pr_version(package_name)
86
87 bitbake("-ccleansstate %s" % package_name)
88 self.assertTrue(pr_2 - pr_1 == 1)
89
90
91 def test_import_export_replace_db(self):
92 self.run_test_pr_export_import('m4')
93
94 def test_import_export_override_db(self):
95 self.run_test_pr_export_import('m4', replace_current_db=False)
96
97 def test_pr_service_rpm_arch_dep(self):
98 self.run_test_pr_service('m4', 'rpm', 'do_package')
99
100 def test_pr_service_deb_arch_dep(self):
101 self.run_test_pr_service('m4', 'deb', 'do_package')
102
103 def test_pr_service_ipk_arch_dep(self):
104 self.run_test_pr_service('m4', 'ipk', 'do_package')
105
106 def test_pr_service_rpm_arch_indep(self):
107 self.run_test_pr_service('xcursor-transparent-theme', 'rpm', 'do_package')
108
109 def test_pr_service_deb_arch_indep(self):
110 self.run_test_pr_service('xcursor-transparent-theme', 'deb', 'do_package')
111
112 def test_pr_service_ipk_arch_indep(self):
113 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 0000000..5989724
--- /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 0000000..35ff28b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/sstatetests.py
@@ -0,0 +1,193 @@
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 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 def test_sstate_creation_distro_specific_pass(self):
32 self.run_test_sstate_creation(['binutils-cross', 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
33
34 def test_sstate_creation_distro_specific_fail(self):
35 self.run_test_sstate_creation(['binutils-cross', 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False)
36
37 def test_sstate_creation_distro_nonspecific_pass(self):
38 self.run_test_sstate_creation(['eglibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
39
40 def test_sstate_creation_distro_nonspecific_fail(self):
41 self.run_test_sstate_creation(['eglibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
42
43
44 # Test the sstate files deletion part of the do_cleansstate task
45 def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True):
46 self.config_sstate(temp_sstate_location)
47
48 bitbake(['-ccleansstate'] + targets)
49
50 bitbake(targets)
51 tgz_created = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
52 self.assertTrue(tgz_created, msg="Could not find sstate .tgz files for: %s" % ', '.join(map(str, targets)))
53
54 siginfo_created = self.search_sstate('|'.join(map(str, [s + '.*?\.siginfo$' for s in targets])), distro_specific, distro_nonspecific)
55 self.assertTrue(siginfo_created, msg="Could not find sstate .siginfo files for: %s" % ', '.join(map(str, targets)))
56
57 bitbake(['-ccleansstate'] + targets)
58 tgz_removed = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
59 self.assertTrue(not tgz_removed, msg="do_cleansstate didn't remove .tgz sstate files for: %s" % ', '.join(map(str, targets)))
60
61 def test_cleansstate_task_distro_specific_nonspecific(self):
62 self.run_test_cleansstate_task(['binutils-cross', 'binutils-native', 'eglibc-initial'], distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
63
64 def test_cleansstate_task_distro_nonspecific(self):
65 self.run_test_cleansstate_task(['eglibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
66
67 def test_cleansstate_task_distro_specific(self):
68 self.run_test_cleansstate_task(['binutils-cross', 'binutils-native', 'eglibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
69
70
71 # Test rebuilding of distro-specific sstate files
72 def run_test_rebuild_distro_specific_sstate(self, targets, temp_sstate_location=True):
73 self.config_sstate(temp_sstate_location)
74
75 bitbake(['-ccleansstate'] + targets)
76
77 bitbake(targets)
78 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)))
79 file_tracker_1 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
80 self.assertTrue(len(file_tracker_1) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
81
82 self.track_for_cleanup(self.distro_specific_sstate + "_old")
83 shutil.copytree(self.distro_specific_sstate, self.distro_specific_sstate + "_old")
84 shutil.rmtree(self.distro_specific_sstate)
85
86 bitbake(['-cclean'] + targets)
87 bitbake(targets)
88 file_tracker_2 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
89 self.assertTrue(len(file_tracker_2) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
90
91 not_recreated = [x for x in file_tracker_1 if x not in file_tracker_2]
92 self.assertTrue(not_recreated == [], msg="The following sstate files ware not recreated: %s" % ', '.join(map(str, not_recreated)))
93
94 created_once = [x for x in file_tracker_2 if x not in file_tracker_1]
95 self.assertTrue(created_once == [], msg="The following sstate files ware created only in the second run: %s" % ', '.join(map(str, created_once)))
96
97 def test_rebuild_distro_specific_sstate_cross_native_targets(self):
98 self.run_test_rebuild_distro_specific_sstate(['binutils-cross', 'binutils-native'], temp_sstate_location=True)
99
100 def test_rebuild_distro_specific_sstate_cross_target(self):
101 self.run_test_rebuild_distro_specific_sstate(['binutils-cross'], temp_sstate_location=True)
102
103 def test_rebuild_distro_specific_sstate_native_target(self):
104 self.run_test_rebuild_distro_specific_sstate(['binutils-native'], temp_sstate_location=True)
105
106
107 # Test the sstate-cache-management script. Each element in the global_config list is used with the corresponding element in the target_config list
108 # 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)
109 def run_test_sstate_cache_management_script(self, target, global_config=[''], target_config=[''], ignore_patterns=[]):
110 self.assertTrue(global_config)
111 self.assertTrue(target_config)
112 self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements')
113 self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path])
114
115 # If buildhistory is enabled, we need to disable version-going-backwards QA checks for this test. It may report errors otherwise.
116 if ('buildhistory' in get_bb_var('USER_CLASSES')) or ('buildhistory' in get_bb_var('INHERIT')):
117 remove_errors_config = 'ERROR_QA_remove = "version-going-backwards"'
118 self.append_config(remove_errors_config)
119
120 # For not this only checks if random sstate tasks are handled correctly as a group.
121 # In the future we should add control over what tasks we check for.
122
123 sstate_archs_list = []
124 expected_remaining_sstate = []
125 for idx in range(len(target_config)):
126 self.append_config(global_config[idx])
127 self.append_recipeinc(target, target_config[idx])
128 sstate_arch = get_bb_var('SSTATE_PKGARCH', target)
129 if not sstate_arch in sstate_archs_list:
130 sstate_archs_list.append(sstate_arch)
131 if target_config[idx] == target_config[-1]:
132 target_sstate_before_build = self.search_sstate(target + '.*?\.tgz$')
133 bitbake("-cclean %s" % target)
134 result = bitbake(target, ignore_status=True)
135 if target_config[idx] == target_config[-1]:
136 target_sstate_after_build = self.search_sstate(target + '.*?\.tgz$')
137 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)]
138 self.remove_config(global_config[idx])
139 self.remove_recipeinc(target, target_config[idx])
140 self.assertEqual(result.status, 0)
141
142 runCmd("sstate-cache-management.sh -y --cache-dir=%s --remove-duplicated --extra-archs=%s" % (self.sstate_path, ','.join(map(str, sstate_archs_list))))
143 actual_remaining_sstate = [x for x in self.search_sstate(target + '.*?\.tgz$') if not any(pattern in x for pattern in ignore_patterns)]
144
145 actual_not_expected = [x for x in actual_remaining_sstate if x not in expected_remaining_sstate]
146 self.assertFalse(actual_not_expected, msg="Files should have been removed but ware not: %s" % ', '.join(map(str, actual_not_expected)))
147 expected_not_actual = [x for x in expected_remaining_sstate if x not in actual_remaining_sstate]
148 self.assertFalse(expected_not_actual, msg="Extra files ware removed: %s" ', '.join(map(str, expected_not_actual)))
149
150
151 def test_sstate_cache_management_script_using_pr_1(self):
152 global_config = []
153 target_config = []
154 global_config.append('')
155 target_config.append('PR = "0"')
156 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
157
158 def test_sstate_cache_management_script_using_pr_2(self):
159 global_config = []
160 target_config = []
161 global_config.append('')
162 target_config.append('PR = "0"')
163 global_config.append('')
164 target_config.append('PR = "1"')
165 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
166
167 def test_sstate_cache_management_script_using_pr_3(self):
168 global_config = []
169 target_config = []
170 global_config.append('MACHINE = "qemux86-64"')
171 target_config.append('PR = "0"')
172 global_config.append(global_config[0])
173 target_config.append('PR = "1"')
174 global_config.append('MACHINE = "qemux86"')
175 target_config.append('PR = "1"')
176 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
177
178 def test_sstate_cache_management_script_using_machine(self):
179 global_config = []
180 target_config = []
181 global_config.append('MACHINE = "qemux86-64"')
182 target_config.append('')
183 global_config.append('MACHINE = "qemux86"')
184 target_config.append('')
185 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
186
187
188
189
190
191
192
193
diff --git a/meta/lib/oeqa/targetcontrol.py b/meta/lib/oeqa/targetcontrol.py
new file mode 100644
index 0000000..873a664
--- /dev/null
+++ b/meta/lib/oeqa/targetcontrol.py
@@ -0,0 +1,175 @@
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 def __init__(self, d):
48 self.connection = None
49 self.ip = None
50 self.server_ip = None
51 self.datetime = d.getVar('DATETIME', True)
52 self.testdir = d.getVar("TEST_LOG_DIR", True)
53 self.pn = d.getVar("PN", True)
54
55 @abstractmethod
56 def deploy(self):
57
58 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
59 sshloglink = os.path.join(self.testdir, "ssh_target_log")
60 if os.path.islink(sshloglink):
61 os.unlink(sshloglink)
62 os.symlink(self.sshlog, sshloglink)
63 bb.note("SSH log file: %s" % self.sshlog)
64
65 @abstractmethod
66 def start(self, params=None):
67 pass
68
69 @abstractmethod
70 def stop(self):
71 pass
72
73 @abstractmethod
74 def restart(self, params=None):
75 pass
76
77 def run(self, cmd, timeout=None):
78 return self.connection.run(cmd, timeout)
79
80 def copy_to(self, localpath, remotepath):
81 return self.connection.copy_to(localpath, remotepath)
82
83 def copy_from(self, remotepath, localpath):
84 return self.connection.copy_from(remotepath, localpath)
85
86
87
88class QemuTarget(BaseTarget):
89
90 def __init__(self, d):
91
92 super(QemuTarget, self).__init__(d)
93
94 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
95 self.origrootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.ext3')
96 self.rootfs = os.path.join(self.testdir, d.getVar("IMAGE_LINK_NAME", True) + '-testimage.ext3')
97
98 self.runner = QemuRunner(machine=d.getVar("MACHINE", True),
99 rootfs=self.rootfs,
100 tmpdir = d.getVar("TMPDIR", True),
101 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
102 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
103 logfile = self.qemulog,
104 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)))
105
106 def deploy(self):
107 try:
108 shutil.copyfile(self.origrootfs, self.rootfs)
109 except Exception as e:
110 bb.fatal("Error copying rootfs: %s" % e)
111
112 qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
113 if os.path.islink(qemuloglink):
114 os.unlink(qemuloglink)
115 os.symlink(self.qemulog, qemuloglink)
116
117 bb.note("rootfs file: %s" % self.rootfs)
118 bb.note("Qemu log file: %s" % self.qemulog)
119 super(QemuTarget, self).deploy()
120
121 def start(self, params=None):
122 if self.runner.start(params):
123 self.ip = self.runner.ip
124 self.server_ip = self.runner.server_ip
125 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
126 else:
127 raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
128
129 def stop(self):
130 self.runner.stop()
131 self.connection = None
132 self.ip = None
133 self.server_ip = None
134
135 def restart(self, params=None):
136 if self.runner.restart(params):
137 self.ip = self.runner.ip
138 self.server_ip = self.runner.server_ip
139 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
140 else:
141 raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
142
143
144class SimpleRemoteTarget(BaseTarget):
145
146 def __init__(self, d):
147 super(SimpleRemoteTarget, self).__init__(d)
148 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.')
149 self.ip = addr.split(":")[0]
150 try:
151 self.port = addr.split(":")[1]
152 except IndexError:
153 self.port = None
154 bb.note("Target IP: %s" % self.ip)
155 self.server_ip = d.getVar("TEST_SERVER_IP", True)
156 if not self.server_ip:
157 try:
158 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
159 except Exception as e:
160 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)
161 bb.note("Server IP: %s" % self.server_ip)
162
163 def deploy(self):
164 super(SimpleRemoteTarget, self).deploy()
165
166 def start(self, params=None):
167 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
168
169 def stop(self):
170 self.connection = None
171 self.ip = None
172 self.server_ip = None
173
174 def restart(self, params=None):
175 pass
diff --git a/meta/lib/oeqa/utils/__init__.py b/meta/lib/oeqa/utils/__init__.py
new file mode 100644
index 0000000..8eda927
--- /dev/null
+++ b/meta/lib/oeqa/utils/__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/utils/commands.py b/meta/lib/oeqa/utils/commands.py
new file mode 100644
index 0000000..9b42620
--- /dev/null
+++ b/meta/lib/oeqa/utils/commands.py
@@ -0,0 +1,137 @@
1# Copyright (c) 2013 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
17
18class Command(object):
19 def __init__(self, command, bg=False, timeout=None, data=None, **options):
20
21 self.defaultopts = {
22 "stdout": subprocess.PIPE,
23 "stderr": subprocess.STDOUT,
24 "stdin": None,
25 "shell": False,
26 "bufsize": -1,
27 }
28
29 self.cmd = command
30 self.bg = bg
31 self.timeout = timeout
32 self.data = data
33
34 self.options = dict(self.defaultopts)
35 if isinstance(self.cmd, basestring):
36 self.options["shell"] = True
37 if self.data:
38 self.options['stdin'] = subprocess.PIPE
39 self.options.update(options)
40
41 self.status = None
42 self.output = None
43 self.error = None
44 self.thread = None
45
46 self.log = logging.getLogger("utils.commands")
47
48 def run(self):
49 self.process = subprocess.Popen(self.cmd, **self.options)
50
51 def commThread():
52 self.output, self.error = self.process.communicate(self.data)
53
54 self.thread = threading.Thread(target=commThread)
55 self.thread.start()
56
57 self.log.debug("Running command '%s'" % self.cmd)
58
59 if not self.bg:
60 self.thread.join(self.timeout)
61 self.stop()
62
63 def stop(self):
64 if self.thread.isAlive():
65 self.process.terminate()
66 # let's give it more time to terminate gracefully before killing it
67 self.thread.join(5)
68 if self.thread.isAlive():
69 self.process.kill()
70 self.thread.join()
71
72 self.output = self.output.rstrip()
73 self.status = self.process.poll()
74
75 self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status))
76 # logging the complete output is insane
77 # bitbake -e output is really big
78 # and makes the log file useless
79 if self.status:
80 lout = "\n".join(self.output.splitlines()[-20:])
81 self.log.debug("Last 20 lines:\n%s" % lout)
82
83
84class Result(object):
85 pass
86
87def runCmd(command, ignore_status=False, timeout=None, **options):
88
89 result = Result()
90
91 cmd = Command(command, timeout=timeout, **options)
92 cmd.run()
93
94 result.command = command
95 result.status = cmd.status
96 result.output = cmd.output
97 result.pid = cmd.process.pid
98
99 if result.status and not ignore_status:
100 raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, result.output))
101
102 return result
103
104
105def bitbake(command, ignore_status=False, timeout=None, **options):
106 if isinstance(command, basestring):
107 cmd = "bitbake " + command
108 else:
109 cmd = [ "bitbake" ] + command
110
111 return runCmd(cmd, ignore_status, timeout, **options)
112
113
114def get_bb_env(target=None):
115 if target:
116 return runCmd("bitbake -e %s" % target).output
117 else:
118 return runCmd("bitbake -e").output
119
120def get_bb_var(var, target=None):
121 val = None
122 bbenv = get_bb_env(target)
123 for line in bbenv.splitlines():
124 if line.startswith(var + "="):
125 val = line.split('=')[1]
126 val = val.replace('\"','')
127 break
128 return val
129
130def get_test_layer():
131 layers = get_bb_var("BBLAYERS").split()
132 testlayer = None
133 for l in layers:
134 if "/meta-selftest" in l and os.path.isdir(l):
135 testlayer = l
136 break
137 return testlayer
diff --git a/meta/lib/oeqa/utils/decorators.py b/meta/lib/oeqa/utils/decorators.py
new file mode 100644
index 0000000..b99da8d
--- /dev/null
+++ b/meta/lib/oeqa/utils/decorators.py
@@ -0,0 +1,50 @@
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
9from oeqa.oetest import *
10
11class skipIfFailure(object):
12
13 def __init__(self,testcase):
14 self.testcase = testcase
15
16 def __call__(self,f):
17 def wrapped_f(*args):
18 if self.testcase in (oeTest.testFailures or oeTest.testErrors):
19 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
20 return f(*args)
21 wrapped_f.__name__ = f.__name__
22 return wrapped_f
23
24class skipIfSkipped(object):
25
26 def __init__(self,testcase):
27 self.testcase = testcase
28
29 def __call__(self,f):
30 def wrapped_f(*args):
31 if self.testcase in oeTest.testSkipped:
32 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
33 return f(*args)
34 wrapped_f.__name__ = f.__name__
35 return wrapped_f
36
37class skipUnlessPassed(object):
38
39 def __init__(self,testcase):
40 self.testcase = testcase
41
42 def __call__(self,f):
43 def wrapped_f(*args):
44 if self.testcase in oeTest.testSkipped or \
45 self.testcase in oeTest.testFailures or \
46 self.testcase in oeTest.testErrors:
47 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
48 return f(*args)
49 wrapped_f.__name__ = f.__name__
50 return wrapped_f
diff --git a/meta/lib/oeqa/utils/ftools.py b/meta/lib/oeqa/utils/ftools.py
new file mode 100644
index 0000000..64ebe3d
--- /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 0000000..f161a1b
--- /dev/null
+++ b/meta/lib/oeqa/utils/httpserver.py
@@ -0,0 +1,33 @@
1import SimpleHTTPServer
2import multiprocessing
3import os
4
5class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer):
6
7 def server_start(self, root_dir):
8 os.chdir(root_dir)
9 self.serve_forever()
10
11class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
12
13 def log_message(self, format_str, *args):
14 pass
15
16class HTTPService(object):
17
18 def __init__(self, root_dir, host=''):
19 self.root_dir = root_dir
20 self.host = host
21 self.port = 0
22
23 def start(self):
24 self.server = HTTPServer((self.host, self.port), HTTPRequestHandler)
25 if self.port == 0:
26 self.port = self.server.server_port
27 self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir])
28 self.process.start()
29
30 def stop(self):
31 self.server.server_close()
32 self.process.terminate()
33 self.process.join()
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
new file mode 100644
index 0000000..f1a7e24
--- /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 0000000..d355d5e
--- /dev/null
+++ b/meta/lib/oeqa/utils/sshcontrol.py
@@ -0,0 +1,127 @@
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
33 def run(self, command, timeout=None):
34 self.starttime = time.time()
35 output = ''
36 self.process = subprocess.Popen(command, **self.options)
37 if timeout:
38 endtime = self.starttime + timeout
39 eof = False
40 while time.time() < endtime and not eof:
41 if select.select([self.process.stdout], [], [], 5)[0] != []:
42 data = os.read(self.process.stdout.fileno(), 1024)
43 if not data:
44 self.process.stdout.close()
45 eof = True
46 else:
47 output += data
48 endtime = time.time() + timeout
49
50 # process hasn't returned yet
51 if not eof:
52 self.process.terminate()
53 time.sleep(5)
54 try:
55 self.process.kill()
56 except OSError:
57 pass
58 output += "\nProcess killed - no output for %d seconds. Total running time: %d seconds." % (timeout, time.time() - self.starttime)
59 else:
60 output = self.process.communicate()[0]
61
62 self.status = self.process.wait()
63 self.output = output.rstrip()
64 return (self.status, self.output)
65
66
67class SSHControl(object):
68 def __init__(self, ip, logfile=None, timeout=300, user='root', port=None):
69 self.ip = ip
70 self.defaulttimeout = timeout
71 self.ignore_status = True
72 self.logfile = logfile
73 self.user = user
74 self.ssh_options = [
75 '-o', 'UserKnownHostsFile=/dev/null',
76 '-o', 'StrictHostKeyChecking=no',
77 '-o', 'LogLevel=ERROR'
78 ]
79 self.ssh = ['ssh', '-l', self.user ] + self.ssh_options
80 self.scp = ['scp'] + self.ssh_options
81 if port:
82 self.ssh = self.ssh + [ '-p', port ]
83 self.scp = self.scp + [ '-P', port ]
84
85 def log(self, msg):
86 if self.logfile:
87 with open(self.logfile, "a") as f:
88 f.write("%s\n" % msg)
89
90 def _internal_run(self, command, timeout=None, ignore_status = True):
91 self.log("[Running]$ %s" % " ".join(command))
92
93 proc = SSHProcess()
94 status, output = proc.run(command, timeout)
95
96 self.log("%s" % output)
97 self.log("[Command returned '%d' after %.2f seconds]" % (status, time.time() - proc.starttime))
98
99 if status and not ignore_status:
100 raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, status, output))
101
102 return (status, output)
103
104 def run(self, command, timeout=None):
105 """
106 command - ssh command to run
107 timeout=<val> - kill command if there is no output after <val> seconds
108 timeout=None - kill command if there is no output after a default value seconds
109 timeout=0 - no timeout, let command run until it returns
110 """
111
112 # We need to source /etc/profile for a proper PATH on the target
113 command = self.ssh + [self.ip, ' . /etc/profile; ' + command]
114
115 if timeout is None:
116 return self._internal_run(command, self.defaulttimeout, self.ignore_status)
117 if timeout == 0:
118 return self._internal_run(command, None, self.ignore_status)
119 return self._internal_run(command, timeout, self.ignore_status)
120
121 def copy_to(self, localpath, remotepath):
122 command = self.scp + [localpath, '%s@%s:%s' % (self.user, self.ip, remotepath)]
123 return self._internal_run(command, ignore_status=False)
124
125 def copy_from(self, remotepath, localpath):
126 command = self.scp + ['%s@%s:%s' % (self.user, self.ip, remotepath), localpath]
127 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 0000000..3229676
--- /dev/null
+++ b/meta/lib/oeqa/utils/targetbuild.py
@@ -0,0 +1,68 @@
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 subprocess
10
11
12class TargetBuildProject():
13
14 def __init__(self, target, d, uri, foldername=None):
15 self.target = target
16 self.d = d
17 self.uri = uri
18 self.targetdir = "~/"
19 self.archive = os.path.basename(uri)
20 self.localarchive = "/tmp/" + self.archive
21 self.fname = re.sub(r'.tar.bz2|tar.gz$', '', self.archive)
22 if foldername:
23 self.fname = foldername
24
25 def download_archive(self):
26
27 exportvars = ['HTTP_PROXY', 'http_proxy',
28 'HTTPS_PROXY', 'https_proxy',
29 'FTP_PROXY', 'ftp_proxy',
30 'FTPS_PROXY', 'ftps_proxy',
31 'NO_PROXY', 'no_proxy',
32 'ALL_PROXY', 'all_proxy',
33 'SOCKS5_USER', 'SOCKS5_PASSWD']
34
35 cmd = ''
36 for var in exportvars:
37 val = self.d.getVar(var, True)
38 if val:
39 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
40
41 cmd = cmd + "wget -O %s %s" % (self.localarchive, self.uri)
42 subprocess.check_call(cmd, shell=True)
43
44 (status, output) = self.target.copy_to(self.localarchive, self.targetdir)
45 if status != 0:
46 raise Exception("Failed to copy archive to target, output: %s" % output)
47
48 (status, output) = self.target.run('tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir))
49 if status != 0:
50 raise Exception("Failed to extract archive, output: %s" % output)
51
52 #Change targetdir to project folder
53 self.targetdir = self.targetdir + self.fname
54
55 # The timeout parameter of target.run is set to 0 to make the ssh command
56 # run with no timeout.
57 def run_configure(self):
58 return self.target.run('cd %s; ./configure' % self.targetdir, 0)[0]
59
60 def run_make(self):
61 return self.target.run('cd %s; make' % self.targetdir, 0)[0]
62
63 def run_install(self):
64 return self.target.run('cd %s; make install' % self.targetdir, 0)[0]
65
66 def clean(self):
67 self.target.run('rm -rf %s' % self.targetdir)
68 subprocess.call('rm -f %s' % self.localarchive, shell=True)