Openembedded Core Discussions
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework
@ 2013-07-05  9:27 Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 1/8] classes/testimage.bbclass: new class for image tests Stefan Stanacar
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core


Hello,

This is the proposed implementation of a new runtime tests framework based on python unittest..
It's mean to ease qemu image testing and encourage developers to add more tests similar to the example tests provided (all tests are basically commands ran over ssh)

You can try it out like this:
 - first build a qemu core-image-sato (a minimal wouldn't be interesting at all)
 - add INHERIT += "testimage" in local.conf
 - then bitbake core-image-sato -c testimage. That will run a standard suite of tests.

You can set TEST_SUITES = "ping ssh <test name>" in local.conf to force run only certain tests (order matters here, it's the order in which tests run). You can also append "auto" and it will also run whatever tests are suitable for the image (if that was a sato-sdk image and you set TEST_SUITE = "ping ssh rpm auto" you force run ping, ssh and rpm but you also get smart, connman and gcc tests).

Check the task log (log.do_testimage) in WORKDIR to see the results. Also a ssh log (what command is running, output and return codes) and qemu boot log are kept in WORKDIR/testimage/

There are some areas for improvement:
 - a better way of getting the list of installed packages in a image (currently it uses the installed_packages.txt file in WORKDIR)
 - qemu is started with the -snapshot option, we should create a copy of the original rootfs instead (so that we can keep copy of the tested image)
 - when using TEST_SUITES = "auto" there is NO dependency at all between tests (rpm would run before ssh test)
 - right know the class assumes qemu machines, plan is to abstract it to allow provision of real hardware too.
 - other I don't remember right now :)

Changed in v2:
 - renamed oeTelnetlib to oeQemuConsole
 - added some tests for xorg and systemd
 - increased timeouts for starting qemu a little bit
 - move the unittest runner from the task to oetest module
 - print and check DISPLAY value
 - some comments and strings changes

I've run this on my AB instance with this applied http://git.yoctoproject.org/cgit/cgit.cgi/poky-contrib/commit/?h=stefans/yab-master2&id=9cef75a5cc360629dd4ad32e121d9a9936cf2b5f

Comments and feedback are most welcome!

Regards,
Stefan

The following changes since commit 8a186a6b3853fc1a7dcf342d421c8926c38949c9:

  bitbake: hob: save button from settings called a nonexisting method (2013-07-03 08:13:35 +0100)

are available in the git repository at:

  git://git.yoctoproject.org/poky-contrib stefans/oeqa7

for you to fetch changes up to cee1fb0376c3440106552800af179cfb9df97b96:

  lib/oeqa/runtime: add gcc test (2013-07-05 11:36:08 +0300)

----------------------------------------------------------------
Radu Moisan (2):
      lib/oeqa/utils/qemurunner.py: class to handle qemu instance
      lib/oeqa/utils/decorators.py: decorators for test methods

Stefan Stanacar (6):
      classes/testimage.bbclass: new class for image tests
      lib/oeqa/oetest.py: base module for all runtime unittests
      lib/oeqa/utils/sshcontrol.py: helper module for running remote commands
      lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection
      lib/oeqa/runtime: image sanity tests
      lib/oeqa/runtime: add gcc test

 meta/classes/testimage.bbclass           |  92 +++++++++++++++++
 meta/lib/oeqa/__init__.py                |   0
 meta/lib/oeqa/oetest.py                  | 101 +++++++++++++++++++
 meta/lib/oeqa/runtime/__init__.py        |   0
 meta/lib/oeqa/runtime/connman.py         |  34 +++++++
 meta/lib/oeqa/runtime/dmesg.py           |  13 +++
 meta/lib/oeqa/runtime/files/test.c       |  26 +++++
 meta/lib/oeqa/runtime/files/testmakefile |   5 +
 meta/lib/oeqa/runtime/gcc.py             |  36 +++++++
 meta/lib/oeqa/runtime/multilib.py        |  14 +++
 meta/lib/oeqa/runtime/ping.py            |  11 +++
 meta/lib/oeqa/runtime/rpm.py             |  25 +++++
 meta/lib/oeqa/runtime/smart.py           |  27 +++++
 meta/lib/oeqa/runtime/ssh.py             |  16 +++
 meta/lib/oeqa/runtime/systemd.py         |  24 +++++
 meta/lib/oeqa/runtime/xorg.py            |  21 ++++
 meta/lib/oeqa/utils/__init__.py          |   0
 meta/lib/oeqa/utils/decorators.py        |  40 ++++++++
 meta/lib/oeqa/utils/oeqemuconsole.py     |  45 +++++++++
 meta/lib/oeqa/utils/qemurunner.py        | 164 +++++++++++++++++++++++++++++++
 meta/lib/oeqa/utils/sshcontrol.py        | 100 +++++++++++++++++++
 scripts/runqemu                          |   2 +-
 22 files changed, 795 insertions(+), 1 deletion(-)
 create mode 100644 meta/classes/testimage.bbclass
 create mode 100644 meta/lib/oeqa/__init__.py
 create mode 100644 meta/lib/oeqa/oetest.py
 create mode 100644 meta/lib/oeqa/runtime/__init__.py
 create mode 100644 meta/lib/oeqa/runtime/connman.py
 create mode 100644 meta/lib/oeqa/runtime/dmesg.py
 create mode 100644 meta/lib/oeqa/runtime/files/test.c
 create mode 100644 meta/lib/oeqa/runtime/files/testmakefile
 create mode 100644 meta/lib/oeqa/runtime/gcc.py
 create mode 100644 meta/lib/oeqa/runtime/multilib.py
 create mode 100644 meta/lib/oeqa/runtime/ping.py
 create mode 100644 meta/lib/oeqa/runtime/rpm.py
 create mode 100644 meta/lib/oeqa/runtime/smart.py
 create mode 100644 meta/lib/oeqa/runtime/ssh.py
 create mode 100644 meta/lib/oeqa/runtime/systemd.py
 create mode 100644 meta/lib/oeqa/runtime/xorg.py
 create mode 100644 meta/lib/oeqa/utils/__init__.py
 create mode 100644 meta/lib/oeqa/utils/decorators.py
 create mode 100644 meta/lib/oeqa/utils/oeqemuconsole.py
 create mode 100644 meta/lib/oeqa/utils/qemurunner.py
 create mode 100644 meta/lib/oeqa/utils/sshcontrol.py

Radu Moisan (2):
  lib/oeqa/utils/qemurunner.py: class to handle qemu instance
  lib/oeqa/utils/decorators.py: decorators for test methods

Stefan Stanacar (6):
  classes/testimage.bbclass: new class for image tests
  lib/oeqa/oetest.py: base module for all runtime unittests
  lib/oeqa/utils/sshcontrol.py: helper module for running remote
    commands
  lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection
  lib/oeqa/runtime: image sanity tests
  lib/oeqa/runtime: add gcc test

 meta/classes/testimage.bbclass           |  92 +++++++++++++++++
 meta/lib/oeqa/__init__.py                |   0
 meta/lib/oeqa/oetest.py                  | 101 +++++++++++++++++++
 meta/lib/oeqa/runtime/__init__.py        |   0
 meta/lib/oeqa/runtime/connman.py         |  34 +++++++
 meta/lib/oeqa/runtime/dmesg.py           |  13 +++
 meta/lib/oeqa/runtime/files/test.c       |  26 +++++
 meta/lib/oeqa/runtime/files/testmakefile |   5 +
 meta/lib/oeqa/runtime/gcc.py             |  36 +++++++
 meta/lib/oeqa/runtime/multilib.py        |  14 +++
 meta/lib/oeqa/runtime/ping.py            |  11 +++
 meta/lib/oeqa/runtime/rpm.py             |  25 +++++
 meta/lib/oeqa/runtime/smart.py           |  27 +++++
 meta/lib/oeqa/runtime/ssh.py             |  16 +++
 meta/lib/oeqa/runtime/systemd.py         |  24 +++++
 meta/lib/oeqa/runtime/xorg.py            |  21 ++++
 meta/lib/oeqa/utils/__init__.py          |   0
 meta/lib/oeqa/utils/decorators.py        |  40 ++++++++
 meta/lib/oeqa/utils/oeqemuconsole.py     |  45 +++++++++
 meta/lib/oeqa/utils/qemurunner.py        | 164 +++++++++++++++++++++++++++++++
 meta/lib/oeqa/utils/sshcontrol.py        | 100 +++++++++++++++++++
 scripts/runqemu                          |   2 +-
 22 files changed, 795 insertions(+), 1 deletion(-)
 create mode 100644 meta/classes/testimage.bbclass
 create mode 100644 meta/lib/oeqa/__init__.py
 create mode 100644 meta/lib/oeqa/oetest.py
 create mode 100644 meta/lib/oeqa/runtime/__init__.py
 create mode 100644 meta/lib/oeqa/runtime/connman.py
 create mode 100644 meta/lib/oeqa/runtime/dmesg.py
 create mode 100644 meta/lib/oeqa/runtime/files/test.c
 create mode 100644 meta/lib/oeqa/runtime/files/testmakefile
 create mode 100644 meta/lib/oeqa/runtime/gcc.py
 create mode 100644 meta/lib/oeqa/runtime/multilib.py
 create mode 100644 meta/lib/oeqa/runtime/ping.py
 create mode 100644 meta/lib/oeqa/runtime/rpm.py
 create mode 100644 meta/lib/oeqa/runtime/smart.py
 create mode 100644 meta/lib/oeqa/runtime/ssh.py
 create mode 100644 meta/lib/oeqa/runtime/systemd.py
 create mode 100644 meta/lib/oeqa/runtime/xorg.py
 create mode 100644 meta/lib/oeqa/utils/__init__.py
 create mode 100644 meta/lib/oeqa/utils/decorators.py
 create mode 100644 meta/lib/oeqa/utils/oeqemuconsole.py
 create mode 100644 meta/lib/oeqa/utils/qemurunner.py
 create mode 100644 meta/lib/oeqa/utils/sshcontrol.py

-- 
1.8.1.4



^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 1/8] classes/testimage.bbclass: new class for image tests
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 2/8] lib/oeqa/oetest.py: base module for all runtime unittests Stefan Stanacar
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

Replacement class for imagetest-qemu.bbclass. It launches a qemu instance and
runs test modules defined in TEST_SUITES.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
Signed-off-by: Radu Moisan <radu.moisan@intel.com>
---
 meta/classes/testimage.bbclass | 92 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)
 create mode 100644 meta/classes/testimage.bbclass

diff --git a/meta/classes/testimage.bbclass b/meta/classes/testimage.bbclass
new file mode 100644
index 0000000..35c6811
--- /dev/null
+++ b/meta/classes/testimage.bbclass
@@ -0,0 +1,92 @@
+TEST_LOG_DIR ?= "${WORKDIR}/testimage"
+
+DEFAULT_TEST_SUITES = "ping auto"
+DEFAULT_TEST_SUITES_pn-core-image-minimal = "ping"
+DEFAULT_TEST_SUITES_pn-core-image-sato = "ping ssh connman rpm smart xorg dmesg"
+DEFAULT_TEST_SUITES_pn-core-image-sato-sdk = "ping ssh connman rpm smart gcc xorg dmesg"
+
+TEST_SUITES ?= "${DEFAULT_TEST_SUITES}"
+
+python do_testimage() {
+    testimage_main(d)
+}
+addtask testimage
+do_testimage[nostamp] = "1"
+do_testimage[depends] += "qemu-native:do_populate_sysroot"
+do_testimage[depends] += "qemu-helper-native:do_populate_sysroot"
+
+def testimage_main(d):
+    import unittest
+    import os
+    import oeqa.runtime
+    import re
+    from oeqa.oetest import runTests
+    from oeqa.utils.sshcontrol import SSHControl
+    from oeqa.utils.qemurunner import QemuRunner
+
+    testdir = d.getVar("TEST_LOG_DIR", True)
+    bb.utils.mkdirhier(testdir)
+    sshlog = os.path.join(testdir, "ssh_target_log.%s" % d.getVar('DATETIME', True))
+    sshloglink = os.path.join(testdir, "ssh_target_log")
+    if os.path.islink(sshloglink):
+        os.unlink(sshloglink)
+    os.symlink(sshlog, sshloglink)
+
+    # tests in TEST_SUITES become required tests
+    # they won't be skipped even if they aren't suitable for a default image (like xorg for minimal)
+    testsuites = d.getVar("TEST_SUITES", True)
+    # testslist is what we'll run and order matters
+    testslist = [ x for x in testsuites.split() if x != "auto" ]
+    # if we have auto search for other modules
+    if "auto" in testsuites:
+        for f in os.listdir(os.path.dirname(os.path.abspath(oeqa.runtime.__file__))):
+            if f.endswith('.py') and not f.startswith('_')  and f[:-3] not in testslist:
+                testslist.append(f[:-3])
+
+    testslist = [ "oeqa.runtime." + x for x in testslist ]
+
+    class TestContext:
+        def __init__(self):
+            self.d = d
+            self.testslist = testslist
+            self.testsrequired = testsuites.split()
+            self.filesdir = os.path.join(os.path.dirname(os.path.abspath(oeqa.runtime.__file__)),"files")
+
+    # test context
+    tc = TestContext()
+
+    # prepare qemu instance
+    # and boot each supported fs type
+    machine=d.getVar("MACHINE", True)
+    #will handle fs type eventually, stick with ext3 for now
+    rootfs=d.getVar("DEPLOY_DIR_IMAGE", True) + '/' + d.getVar("IMAGE_BASENAME",True) + '-' + machine + '.ext3'
+
+    qemu = QemuRunner(machine, rootfs)
+    qemu.tmpdir = d.getVar("TMPDIR", True)
+    qemu.display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True)
+    qemu.logfile = os.path.join(testdir, "qemu_boot_log.%s" % d.getVar('DATETIME', True))
+
+    bb.note("DISPLAY value: %s" % qemu.display)
+    bb.note("rootfs file: %s" %  rootfs)
+    bb.note("Qemu logfile: %s" % qemu.logfile)
+
+    #catch exceptions when loading or running tests (mostly our own errors)
+    try:
+        if qemu.launch():
+
+            # set more context - ssh instance and qemu
+            # we do these here because we needed qemu to boot and get the ip
+            tc.qemu = qemu
+            tc.target = SSHControl(host=qemu.ip,logfile=sshlog)
+            # run tests and get the results
+            result = runTests(tc)
+
+            if result.wasSuccessful():
+                bb.note("All required tests passed")
+            else:
+                raise bb.build.FuncFailed("Some tests failed. You should check the task log and the ssh log. (ssh log is %s" % sshlog)
+
+        else:
+            raise bb.build.FuncFailed("Failed to start qemu. You should check the task log and the qemu boot log (qemu log is %s)" % qemu.logfile)
+    finally:
+        qemu.kill()
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 2/8] lib/oeqa/oetest.py: base module for all runtime unittests
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 1/8] classes/testimage.bbclass: new class for image tests Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands Stefan Stanacar
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

This module contains the base class for all runtime tests
and some helper methods.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
Signed-off-by: Radu Moisan <radu.moisan@intel.com>
---
 meta/lib/oeqa/__init__.py |   0
 meta/lib/oeqa/oetest.py   | 101 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)
 create mode 100644 meta/lib/oeqa/__init__.py
 create mode 100644 meta/lib/oeqa/oetest.py

diff --git a/meta/lib/oeqa/__init__.py b/meta/lib/oeqa/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/meta/lib/oeqa/oetest.py b/meta/lib/oeqa/oetest.py
new file mode 100644
index 0000000..24548e9
--- /dev/null
+++ b/meta/lib/oeqa/oetest.py
@@ -0,0 +1,101 @@
+import os, re, mmap
+import unittest
+import inspect
+import bb
+from oeqa.utils.sshcontrol import SSHControl
+
+
+def runTests(tc):
+
+    # set the context object passed from the test class
+    setattr(oeRuntimeTest, "tc", tc)
+
+    # prepare test suite, loader and runner
+    suite = unittest.TestSuite()
+    testloader = unittest.TestLoader()
+    testloader.sortTestMethodsUsing = None
+    runner = unittest.TextTestRunner(verbosity=2)
+
+    bb.note("Test modules  %s" % tc.testslist)
+    suite = testloader.loadTestsFromNames(tc.testslist)
+    bb.note("Found %s tests" % suite.countTestCases())
+
+    result = runner.run(suite)
+
+    return result
+
+
+
+class oeRuntimeTest(unittest.TestCase):
+    testFailures = []
+    testSkipped = []
+    testErrors = []
+    pscmd = "ps"
+
+    def __init__(self, methodName='runTest'):
+        self.target = oeRuntimeTest.tc.target
+        super(oeRuntimeTest, self).__init__(methodName)
+
+
+    def run(self, result=None):
+        super(oeRuntimeTest, self).run(result)
+
+        # we add to our own lists the results, we use those for decorators
+        if len(result.failures) > len(oeRuntimeTest.testFailures):
+            oeRuntimeTest.testFailures.append(str(result.failures[-1][0]).split()[0])
+        if len(result.skipped) > len(oeRuntimeTest.testSkipped):
+            oeRuntimeTest.testSkipped.append(str(result.skipped[-1][0]).split()[0])
+        if len(result.errors) > len(oeRuntimeTest.testErrors):
+            oeRuntimeTest.testErrors.append(str(result.errors[-1][0]).split()[0])
+
+    @classmethod
+    def hasPackage(self, pkg):
+
+        pkgfile = os.path.join(oeRuntimeTest.tc.d.getVar("WORKDIR", True), "installed_pkgs.txt")
+
+        with open(pkgfile) as f:
+            data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
+            match = re.search(pkg, data)
+            data.close()
+
+        if match:
+            return True
+
+        return False
+
+    @classmethod
+    def hasFeature(self,feature):
+
+        if feature in oeRuntimeTest.tc.d.getVar("IMAGE_FEATURES", True).split() or \
+                feature in oeRuntimeTest.tc.d.getVar("DISTRO_FEATURES", True).split():
+            return True
+        else:
+            return False
+
+
+
+
+def getmodule(pos=2):
+    # stack returns a list of tuples containg frame information
+    # First element of the list the is current frame, caller is 1
+    frameinfo = inspect.stack()[pos]
+    modname = inspect.getmodulename(frameinfo[1])
+    #modname = inspect.getmodule(frameinfo[0]).__name__
+    return modname
+
+def skipModule(reason, pos=2):
+    modname = getmodule(pos)
+    if modname not in oeRuntimeTest.tc.testsrequired:
+        raise unittest.SkipTest("%s: %s" % (modname, reason))
+    else:
+        bb.warn("Test %s is required, not skipping" % modname)
+
+def skipModuleIf(cond, reason):
+
+    if cond:
+        skipModule(reason, 3)
+
+def skipModuleUnless(cond, reason):
+
+    if not cond:
+        skipModule(reason, 3)
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 1/8] classes/testimage.bbclass: new class for image tests Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 2/8] lib/oeqa/oetest.py: base module for all runtime unittests Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-07 21:55   ` Colin Walters
  2013-07-05  9:27 ` [RFC PATCH v2 4/8] lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection Stefan Stanacar
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

Provides a class for setting up ssh connections,
running commands and copying files to/from a target.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
---
 meta/lib/oeqa/utils/__init__.py   |   0
 meta/lib/oeqa/utils/sshcontrol.py | 100 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 100 insertions(+)
 create mode 100644 meta/lib/oeqa/utils/__init__.py
 create mode 100644 meta/lib/oeqa/utils/sshcontrol.py

diff --git a/meta/lib/oeqa/utils/__init__.py b/meta/lib/oeqa/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/meta/lib/oeqa/utils/sshcontrol.py b/meta/lib/oeqa/utils/sshcontrol.py
new file mode 100644
index 0000000..85a09a0
--- /dev/null
+++ b/meta/lib/oeqa/utils/sshcontrol.py
@@ -0,0 +1,100 @@
+import subprocess
+import time
+import os
+import shlex
+
+class SSHControl(object):
+
+    def __init__(self, host=None, timeout=200, logfile=None):
+        self.host = host
+        self.timeout = timeout
+        self._out = ''
+        self._ret = 126
+        self.logfile = logfile
+
+    def log(self, msg):
+        if self.logfile:
+            with open(self.logfile, "a") as f:
+                f.write("%s\n" % msg)
+
+    def _internal_run(self, cmd):
+        # ssh hangs without os.setsid
+        proc = subprocess.Popen(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=os.setsid)
+        return proc
+
+    def run(self, cmd, timeout=None):
+        """Run cmd and get it's return code and output.
+        Let it run for timeout seconds and then terminate/kill it,
+        if time is 0 will let cmd run until it finishes.
+        Time can be passed to here or can be set per class instance."""
+
+
+
+        actualcmd = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l root %s '%s'" % (self.host, cmd)
+        if self.host:
+            sshconn = self._internal_run(actualcmd)
+        else:
+            raise Exception("Remote IP hasn't been set: '%s'" % actualcmd)
+
+        if timeout == 0:
+            self.log("[SSH run without timeout]$ %s" % actualcmd)
+            self.log("  # %s" % cmd)
+            self._out = sshconn.communicate()[0]
+            self._ret = sshconn.poll()
+        else:
+            if timeout is None:
+                endtime = time.time() + self.timeout
+            else:
+                endtime = time.time() + timeout
+            while sshconn.poll() is None and time.time() < endtime:
+                time.sleep(1)
+            self.log("[SSH run with timeout]$ %s" % actualcmd)
+            self.log("  # %s" % cmd)
+            # process hasn't returned yet
+            if sshconn.poll() is None:
+                self._ret = 255
+                sshconn.terminate()
+                sshconn.kill()
+                self._out = sshconn.stdout.read()
+                sshconn.stdout.close()
+                self.log("[!!! process killed]")
+            else:
+                self._out = sshconn.stdout.read()
+                self._ret = sshconn.poll()
+        # remove first line from output which is always smth like (unless return code is 255 - which is a ssh error):
+        # Warning: Permanently added '192.168.7.2' (RSA) to the list of known hosts.
+        if self._ret != 255:
+            self._out = '\n'.join(self._out.splitlines()[1:])
+        self.log("%s" % self._out)
+        self.log("[SSH command returned]: %s" % self._ret)
+        return (self._ret, self._out)
+
+    def _internal_scp(self, cmd):
+        self.log("[SCP]$ %s" % cmd)
+        scpconn = self._internal_run(cmd)
+        try:
+            self._out = scpconn.communicate()[0]
+            self._ret = scpconn.poll()
+            if self._ret != 0:
+                self.log("%s" % self._out)
+                raise Exception("Error copying file")
+        except Exception as e:
+            print("Execution failed: %s :" % cmd)
+            print e
+        self.log("%s" % self._out)
+        return (self._ret, self._out)
+
+    def copy_to(self, localpath, remotepath):
+        actualcmd = "scp -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no %s root@%s:%s" % (localpath, self.host, remotepath)
+        return self._internal_scp(actualcmd)
+
+
+    def copy_from(self, remotepath, localpath):
+        actualcmd = "scp -o UserKnownHostsFile=/dev/null  -o StrictHostKeyChecking=no root@%s:%s %s" % (self.host, remotepath, localpath)
+        return self._internal_scp(actualcmd)
+
+    def get_status(self):
+        return self._ret
+
+    def get_output(self):
+        return self._out
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 4/8] lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
                   ` (2 preceding siblings ...)
  2013-07-05  9:27 ` [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 5/8] lib/oeqa/utils/qemurunner.py: class to handle qemu instance Stefan Stanacar
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

Python's telnetlib Telnet class connects only to AF_INET sockets, but we
want to use Unix domain socket for the qemu serial connection, so that's
why we override it.
Also we add a new read_all_timeout method similar to Telnet's read_all,
that read until a match or timeout and logs all output.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
---
 meta/lib/oeqa/utils/oeqemuconsole.py | 45 ++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 meta/lib/oeqa/utils/oeqemuconsole.py

diff --git a/meta/lib/oeqa/utils/oeqemuconsole.py b/meta/lib/oeqa/utils/oeqemuconsole.py
new file mode 100644
index 0000000..95a2133
--- /dev/null
+++ b/meta/lib/oeqa/utils/oeqemuconsole.py
@@ -0,0 +1,45 @@
+import socket
+import time
+import re
+from telnetlib import Telnet
+
+class oeQemuConsole(Telnet):
+
+    """
+    Override Telnet class to use unix domain sockets,
+    Telnet uses AF_INET for socket, we don't want that.
+    Also, provide a read_all variant with timeout, that
+    returns whatever output there is.
+    """
+
+    def __init__(self, stream, logfile):
+
+        Telnet.__init__(self, host=None)
+        self.stream = stream
+        self.logfile = logfile
+        self.eof = 0
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.sock.connect(stream)
+
+    def log(self, msg):
+        if self.logfile:
+            with open(self.logfile, "a") as f:
+                f.write("%s\n" % msg)
+
+
+    def read_all_timeout(self, match, timeout=200):
+        """Read until EOF or until timeout or until match.
+        """
+        ret = False
+        self.process_rawq()
+        endtime = time.time() + timeout
+        while not self.eof and time.time() < endtime:
+            self.fill_rawq()
+            self.process_rawq()
+            if re.search(match, self.cookedq):
+                ret = True
+                break
+        buf = self.cookedq
+        self.cookedq = ''
+        self.log(buf)
+        return (ret, buf)
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 5/8] lib/oeqa/utils/qemurunner.py: class to handle qemu instance
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
                   ` (3 preceding siblings ...)
  2013-07-05  9:27 ` [RFC PATCH v2 4/8] lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 6/8] lib/oeqa/utils/decorators.py: decorators for test methods Stefan Stanacar
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

From: Radu Moisan <radu.moisan@intel.com>

Handles qemu instances (launch, kill, restart, serial connection, logging)
Launch is blocking until login prompt and returns to the task. A qemu
serial connection is used to save the boot log and get the ip from the image.
Changed runqemu script not to error out when using custom serial option.

Signed-off-by: Radu Moisan <radu.moisan@intel.com>
Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
---
 meta/lib/oeqa/utils/qemurunner.py | 164 ++++++++++++++++++++++++++++++++++++++
 scripts/runqemu                   |   2 +-
 2 files changed, 165 insertions(+), 1 deletion(-)
 create mode 100644 meta/lib/oeqa/utils/qemurunner.py

diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
new file mode 100644
index 0000000..3132b68
--- /dev/null
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -0,0 +1,164 @@
+import subprocess
+import optparse
+import sys
+import os
+import time
+import signal
+import re
+import bb
+from oeqa.utils.oeqemuconsole import oeQemuConsole
+
+class QemuRunner:
+
+    def __init__(self, machine, rootfs, display = None, tmpdir = None, logfile = None):
+        # Popen object
+        self.runqemu = None
+
+        self.machine = machine
+        self.rootfs = rootfs
+
+        self.streampath = '/tmp/qemuconnection.%s' % os.getpid()
+        self.qemuparams = 'bootparams="console=ttyS0" qemuparams="-snapshot -serial unix:%s,server,nowait"' % self.streampath
+        self.qemupid = None
+        self.ip = None
+
+        self.display = display
+        self.tmpdir = tmpdir
+        self.logfile = logfile
+
+    def launch(self, qemuparams = None):
+
+        if qemuparams:
+            self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
+
+        if self.display:
+            os.environ["DISPLAY"] = self.display
+        else:
+            bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
+            return False
+        if not os.path.exists(self.rootfs):
+            bb.error("Invalid rootfs %s" % self.rootfs)
+            return False
+        if not os.path.exists(self.tmpdir):
+            bb.error("Invalid TMPDIR path %s" % self.tmpdir)
+            return False
+        else:
+            os.environ["OE_TMPDIR"] = self.tmpdir
+
+        launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams)
+        self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
+
+        bb.note("runqemu started, pid is %s" % self.runqemu.pid)
+        # wait at most 30 seconds until qemu pid appears
+        bb.note("waiting at most 60 seconds for qemu pid")
+        endtime = time.time() + 60
+        while not self.is_alive() and time.time() < endtime:
+            time.sleep(1)
+
+        if self.is_alive():
+            bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
+
+            console = oeQemuConsole(self.streampath, self.logfile)
+            bb.note("Waiting at most 200 seconds for login banner")
+            (match, text) = console.read_all_timeout("login:", 200)
+
+            if match:
+                bb.note("Reached login banner")
+                console.write("root\n")
+                (index, match, text) = console.expect([r"(root@[\w-]+:~#)"],10)
+                if not match:
+                    bb.note("Couldn't get prompt, all I got was:\n%s" % match.group(0))
+                    return False
+                console.write("ip addr show eth0 | sed -n '3p' | awk '{ print $2 }' | cut -f 1 -d \"/\"\n")
+                (index, match, text) = console.expect([r"((?:[0-9]{1,3}\.){3}[0-9]{1,3})"],10)
+                console.close()
+                if match:
+                    self.ip = match.group(0)
+                    bb.note("Ip found: %s" % self.ip)
+                else:
+                    bb.note("Couldn't determine ip, all I got was:\n%s" % text)
+                    return False
+            else:
+                console.close()
+                bb.note("Target didn't reached login boot in 120 seconds")
+                lines = "\n".join(text.splitlines()[-5:])
+                bb.note("Last 5 lines of text:\n%s" % lines)
+                bb.note("Check full boot log: %s" % self.logfile)
+                return False
+        else:
+            bb.note("Qemu pid didn't appeared in 30 seconds")
+            self.runqemu.terminate()
+            self.runqemu.kill()
+            bb.note("Output from runqemu: %s " % self.runqemu.stdout.read())
+            self.runqemu.stdout.close()
+            return False
+
+        return self.is_alive()
+
+
+    def kill(self):
+        if self.runqemu:
+            os.kill(-self.runqemu.pid,signal.SIGTERM)
+        self.qemupid = None
+        self.ip = None
+        if os.path.exists(self.streampath):
+            os.remove(self.streampath)
+
+    def restart(self, qemuparams = None):
+        if self.is_alive():
+            self.kill()
+        bb.note("Qemu Restart required...")
+        return self.launch(qemuparams)
+
+    def is_alive(self):
+        qemu_child = self.find_child(str(self.runqemu.pid))
+        if qemu_child:
+            self.qemupid = qemu_child[0]
+            if os.path.exists("/proc/" + str(self.qemupid)) and os.path.exists(self.streampath):
+                return True
+        return False
+
+    def find_child(self,parent_pid):
+        #
+        # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
+        #
+        ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
+        processes = ps.split('\n')
+        nfields = len(processes[0].split()) - 1
+        pids = {}
+        commands = {}
+        for row in processes[1:]:
+            data = row.split(None, nfields)
+            if len(data) != 3:
+                continue
+            if data[1] not in pids:
+                pids[data[1]] = []
+
+            pids[data[1]].append(data[0])
+            commands[data[0]] = data[2]
+
+        if parent_pid not in pids:
+            sys.stderr.write("No children found matching %s\n" % parent_pid)
+            return []
+
+        parents = []
+        newparents = pids[parent_pid]
+        while newparents:
+            next = []
+            for p in newparents:
+                if p in pids:
+                    for n in pids[p]:
+                        if n not in parents and n not in next:
+                            next.append(n)
+                if p not in parents:
+                    parents.append(p)
+                    newparents = next
+        #print "Children matching %s:" % str(parents)
+        for p in parents:
+            # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
+            # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
+            basecmd = commands[p].split()[0]
+            basecmd = os.path.basename(basecmd)
+            if "qemu-system" in basecmd and "-serial unix" in commands[p]:
+                return [int(p),commands[p]]
+
diff --git a/scripts/runqemu b/scripts/runqemu
index f2eb2e1..406092b 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -156,7 +156,7 @@ while true; do
             serial_option=`expr "$SCRIPT_QEMU_EXTRA_OPT" : '.*\(-serial\)'`
             kvm_option=`expr "$SCRIPT_QEMU_EXTRA_OPT" : '.*\(-enable-kvm\)'`
             [ ! -z "$serial_option" -o ! -z "$kvm_option" ] && \
-                error "Please use simplified serial or kvm options instead"
+                echo "Please use simplified serial or kvm options instead"
             ;;
         "bootparams="*)
             SCRIPT_KERNEL_OPT="$SCRIPT_KERNEL_OPT ${arg##bootparams=}"
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 6/8] lib/oeqa/utils/decorators.py: decorators for test methods
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
                   ` (4 preceding siblings ...)
  2013-07-05  9:27 ` [RFC PATCH v2 5/8] lib/oeqa/utils/qemurunner.py: class to handle qemu instance Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 7/8] lib/oeqa/runtime: image sanity tests Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 8/8] lib/oeqa/runtime: add gcc test Stefan Stanacar
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

From: Radu Moisan <radu.moisan@intel.com>

Some skip decorators meant only for test methods, providing
some kind of test methods dependency.
They are used together with a test method name not a condition.
These are complementary to python's unittest skip decorators.

Signed-off-by: Radu Moisan <radu.moisan@intel.com>
Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
---
 meta/lib/oeqa/utils/decorators.py | 40 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 meta/lib/oeqa/utils/decorators.py

diff --git a/meta/lib/oeqa/utils/decorators.py b/meta/lib/oeqa/utils/decorators.py
new file mode 100644
index 0000000..21e6b22
--- /dev/null
+++ b/meta/lib/oeqa/utils/decorators.py
@@ -0,0 +1,40 @@
+from oeqa.oetest import *
+
+class skipIfFailure(object):
+
+    def __init__(self,testcase):
+        self.testcase = testcase
+
+    def __call__(self,f):
+        def wrapped_f(*args):
+            if self.testcase in (oeRuntimeTest.testFailures or oeRuntimeTest.testErrors):
+                raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
+            f(*args)
+        wrapped_f.__name__ = f.__name__
+        return wrapped_f
+
+class skipIfSkipped(object):
+
+    def __init__(self,testcase):
+        self.testcase = testcase
+
+    def __call__(self,f):
+        def wrapped_f(*args):
+            if self.testcase in oeRuntimeTest.testSkipped:
+                raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
+            f(*args)
+        wrapped_f.__name__ = f.__name__
+        return wrapped_f
+
+class skipUnlessPassed(object):
+
+    def __init__(self,testcase):
+        self.testcase = testcase
+
+    def __call__(self,f):
+        def wrapped_f(*args):
+            if self.testcase in (oeRuntimeTest.testSkipped, oeRuntimeTest.testFailures, oeRuntimeTest.testErrors):
+                raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
+            f(*args)
+        wrapped_f.__name__ = f.__name__
+        return wrapped_f
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 7/8] lib/oeqa/runtime: image sanity tests
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
                   ` (5 preceding siblings ...)
  2013-07-05  9:27 ` [RFC PATCH v2 6/8] lib/oeqa/utils/decorators.py: decorators for test methods Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  2013-07-05  9:27 ` [RFC PATCH v2 8/8] lib/oeqa/runtime: add gcc test Stefan Stanacar
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

These are basic sanity tests. A test can be force run by setting
TEST_SUITES = "ping ssh <module-name>" in local.conf.
By default there are suites for minimal, sato and sato-sdk images.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
Signed-off-by: Radu Moisan <radu.moisan@intel.com>
---
 meta/lib/oeqa/runtime/__init__.py |  0
 meta/lib/oeqa/runtime/connman.py  | 34 ++++++++++++++++++++++++++++++++++
 meta/lib/oeqa/runtime/dmesg.py    | 13 +++++++++++++
 meta/lib/oeqa/runtime/multilib.py | 14 ++++++++++++++
 meta/lib/oeqa/runtime/ping.py     | 11 +++++++++++
 meta/lib/oeqa/runtime/rpm.py      | 25 +++++++++++++++++++++++++
 meta/lib/oeqa/runtime/smart.py    | 27 +++++++++++++++++++++++++++
 meta/lib/oeqa/runtime/ssh.py      | 16 ++++++++++++++++
 meta/lib/oeqa/runtime/systemd.py  | 24 ++++++++++++++++++++++++
 meta/lib/oeqa/runtime/xorg.py     | 21 +++++++++++++++++++++
 10 files changed, 185 insertions(+)
 create mode 100644 meta/lib/oeqa/runtime/__init__.py
 create mode 100644 meta/lib/oeqa/runtime/connman.py
 create mode 100644 meta/lib/oeqa/runtime/dmesg.py
 create mode 100644 meta/lib/oeqa/runtime/multilib.py
 create mode 100644 meta/lib/oeqa/runtime/ping.py
 create mode 100644 meta/lib/oeqa/runtime/rpm.py
 create mode 100644 meta/lib/oeqa/runtime/smart.py
 create mode 100644 meta/lib/oeqa/runtime/ssh.py
 create mode 100644 meta/lib/oeqa/runtime/systemd.py
 create mode 100644 meta/lib/oeqa/runtime/xorg.py

diff --git a/meta/lib/oeqa/runtime/__init__.py b/meta/lib/oeqa/runtime/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/meta/lib/oeqa/runtime/connman.py b/meta/lib/oeqa/runtime/connman.py
new file mode 100644
index 0000000..835e135
--- /dev/null
+++ b/meta/lib/oeqa/runtime/connman.py
@@ -0,0 +1,34 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasPackage("connman"):
+        skipModule("No connman package in image")
+
+
+class ConnmanTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ssh')
+    def test_connmand_help(self):
+        (status, output) = self.target.run('/usr/sbin/connmand --help')
+        self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
+
+
+    @skipUnlessPassed('test_connmand_help')
+    def test_connmand_running(self):
+        status = self.target.run('ls -l `which ps` | grep busybox')[0]
+        if status == 0:
+            oeRuntimeTest.pscmd = 'ps'
+        else:
+            oeRuntimeTest.pscmd = 'ps -ef'
+        (status, output) = self.target.run(oeRuntimeTest.pscmd + ' | grep [c]onnmand')
+        self.assertEqual(status, 0, msg="no connmand process, ps output: %s" % self.target.run(oeRuntimeTest.pscmd)[1])
+
+    @skipUnlessPassed('test_connmand_running')
+    def test_connmand_unique(self):
+        self.target.run('/usr/sbin/connmand')
+        output = self.target.run(oeRuntimeTest.pscmd + ' | grep -c [c]onnmand')[1]
+        self.assertEqual(output, "1", msg="more than one connmand running in background, ps output: %s\n%s" % (output, self.target.run(oeRuntimeTest.pscmd)[1]))
+
+
diff --git a/meta/lib/oeqa/runtime/dmesg.py b/meta/lib/oeqa/runtime/dmesg.py
new file mode 100644
index 0000000..b0e2175
--- /dev/null
+++ b/meta/lib/oeqa/runtime/dmesg.py
@@ -0,0 +1,13 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    skipModuleUnless(oeRuntimeTest.tc.target.run('which dmesg')[0] == 0, "No dmesg in image or no connection")
+
+class DmesgTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ssh')
+    def test_dmesg(self):
+        (status, output) = self.target.run('dmesg | grep -v mmci-pl18x | grep -i error')
+        self.assertEqual(status, 1, msg = "Error messages in dmesg log: %s" % output)
diff --git a/meta/lib/oeqa/runtime/multilib.py b/meta/lib/oeqa/runtime/multilib.py
new file mode 100644
index 0000000..397d075
--- /dev/null
+++ b/meta/lib/oeqa/runtime/multilib.py
@@ -0,0 +1,14 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest, skipModule
+
+def setUpModule():
+    multilibs = oeRuntimeTest.tc.d.getVar("MULTILIBS", True) or ""
+    if "multlib:lib32" not in multilibs:
+        skipModule("this isn't a multilib:lib32 image")
+
+
+class MultilibFileTest(oeRuntimeTest):
+
+    def test_file_connman(self):
+        (status, output) = self.target.run('file -L /usr/sbin/connmand | grep "ELF 32-bit LSB executable"')
+        self.assertEqual(status, 0, msg="status and output : %s and %s" % (status,output))
diff --git a/meta/lib/oeqa/runtime/ping.py b/meta/lib/oeqa/runtime/ping.py
new file mode 100644
index 0000000..d6a0c28
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ping.py
@@ -0,0 +1,11 @@
+import subprocess
+import unittest
+import sys
+from oeqa.oetest import oeRuntimeTest
+
+class PingTest(oeRuntimeTest):
+
+    def test_ping(self):
+        status = subprocess.call("ping -w 30 -c 1 %s" % oeRuntimeTest.tc.qemu.ip, shell=True, stdout=subprocess.PIPE)
+        self.assertEqual(status, 0)
+
diff --git a/meta/lib/oeqa/runtime/rpm.py b/meta/lib/oeqa/runtime/rpm.py
new file mode 100644
index 0000000..57101b0
--- /dev/null
+++ b/meta/lib/oeqa/runtime/rpm.py
@@ -0,0 +1,25 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasFeature("package-management"):
+            skipModule("rpm module skipped: target doesn't have package-management in IMAGE_FEATURES")
+    if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
+            skipModule("rpm module skipped: target doesn't have rpm as primary package manager")
+
+
+class RpmHelpTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ssh')
+    def test_rpm_help(self):
+        (status, output) = self.target.run('rpm --help')
+        self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
+
+class RpmQueryTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_rpm_help')
+    def test_rpm_query(self):
+        (status, output) = self.target.run('rpm -q rpm')
+        self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
+
diff --git a/meta/lib/oeqa/runtime/smart.py b/meta/lib/oeqa/runtime/smart.py
new file mode 100644
index 0000000..8cfacd4
--- /dev/null
+++ b/meta/lib/oeqa/runtime/smart.py
@@ -0,0 +1,27 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasFeature("package-management"):
+        skipModule("Image doesn't have package management feature")
+    if not oeRuntimeTest.hasPackage("smart"):
+        skipModule("Image doesn't have smart installed")
+
+class SmartHelpTest(oeRuntimeTest):
+
+    def test_smart_help(self):
+        status = self.target.run('smart --help')[0]
+        self.assertEqual(status, 0)
+
+class SmartQueryTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_smart_help')
+    def test_smart_query(self):
+        (status, output) = self.target.run('smart query rpm')
+        self.assertEqual(status, 0, msg="smart query failed, output: %s" % output)
+
+    @skipUnlessPassed('test_smart_query')
+    def test_smart_info(self):
+        (status, output) = self.target.run('smart info rpm')
+        self.assertEqual(status, 0, msg="smart info rpm failed, output: %s" % output)
diff --git a/meta/lib/oeqa/runtime/ssh.py b/meta/lib/oeqa/runtime/ssh.py
new file mode 100644
index 0000000..8c96020
--- /dev/null
+++ b/meta/lib/oeqa/runtime/ssh.py
@@ -0,0 +1,16 @@
+import subprocess
+import unittest
+import sys
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not (oeRuntimeTest.hasPackage("dropbear") or oeRuntimeTest.hasPackage("openssh")):
+        skipModule("No ssh package in image")
+
+class SshTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ping')
+    def test_ssh(self):
+        (status, output) = self.target.run('uname -a')
+        self.assertEqual(status, 0, msg="SSH Test failed: %s" % output)
diff --git a/meta/lib/oeqa/runtime/systemd.py b/meta/lib/oeqa/runtime/systemd.py
new file mode 100644
index 0000000..edf4f39
--- /dev/null
+++ b/meta/lib/oeqa/runtime/systemd.py
@@ -0,0 +1,24 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasFeature("systemd"):
+            skipModule("target doesn't have systemd in DISTRO_FEATURES")
+    if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", True):
+            skipModule("systemd is not the init manager for this image")
+
+
+class SystemdBasicTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ssh')
+    def test_systemd_version(self):
+        (status, output) = self.target.run('systemctl --version')
+        self.assertEqual(status, 0, msg="status and output: %s and %s" % (status,output))
+
+class SystemdTests(oeRuntimeTest):
+
+    @skipUnlessPassed('test_systemd_version')
+    def test_systemd_failed(self):
+        (status, output) = self.target.run('systemctl --failed | grep "0 loaded units listed"')
+        self.assertEqual(status, 0, msg="Failed systemd services: %s" % self.target.run('systemctl --failed')[1])
diff --git a/meta/lib/oeqa/runtime/xorg.py b/meta/lib/oeqa/runtime/xorg.py
new file mode 100644
index 0000000..96cc20a
--- /dev/null
+++ b/meta/lib/oeqa/runtime/xorg.py
@@ -0,0 +1,21 @@
+import unittest
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasFeature("x11-base"):
+            skipModule("target doesn't have x11 in IMAGE_FEATURES")
+
+
+class XorgTest(oeRuntimeTest):
+
+    @skipUnlessPassed('test_ssh')
+    def test_xorg_running(self):
+        (status, output) = self.target.run('ps | grep -v xinit | grep [X]org')
+        self.assertEqual(status, 0, msg="Xorg does not appear to be running %s" % self.target.run('ps')[1])
+
+    @skipUnlessPassed('test_ssh')
+    def test_xorg_error(self):
+        (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)"')
+        self.assertEqual(status, 1, msg="Errors in Xorg log: %s" % output)
+
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 8/8] lib/oeqa/runtime: add gcc test
  2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
                   ` (6 preceding siblings ...)
  2013-07-05  9:27 ` [RFC PATCH v2 7/8] lib/oeqa/runtime: image sanity tests Stefan Stanacar
@ 2013-07-05  9:27 ` Stefan Stanacar
  7 siblings, 0 replies; 11+ messages in thread
From: Stefan Stanacar @ 2013-07-05  9:27 UTC (permalink / raw)
  To: openembedded-core

gcc compile test and support files.

Signed-off-by: Stefan Stanacar <stefanx.stanacar@intel.com>
---
 meta/lib/oeqa/runtime/files/test.c       | 26 +++++++++++++++++++++++
 meta/lib/oeqa/runtime/files/testmakefile |  5 +++++
 meta/lib/oeqa/runtime/gcc.py             | 36 ++++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+)
 create mode 100644 meta/lib/oeqa/runtime/files/test.c
 create mode 100644 meta/lib/oeqa/runtime/files/testmakefile
 create mode 100644 meta/lib/oeqa/runtime/gcc.py

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 @@
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+
+double convert(long long l)
+{
+  return (double)l;
+}
+
+int main(int argc, char * argv[]) {
+
+  long long l = 10;
+  double f;
+  double check = 10.0;
+
+  f = convert(l);
+  printf("convert: %lld => %f\n", l, f);
+  if ( f != check ) exit(1);
+
+  f = 1234.67;
+  check = 1234.0;
+  printf("floorf(%f) = %f\n", f, floorf(f));
+  if ( floorf(f) != check) exit(1);
+
+  return 0;
+}
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 @@
+test: test.o
+	gcc -o test test.o -lm
+test.o: test.c
+	gcc -c test.c
+
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 @@
+import unittest
+import os
+from oeqa.oetest import oeRuntimeTest, skipModule
+from oeqa.utils.decorators import *
+
+def setUpModule():
+    if not oeRuntimeTest.hasFeature("tools-sdk"):
+        skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
+
+
+class GccCompileTest(oeRuntimeTest):
+
+    @classmethod
+    def setUpClass(self):
+        oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.c"), "/tmp/test.c")
+        oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "testmakefile"), "/tmp/testmakefile")
+
+    def test_gcc_compile(self):
+        (status, output) = self.target.run('gcc /tmp/test.c -o /tmp/test -lm')
+        self.assertEqual(status, 0, msg="gcc compile failed, output: %s" % output)
+        (status, output) = self.target.run('/tmp/test')
+        self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
+
+    def test_gpp_compile(self):
+        (status, output) = self.target.run('g++ /tmp/test.c -o /tmp/test -lm')
+        self.assertEqual(status, 0, msg="g++ compile failed, output: %s" % output)
+        (status, output) = self.target.run('/tmp/test')
+        self.assertEqual(status, 0, msg="running compiled file failed, output %s" % output)
+
+    def test_make(self):
+        (status, output) = self.target.run('cd /tmp; make -f testmakefile')
+        self.assertEqual(status, 0, msg="running make failed, output %s" % output)
+
+    @classmethod
+    def tearDownClass(self):
+        oeRuntimeTest.tc.target.run("rm /tmp/test.c /tmp/test.o /tmp/test /tmp/testmakefile")
-- 
1.8.1.4



^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands
  2013-07-05  9:27 ` [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands Stefan Stanacar
@ 2013-07-07 21:55   ` Colin Walters
  2013-07-09 16:50     ` Stanacar, StefanX
  0 siblings, 1 reply; 11+ messages in thread
From: Colin Walters @ 2013-07-07 21:55 UTC (permalink / raw)
  To: Stefan Stanacar; +Cc: openembedded-core

On Fri, 2013-07-05 at 12:27 +0300, Stefan Stanacar wrote:

> +        actualcmd = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l root %s '%s'" % (self.host, cmd)

This is going to fail if cmd contains a single quote.  Personally, I
*always* pass around subprocess arguments as an array.  Anything else is
going to lead to quoting problems.





^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands
  2013-07-07 21:55   ` Colin Walters
@ 2013-07-09 16:50     ` Stanacar, StefanX
  0 siblings, 0 replies; 11+ messages in thread
From: Stanacar, StefanX @ 2013-07-09 16:50 UTC (permalink / raw)
  To: Colin Walters; +Cc: openembedded-core@lists.openembedded.org

Hi Colin,

On Sun, 2013-07-07 at 17:55 -0400, Colin Walters wrote:
> On Fri, 2013-07-05 at 12:27 +0300, Stefan Stanacar wrote:
> 
> > +        actualcmd = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l root %s '%s'" % (self.host, cmd)
> 
> This is going to fail if cmd contains a single quote.  Personally, I
> *always* pass around subprocess arguments as an array.  Anything else is
> going to lead to quoting problems.
> 
> 
> 

You're right, I just sent a patch that drops that and doesn't use shlex
anymore.

Thanks,
Stefan

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2013-07-09 16:50 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-07-05  9:27 [RFC PATCH v2 0/8] Proposed implementation of a new runtime tests framework Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 1/8] classes/testimage.bbclass: new class for image tests Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 2/8] lib/oeqa/oetest.py: base module for all runtime unittests Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 3/8] lib/oeqa/utils/sshcontrol.py: helper module for running remote commands Stefan Stanacar
2013-07-07 21:55   ` Colin Walters
2013-07-09 16:50     ` Stanacar, StefanX
2013-07-05  9:27 ` [RFC PATCH v2 4/8] lib/oeqa/utils/oeqemuconsole.py: handle qemu serial console connection Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 5/8] lib/oeqa/utils/qemurunner.py: class to handle qemu instance Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 6/8] lib/oeqa/utils/decorators.py: decorators for test methods Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 7/8] lib/oeqa/runtime: image sanity tests Stefan Stanacar
2013-07-05  9:27 ` [RFC PATCH v2 8/8] lib/oeqa/runtime: add gcc test Stefan Stanacar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox