Yocto 2.3

Move OpenBMC to Yocto 2.3(pyro).

Tested: Built and verified Witherspoon and Palmetto images
Change-Id: I50744030e771f4850afc2a93a10d3507e76d36bc
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Resolves: openbmc/openbmc#2461
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py
index 59dd025..6e62b27 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/base.py
@@ -10,21 +10,22 @@
 # more details.
 #
 """Build performance test base classes and functionality"""
-import glob
 import json
 import logging
 import os
 import re
 import resource
-import shutil
 import socket
+import shutil
 import time
-import traceback
 import unittest
+import xml.etree.ElementTree as ET
+from collections import OrderedDict
 from datetime import datetime, timedelta
 from functools import partial
 from multiprocessing import Process
 from multiprocessing import SimpleQueue
+from xml.dom import minidom
 
 import oe.path
 from oeqa.utils.commands import CommandError, runCmd, get_bb_vars
@@ -35,7 +36,7 @@
 
 # Our own version of runCmd which does not raise AssertErrors which would cause
 # errors to interpreted as failures
-runCmd2 = partial(runCmd, assert_error=False)
+runCmd2 = partial(runCmd, assert_error=False, limit_exc_output=40)
 
 
 class KernelDropCaches(object):
@@ -99,50 +100,34 @@
         super(BuildPerfTestResult, self).__init__(*args, **kwargs)
 
         self.out_dir = out_dir
-        # Get Git parameters
-        try:
-            self.repo = GitRepo('.')
-        except GitError:
-            self.repo = None
-        self.git_commit, self.git_commit_count, self.git_branch = \
-                self.get_git_revision()
         self.hostname = socket.gethostname()
         self.product = os.getenv('OE_BUILDPERFTEST_PRODUCT', 'oe-core')
         self.start_time = self.elapsed_time = None
         self.successes = []
-        log.info("Using Git branch:commit %s:%s (%s)", self.git_branch,
-                 self.git_commit, self.git_commit_count)
-
-    def get_git_revision(self):
-        """Get git branch and commit under testing"""
-        commit = os.getenv('OE_BUILDPERFTEST_GIT_COMMIT')
-        commit_cnt = os.getenv('OE_BUILDPERFTEST_GIT_COMMIT_COUNT')
-        branch = os.getenv('OE_BUILDPERFTEST_GIT_BRANCH')
-        if not self.repo and (not commit or not commit_cnt or not branch):
-            log.info("The current working directory doesn't seem to be a Git "
-                     "repository clone. You can specify branch and commit "
-                     "displayed in test results with OE_BUILDPERFTEST_GIT_BRANCH, "
-                     "OE_BUILDPERFTEST_GIT_COMMIT and "
-                     "OE_BUILDPERFTEST_GIT_COMMIT_COUNT environment variables")
-        else:
-            if not commit:
-                commit = self.repo.rev_parse('HEAD^0')
-                commit_cnt = self.repo.run_cmd(['rev-list', '--count', 'HEAD^0'])
-            if not branch:
-                branch = self.repo.get_current_branch()
-                if not branch:
-                    log.debug('Currently on detached HEAD')
-        return str(commit), str(commit_cnt), str(branch)
 
     def addSuccess(self, test):
         """Record results from successful tests"""
         super(BuildPerfTestResult, self).addSuccess(test)
-        self.successes.append((test, None))
+        self.successes.append(test)
+
+    def addError(self, test, err):
+        """Record results from crashed test"""
+        test.err = err
+        super(BuildPerfTestResult, self).addError(test, err)
+
+    def addFailure(self, test, err):
+        """Record results from failed test"""
+        test.err = err
+        super(BuildPerfTestResult, self).addFailure(test, err)
+
+    def addExpectedFailure(self, test, err):
+        """Record results from expectedly failed test"""
+        test.err = err
+        super(BuildPerfTestResult, self).addExpectedFailure(test, err)
 
     def startTest(self, test):
         """Pre-test hook"""
         test.base_dir = self.out_dir
-        os.mkdir(test.out_dir)
         log.info("Executing test %s: %s", test.name, test.shortDescription())
         self.stream.write(datetime.now().strftime("[%Y-%m-%d %H:%M:%S] "))
         super(BuildPerfTestResult, self).startTest(test)
@@ -154,141 +139,113 @@
     def stopTestRun(self):
         """Pre-run hook"""
         self.elapsed_time = datetime.utcnow() - self.start_time
-        self.write_results_json()
 
     def all_results(self):
-        result_map = {'SUCCESS': self.successes,
-                      'FAIL': self.failures,
-                      'ERROR': self.errors,
-                      'EXP_FAIL': self.expectedFailures,
-                      'UNEXP_SUCCESS': self.unexpectedSuccesses,
-                      'SKIPPED': self.skipped}
-        for status, tests in result_map.items():
-            for test in tests:
-                yield (status, test)
+        compound = [('SUCCESS', t, None) for t in self.successes] + \
+                   [('FAILURE', t, m) for t, m in self.failures] + \
+                   [('ERROR', t, m) for t, m in self.errors] + \
+                   [('EXPECTED_FAILURE', t, m) for t, m in self.expectedFailures] + \
+                   [('UNEXPECTED_SUCCESS', t, None) for t in self.unexpectedSuccesses] + \
+                   [('SKIPPED', t, m) for t, m in self.skipped]
+        return sorted(compound, key=lambda info: info[1].start_time)
 
 
-    def update_globalres_file(self, filename):
-        """Write results to globalres csv file"""
-        # Map test names to time and size columns in globalres
-        # The tuples represent index and length of times and sizes
-        # respectively
-        gr_map = {'test1': ((0, 1), (8, 1)),
-                  'test12': ((1, 1), (None, None)),
-                  'test13': ((2, 1), (9, 1)),
-                  'test2': ((3, 1), (None, None)),
-                  'test3': ((4, 3), (None, None)),
-                  'test4': ((7, 1), (10, 2))}
+    def write_buildstats_json(self):
+        """Write buildstats file"""
+        buildstats = OrderedDict()
+        for _, test, _ in self.all_results():
+            for key, val in test.buildstats.items():
+                buildstats[test.name + '.' + key] = val
+        with open(os.path.join(self.out_dir, 'buildstats.json'), 'w') as fobj:
+            json.dump(buildstats, fobj, cls=ResultsJsonEncoder)
 
-        if self.repo:
-            git_tag_rev = self.repo.run_cmd(['describe', self.git_commit])
-        else:
-            git_tag_rev = self.git_commit
-
-        values = ['0'] * 12
-        for status, (test, msg) in self.all_results():
-            if status in ['ERROR', 'SKIPPED']:
-                continue
-            (t_ind, t_len), (s_ind, s_len) = gr_map[test.name]
-            if t_ind is not None:
-                values[t_ind:t_ind + t_len] = test.times
-            if s_ind is not None:
-                values[s_ind:s_ind + s_len] = test.sizes
-
-        log.debug("Writing globalres log to %s", filename)
-        with open(filename, 'a') as fobj:
-            fobj.write('{},{}:{},{},'.format(self.hostname,
-                                             self.git_branch,
-                                             self.git_commit,
-                                             git_tag_rev))
-            fobj.write(','.join(values) + '\n')
 
     def write_results_json(self):
         """Write test results into a json-formatted file"""
-        results = {'tester_host': self.hostname,
-                   'git_branch': self.git_branch,
-                   'git_commit': self.git_commit,
-                   'git_commit_count': self.git_commit_count,
-                   'product': self.product,
-                   'start_time': self.start_time,
-                   'elapsed_time': self.elapsed_time}
+        results = OrderedDict([('tester_host', self.hostname),
+                               ('start_time', self.start_time),
+                               ('elapsed_time', self.elapsed_time),
+                               ('tests', OrderedDict())])
 
-        tests = {}
-        for status, (test, reason) in self.all_results():
-            tests[test.name] = {'name': test.name,
-                                'description': test.shortDescription(),
-                                'status': status,
-                                'start_time': test.start_time,
-                                'elapsed_time': test.elapsed_time,
-                                'cmd_log_file': os.path.relpath(test.cmd_log_file,
-                                                                self.out_dir),
-                                'measurements': test.measurements}
-        results['tests'] = tests
+        for status, test, reason in self.all_results():
+            test_result = OrderedDict([('name', test.name),
+                                       ('description', test.shortDescription()),
+                                       ('status', status),
+                                       ('start_time', test.start_time),
+                                       ('elapsed_time', test.elapsed_time),
+                                       ('measurements', test.measurements)])
+            if status in ('ERROR', 'FAILURE', 'EXPECTED_FAILURE'):
+                test_result['message'] = str(test.err[1])
+                test_result['err_type'] = test.err[0].__name__
+                test_result['err_output'] = reason
+            elif reason:
+                test_result['message'] = reason
+
+            results['tests'][test.name] = test_result
 
         with open(os.path.join(self.out_dir, 'results.json'), 'w') as fobj:
-            json.dump(results, fobj, indent=4, sort_keys=True,
+            json.dump(results, fobj, indent=4,
                       cls=ResultsJsonEncoder)
 
+    def write_results_xml(self):
+        """Write test results into a JUnit XML file"""
+        top = ET.Element('testsuites')
+        suite = ET.SubElement(top, 'testsuite')
+        suite.set('name', 'oeqa.buildperf')
+        suite.set('timestamp', self.start_time.isoformat())
+        suite.set('time', str(self.elapsed_time.total_seconds()))
+        suite.set('hostname', self.hostname)
+        suite.set('failures', str(len(self.failures) + len(self.expectedFailures)))
+        suite.set('errors', str(len(self.errors)))
+        suite.set('skipped', str(len(self.skipped)))
 
-    def git_commit_results(self, repo_path, branch=None, tag=None):
-        """Commit results into a Git repository"""
-        repo = GitRepo(repo_path, is_topdir=True)
-        if not branch:
-            branch = self.git_branch
-        else:
-            # Replace keywords
-            branch = branch.format(git_branch=self.git_branch,
-                                   tester_host=self.hostname)
+        test_cnt = 0
+        for status, test, reason in self.all_results():
+            test_cnt += 1
+            testcase = ET.SubElement(suite, 'testcase')
+            testcase.set('classname', test.__module__ + '.' + test.__class__.__name__)
+            testcase.set('name', test.name)
+            testcase.set('description', test.shortDescription())
+            testcase.set('timestamp', test.start_time.isoformat())
+            testcase.set('time', str(test.elapsed_time.total_seconds()))
+            if status in ('ERROR', 'FAILURE', 'EXP_FAILURE'):
+                if status in ('FAILURE', 'EXP_FAILURE'):
+                    result = ET.SubElement(testcase, 'failure')
+                else:
+                    result = ET.SubElement(testcase, 'error')
+                result.set('message', str(test.err[1]))
+                result.set('type', test.err[0].__name__)
+                result.text = reason
+            elif status == 'SKIPPED':
+                result = ET.SubElement(testcase, 'skipped')
+                result.text = reason
+            elif status not in ('SUCCESS', 'UNEXPECTED_SUCCESS'):
+                raise TypeError("BUG: invalid test status '%s'" % status)
 
-        log.info("Committing test results into %s %s", repo_path, branch)
-        tmp_index = os.path.join(repo_path, '.git', 'index.oe-build-perf')
-        try:
-            # Create new commit object from the new results
-            env_update = {'GIT_INDEX_FILE': tmp_index,
-                          'GIT_WORK_TREE': self.out_dir}
-            repo.run_cmd('add .', env_update)
-            tree = repo.run_cmd('write-tree', env_update)
-            parent = repo.rev_parse(branch)
-            msg = "Results of {}:{}\n".format(self.git_branch, self.git_commit)
-            git_cmd = ['commit-tree', tree, '-m', msg]
-            if parent:
-                git_cmd += ['-p', parent]
-            commit = repo.run_cmd(git_cmd, env_update)
+            for data in test.measurements.values():
+                measurement = ET.SubElement(testcase, data['type'])
+                measurement.set('name', data['name'])
+                measurement.set('legend', data['legend'])
+                vals = data['values']
+                if data['type'] == BuildPerfTestCase.SYSRES:
+                    ET.SubElement(measurement, 'time',
+                                  timestamp=vals['start_time'].isoformat()).text = \
+                        str(vals['elapsed_time'].total_seconds())
+                    attrib = dict((k, str(v)) for k, v in vals['iostat'].items())
+                    ET.SubElement(measurement, 'iostat', attrib=attrib)
+                    attrib = dict((k, str(v)) for k, v in vals['rusage'].items())
+                    ET.SubElement(measurement, 'rusage', attrib=attrib)
+                elif data['type'] == BuildPerfTestCase.DISKUSAGE:
+                    ET.SubElement(measurement, 'size').text = str(vals['size'])
+                else:
+                    raise TypeError('BUG: unsupported measurement type')
 
-            # Update branch head
-            git_cmd = ['update-ref', 'refs/heads/' + branch, commit]
-            if parent:
-                git_cmd.append(parent)
-            repo.run_cmd(git_cmd)
+        suite.set('tests', str(test_cnt))
 
-            # Update current HEAD, if we're on branch 'branch'
-            if repo.get_current_branch() == branch:
-                log.info("Updating %s HEAD to latest commit", repo_path)
-                repo.run_cmd('reset --hard')
-
-            # Create (annotated) tag
-            if tag:
-                # Find tags matching the pattern
-                tag_keywords = dict(git_branch=self.git_branch,
-                                    git_commit=self.git_commit,
-                                    git_commit_count=self.git_commit_count,
-                                    tester_host=self.hostname,
-                                    tag_num='[0-9]{1,5}')
-                tag_re = re.compile(tag.format(**tag_keywords) + '$')
-                tag_keywords['tag_num'] = 0
-                for existing_tag in repo.run_cmd('tag').splitlines():
-                    if tag_re.match(existing_tag):
-                        tag_keywords['tag_num'] += 1
-
-                tag = tag.format(**tag_keywords)
-                msg = "Test run #{} of {}:{}\n".format(tag_keywords['tag_num'],
-                                                       self.git_branch,
-                                                       self.git_commit)
-                repo.run_cmd(['tag', '-a', '-m', msg, tag, commit])
-
-        finally:
-            if os.path.exists(tmp_index):
-                os.unlink(tmp_index)
+        # Use minidom for pretty-printing
+        dom_doc = minidom.parseString(ET.tostring(top, 'utf-8'))
+        with open(os.path.join(self.out_dir, 'results.xml'), 'w') as fobj:
+            dom_doc.writexml(fobj, addindent='  ', newl='\n', encoding='utf-8')
 
 
 class BuildPerfTestCase(unittest.TestCase):
@@ -303,7 +260,10 @@
         self.base_dir = None
         self.start_time = None
         self.elapsed_time = None
-        self.measurements = []
+        self.measurements = OrderedDict()
+        self.buildstats = OrderedDict()
+        # self.err is supposed to be a tuple from sys.exc_info()
+        self.err = None
         self.bb_vars = get_bb_vars()
         # TODO: remove 'times' and 'sizes' arrays when globalres support is
         # removed
@@ -311,18 +271,23 @@
         self.sizes = []
 
     @property
-    def out_dir(self):
-        return os.path.join(self.base_dir, self.name)
+    def tmp_dir(self):
+        return os.path.join(self.base_dir, self.name + '.tmp')
 
-    @property
-    def cmd_log_file(self):
-        return os.path.join(self.out_dir, 'commands.log')
+    def shortDescription(self):
+        return super(BuildPerfTestCase, self).shortDescription() or ""
 
     def setUp(self):
         """Set-up fixture for each test"""
+        if not os.path.isdir(self.tmp_dir):
+            os.mkdir(self.tmp_dir)
         if self.build_target:
-            self.log_cmd_output(['bitbake', self.build_target,
-                                 '-c', 'fetchall'])
+            self.run_cmd(['bitbake', self.build_target, '-c', 'fetchall'])
+
+    def tearDown(self):
+        """Tear-down fixture for each test"""
+        if os.path.isdir(self.tmp_dir):
+            shutil.rmtree(self.tmp_dir)
 
     def run(self, *args, **kwargs):
         """Run test"""
@@ -330,17 +295,23 @@
         super(BuildPerfTestCase, self).run(*args, **kwargs)
         self.elapsed_time = datetime.now() - self.start_time
 
-    def log_cmd_output(self, cmd):
-        """Run a command and log it's output"""
+    def run_cmd(self, cmd):
+        """Convenience method for running a command"""
         cmd_str = cmd if isinstance(cmd, str) else ' '.join(cmd)
         log.info("Logging command: %s", cmd_str)
         try:
-            with open(self.cmd_log_file, 'a') as fobj:
-                runCmd2(cmd, stdout=fobj)
+            runCmd2(cmd)
         except CommandError as err:
             log.error("Command failed: %s", err.retcode)
             raise
 
+    def _append_measurement(self, measurement):
+        """Simple helper for adding measurements results"""
+        if measurement['name'] in self.measurements:
+            raise ValueError('BUG: two measurements with the same name in {}'.format(
+                self.__class__.__name__))
+        self.measurements[measurement['name']] = measurement
+
     def measure_cmd_resources(self, cmd, name, legend, save_bs=False):
         """Measure system resource usage of a command"""
         def _worker(data_q, cmd, **kwargs):
@@ -350,12 +321,12 @@
                 ret = runCmd2(cmd, **kwargs)
                 etime = datetime.now() - start_time
                 rusage_struct = resource.getrusage(resource.RUSAGE_CHILDREN)
-                iostat = {}
+                iostat = OrderedDict()
                 with open('/proc/{}/io'.format(os.getpid())) as fobj:
                     for line in fobj.readlines():
                         key, val = line.split(':')
                         iostat[key] = int(val)
-                rusage = {}
+                rusage = OrderedDict()
                 # Skip unused fields, (i.e. 'ru_ixrss', 'ru_idrss', 'ru_isrss',
                 # 'ru_nswap', 'ru_msgsnd', 'ru_msgrcv' and 'ru_nsignals')
                 for key in ['ru_utime', 'ru_stime', 'ru_maxrss', 'ru_minflt',
@@ -374,33 +345,28 @@
         log.info("Timing command: %s", cmd_str)
         data_q = SimpleQueue()
         try:
-            with open(self.cmd_log_file, 'a') as fobj:
-                proc = Process(target=_worker, args=(data_q, cmd,),
-                               kwargs={'stdout': fobj})
-                proc.start()
-                data = data_q.get()
-                proc.join()
+            proc = Process(target=_worker, args=(data_q, cmd,))
+            proc.start()
+            data = data_q.get()
+            proc.join()
             if isinstance(data, Exception):
                 raise data
         except CommandError:
-            log.error("Command '%s' failed, see %s for more details", cmd_str,
-                      self.cmd_log_file)
+            log.error("Command '%s' failed", cmd_str)
             raise
         etime = data['elapsed_time']
 
-        measurement = {'type': self.SYSRES,
-                       'name': name,
-                       'legend': legend}
-        measurement['values'] = {'start_time': data['start_time'],
-                                 'elapsed_time': etime,
-                                 'rusage': data['rusage'],
-                                 'iostat': data['iostat']}
+        measurement = OrderedDict([('type', self.SYSRES),
+                                   ('name', name),
+                                   ('legend', legend)])
+        measurement['values'] = OrderedDict([('start_time', data['start_time']),
+                                             ('elapsed_time', etime),
+                                             ('rusage', data['rusage']),
+                                             ('iostat', data['iostat'])])
         if save_bs:
-            bs_file = self.save_buildstats(legend)
-            measurement['values']['buildstats_file'] = \
-                    os.path.relpath(bs_file, self.base_dir)
+            self.save_buildstats(name)
 
-        self.measurements.append(measurement)
+        self._append_measurement(measurement)
 
         # Append to 'times' array for globalres log
         e_sec = etime.total_seconds()
@@ -418,15 +384,15 @@
         ret = runCmd2(cmd)
         size = int(ret.output.split()[0])
         log.debug("Size of %s path is %s", path, size)
-        measurement = {'type': self.DISKUSAGE,
-                       'name': name,
-                       'legend': legend}
-        measurement['values'] = {'size': size}
-        self.measurements.append(measurement)
+        measurement = OrderedDict([('type', self.DISKUSAGE),
+                                   ('name', name),
+                                   ('legend', legend)])
+        measurement['values'] = OrderedDict([('size', size)])
+        self._append_measurement(measurement)
         # Append to 'sizes' array for globalres log
         self.sizes.append(str(size))
 
-    def save_buildstats(self, label=None):
+    def save_buildstats(self, measurement_name):
         """Save buildstats"""
         def split_nevr(nevr):
             """Split name and version information from recipe "nevr" string"""
@@ -445,9 +411,9 @@
 
         def bs_to_json(filename):
             """Convert (task) buildstats file into json format"""
-            bs_json = {'iostat': {},
-                       'rusage': {},
-                       'child_rusage': {}}
+            bs_json = OrderedDict()
+            iostat = OrderedDict()
+            rusage = OrderedDict()
             with open(filename) as fobj:
                 for line in fobj.readlines():
                     key, val = line.split(':', 1)
@@ -459,7 +425,7 @@
                         end_time = datetime.utcfromtimestamp(float(val))
                     elif key.startswith('IO '):
                         split = key.split()
-                        bs_json['iostat'][split[1]] = int(val)
+                        iostat[split[1]] = int(val)
                     elif key.find('rusage') >= 0:
                         split = key.split()
                         ru_key = split[-1]
@@ -467,12 +433,12 @@
                             val = float(val)
                         else:
                             val = int(val)
-                        ru_type = 'rusage' if split[0] == 'rusage' else \
-                                                          'child_rusage'
-                        bs_json[ru_type][ru_key] = val
+                        rusage[ru_key] = rusage.get(ru_key, 0) + val
                     elif key == 'Status':
                         bs_json['status'] = val
             bs_json['elapsed_time'] = end_time - start_time
+            bs_json['rusage'] = rusage
+            bs_json['iostat'] = iostat
             return bs_json
 
         log.info('Saving buildstats in JSON format')
@@ -488,24 +454,17 @@
             if not os.path.isdir(recipe_dir):
                 continue
             name, epoch, version, revision = split_nevr(fname)
-            recipe_bs = {'name': name,
-                         'epoch': epoch,
-                         'version': version,
-                         'revision': revision,
-                         'tasks': {}}
+            recipe_bs = OrderedDict((('name', name),
+                                     ('epoch', epoch),
+                                     ('version', version),
+                                     ('revision', revision),
+                                     ('tasks', OrderedDict())))
             for task in os.listdir(recipe_dir):
                 recipe_bs['tasks'][task] = bs_to_json(os.path.join(recipe_dir,
                                                                    task))
             buildstats.append(recipe_bs)
 
-        # Write buildstats into json file
-        postfix = '.' + str_to_fn(label) if label else ''
-        postfix += '.json'
-        outfile = os.path.join(self.out_dir, 'buildstats' + postfix)
-        with open(outfile, 'w') as fobj:
-            json.dump(buildstats, fobj, indent=4, sort_keys=True,
-                      cls=ResultsJsonEncoder)
-        return outfile
+        self.buildstats[measurement_name] = buildstats
 
     def rm_tmp(self):
         """Cleanup temporary/intermediate files and directories"""
@@ -547,5 +506,5 @@
         self.out_dir = out_dir
 
     def _makeResult(self):
-       return BuildPerfTestResult(self.out_dir, self.stream, self.descriptions,
-                                  self.verbosity)
+        return BuildPerfTestResult(self.out_dir, self.stream, self.descriptions,
+                                   self.verbosity)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py
index 7a48c1e..a9e4a5b 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/buildperf/test_basic.py
@@ -22,7 +22,7 @@
     build_target = 'core-image-sato'
 
     def test1(self):
-        """Measure wall clock of bitbake core-image-sato and size of tmp dir"""
+        """Build core-image-sato"""
         self.rm_tmp()
         self.rm_sstate()
         self.rm_cache()
@@ -36,10 +36,10 @@
     build_target = 'virtual/kernel'
 
     def test12(self):
-        """Measure bitbake virtual/kernel"""
+        """Build virtual/kernel"""
         # Build and cleans state in order to get all dependencies pre-built
-        self.log_cmd_output(['bitbake', self.build_target])
-        self.log_cmd_output(['bitbake', self.build_target, '-c', 'cleansstate'])
+        self.run_cmd(['bitbake', self.build_target])
+        self.run_cmd(['bitbake', self.build_target, '-c', 'cleansstate'])
 
         self.sync()
         self.measure_cmd_resources(['bitbake', self.build_target], 'build',
@@ -51,30 +51,28 @@
 
     def test13(self):
         """Build core-image-sato with rm_work enabled"""
-        postfile = os.path.join(self.out_dir, 'postfile.conf')
+        postfile = os.path.join(self.tmp_dir, 'postfile.conf')
         with open(postfile, 'w') as fobj:
             fobj.write('INHERIT += "rm_work"\n')
-        try:
-            self.rm_tmp()
-            self.rm_sstate()
-            self.rm_cache()
-            self.sync()
-            cmd = ['bitbake', '-R', postfile, self.build_target]
-            self.measure_cmd_resources(cmd, 'build',
-                                       'bitbake' + self.build_target,
-                                       save_bs=True)
-            self.measure_disk_usage(self.bb_vars['TMPDIR'], 'tmpdir', 'tmpdir')
-        finally:
-            os.unlink(postfile)
+
+        self.rm_tmp()
+        self.rm_sstate()
+        self.rm_cache()
+        self.sync()
+        cmd = ['bitbake', '-R', postfile, self.build_target]
+        self.measure_cmd_resources(cmd, 'build',
+                                   'bitbake' + self.build_target,
+                                   save_bs=True)
+        self.measure_disk_usage(self.bb_vars['TMPDIR'], 'tmpdir', 'tmpdir')
 
 
 class Test2(BuildPerfTestCase):
     build_target = 'core-image-sato'
 
     def test2(self):
-        """Measure bitbake core-image-sato -c rootfs with sstate"""
+        """Run core-image-sato do_rootfs with sstate"""
         # Build once in order to populate sstate cache
-        self.log_cmd_output(['bitbake', self.build_target])
+        self.run_cmd(['bitbake', self.build_target])
 
         self.rm_tmp()
         self.rm_cache()
@@ -86,7 +84,7 @@
 class Test3(BuildPerfTestCase):
 
     def test3(self):
-        """Parsing time metrics (bitbake -p)"""
+        """Bitbake parsing (bitbake -p)"""
         # Drop all caches and parse
         self.rm_cache()
         oe.path.remove(os.path.join(self.bb_vars['TMPDIR'], 'cache'), True)
@@ -106,8 +104,8 @@
 
     def test4(self):
         """eSDK metrics"""
-        self.log_cmd_output("bitbake {} -c do_populate_sdk_ext".format(
-            self.build_target))
+        self.run_cmd(['bitbake', '-c', 'do_populate_sdk_ext',
+                     self.build_target])
         self.bb_vars = get_bb_vars(None, self.build_target)
         tmp_dir = self.bb_vars['TMPDIR']
         installer = os.path.join(
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py b/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
index 9ce3bf8..07418fc 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/controllers/masterimage.py
@@ -32,14 +32,14 @@
         super(MasterImageHardwareTarget, self).__init__(d)
 
         # target ip
-        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.')
+        addr = d.getVar("TEST_TARGET_IP") or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
         self.ip = addr.split(":")[0]
         try:
             self.port = addr.split(":")[1]
         except IndexError:
             self.port = None
         bb.note("Target IP: %s" % self.ip)
-        self.server_ip = d.getVar("TEST_SERVER_IP", True)
+        self.server_ip = d.getVar("TEST_SERVER_IP")
         if not self.server_ip:
             try:
                 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
@@ -49,8 +49,8 @@
 
         # test rootfs + kernel
         self.image_fstype = self.get_image_fstype(d)
-        self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
-        self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
+        self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype)
+        self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
         if not os.path.isfile(self.rootfs):
             # we could've checked that IMAGE_FSTYPES contains tar.gz but the config for running testimage might not be
             # the same as the config with which the image was build, ie
@@ -64,16 +64,16 @@
         # master ssh connection
         self.master = None
         # if the user knows what they are doing, then by all means...
-        self.user_cmds = d.getVar("TEST_DEPLOY_CMDS", True)
+        self.user_cmds = d.getVar("TEST_DEPLOY_CMDS")
         self.deploy_cmds = None
 
         # this is the name of the command that controls the power for a board
         # e.g: TEST_POWERCONTROL_CMD = "/home/user/myscripts/powercontrol.py ${MACHINE} what-ever-other-args-the-script-wants"
         # the command should take as the last argument "off" and "on" and "cycle" (off, on)
-        self.powercontrol_cmd = d.getVar("TEST_POWERCONTROL_CMD", True) or None
+        self.powercontrol_cmd = d.getVar("TEST_POWERCONTROL_CMD") or None
         self.powercontrol_args = d.getVar("TEST_POWERCONTROL_EXTRA_ARGS", False) or ""
 
-        self.serialcontrol_cmd = d.getVar("TEST_SERIALCONTROL_CMD", True) or None
+        self.serialcontrol_cmd = d.getVar("TEST_SERIALCONTROL_CMD") or None
         self.serialcontrol_args = d.getVar("TEST_SERIALCONTROL_EXTRA_ARGS", False) or ""
 
         self.origenv = os.environ
@@ -82,7 +82,7 @@
             # ssh + keys means we need the original user env
             bborigenv = d.getVar("BB_ORIGENV", False) or {}
             for key in bborigenv:
-                val = bborigenv.getVar(key, True)
+                val = bborigenv.getVar(key)
                 if val is not None:
                     self.origenv[key] = str(val)
 
@@ -159,10 +159,10 @@
         self.power_cycle(self.connection)
 
 
-class GummibootTarget(MasterImageHardwareTarget):
+class SystemdbootTarget(MasterImageHardwareTarget):
 
     def __init__(self, d):
-        super(GummibootTarget, self).__init__(d)
+        super(SystemdbootTarget, self).__init__(d)
         # this the value we need to set in the LoaderEntryOneShot EFI variable
         # so the system boots the 'test' bootloader label and not the default
         # The first four bytes are EFI bits, and the rest is an utf-16le string
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/README b/import-layers/yocto-poky/meta/lib/oeqa/core/README
new file mode 100644
index 0000000..0c859fd
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/README
@@ -0,0 +1,38 @@
+= OEQA Framework =
+
+== Introduction ==
+
+This is the new OEQA framework the base clases of the framework
+are in this module oeqa/core the subsequent components needs to
+extend this classes.
+
+A new/unique runner was created called oe-test and is under scripts/
+oe-test, this new runner scans over oeqa module searching for test
+components that supports OETestContextExecutor implemented in context
+module (i.e. oeqa/core/context.py).
+
+For execute an example:
+
+$ source oe-init-build-env
+$ oe-test core
+
+For list supported components:
+
+$ oe-test -h
+
+== Create new Test component ==
+
+Usally for add a new Test component the developer needs to extend
+OETestContext/OETestContextExecutor in context.py and OETestCase in
+case.py.
+
+== How to run the testing of the OEQA framework ==
+
+Run all tests:
+
+$ PATH=$PATH:../../ python3 -m unittest discover -s tests
+
+Run some test:
+
+$ cd tests/
+$ ./test_data.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/case.py b/import-layers/yocto-poky/meta/lib/oeqa/core/case.py
new file mode 100644
index 0000000..d2dbf20
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/case.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import unittest
+
+from oeqa.core.exception import OEQAMissingVariable
+
+def _validate_td_vars(td, td_vars, type_msg):
+    if td_vars:
+        for v in td_vars:
+            if not v in td:
+                raise OEQAMissingVariable("Test %s need %s variable but"\
+                        " isn't into td" % (type_msg, v))
+
+class OETestCase(unittest.TestCase):
+    # TestContext and Logger instance set by OETestLoader.
+    tc = None
+    logger = None
+
+    # td has all the variables needed by the test cases
+    # is the same across all the test cases.
+    td = None
+
+    # td_vars has the variables needed by a test class
+    # or test case instance, if some var isn't into td a
+    # OEMissingVariable exception is raised
+    td_vars = None
+
+    @classmethod
+    def _oeSetUpClass(clss):
+        _validate_td_vars(clss.td, clss.td_vars, "class")
+        clss.setUpClassMethod()
+
+    @classmethod
+    def _oeTearDownClass(clss):
+        clss.tearDownClassMethod()
+
+    def _oeSetUp(self):
+        for d in self.decorators:
+            d.setUpDecorator()
+        self.setUpMethod()
+
+    def _oeTearDown(self):
+        for d in self.decorators:
+            d.tearDownDecorator()
+        self.tearDownMethod()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/cases/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/data.json b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/data.json
new file mode 100644
index 0000000..21d6b16
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/data.json
@@ -0,0 +1 @@
+{"ARCH": "x86", "IMAGE": "core-image-minimal"}
\ No newline at end of file
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/test_basic.py b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/test_basic.py
new file mode 100644
index 0000000..11cf380
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/cases/example/test_basic.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.depends import OETestDepends
+
+class OETestExample(OETestCase):
+    def test_example(self):
+        self.logger.info('IMAGE: %s' % self.td.get('IMAGE'))
+        self.assertEqual('core-image-minimal', self.td.get('IMAGE'))
+        self.logger.info('ARCH: %s' % self.td.get('ARCH'))
+        self.assertEqual('x86', self.td.get('ARCH'))
+
+class OETestExampleDepend(OETestCase):
+    @OETestDepends(['OETestExample.test_example'])
+    def test_example_depends(self):
+        pass
+
+    def test_example_no_depends(self):
+        pass
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/context.py b/import-layers/yocto-poky/meta/lib/oeqa/core/context.py
new file mode 100644
index 0000000..4476750
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/context.py
@@ -0,0 +1,243 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import sys
+import json
+import time
+import logging
+import collections
+import re
+
+from oeqa.core.loader import OETestLoader
+from oeqa.core.runner import OETestRunner, OEStreamLogger, xmlEnabled
+
+class OETestContext(object):
+    loaderClass = OETestLoader
+    runnerClass = OETestRunner
+    streamLoggerClass = OEStreamLogger
+
+    files_dir = os.path.abspath(os.path.join(os.path.dirname(
+        os.path.abspath(__file__)), "../files"))
+
+    def __init__(self, td=None, logger=None):
+        if not type(td) is dict:
+            raise TypeError("td isn't dictionary type")
+
+        self.td = td
+        self.logger = logger
+        self._registry = {}
+        self._registry['cases'] = collections.OrderedDict()
+        self._results = {}
+
+    def _read_modules_from_manifest(self, manifest):
+        if not os.path.exists(manifest):
+            raise
+
+        modules = []
+        for line in open(manifest).readlines():
+            line = line.strip()
+            if line and not line.startswith("#"):
+                modules.append(line)
+
+        return modules
+
+    def loadTests(self, module_paths, modules=[], tests=[],
+            modules_manifest="", modules_required=[], filters={}):
+        if modules_manifest:
+            modules = self._read_modules_from_manifest(modules_manifest)
+
+        self.loader = self.loaderClass(self, module_paths, modules, tests,
+                modules_required, filters)
+        self.suites = self.loader.discover()
+
+    def runTests(self):
+        streamLogger = self.streamLoggerClass(self.logger)
+        self.runner = self.runnerClass(self, stream=streamLogger, verbosity=2)
+
+        self._run_start_time = time.time()
+        result = self.runner.run(self.suites)
+        self._run_end_time = time.time()
+
+        return result
+
+    def logSummary(self, result, component, context_msg=''):
+        self.logger.info("SUMMARY:")
+        self.logger.info("%s (%s) - Ran %d test%s in %.3fs" % (component,
+            context_msg, result.testsRun, result.testsRun != 1 and "s" or "",
+            (self._run_end_time - self._run_start_time)))
+
+        if result.wasSuccessful():
+            msg = "%s - OK - All required tests passed" % component
+        else:
+            msg = "%s - FAIL - Required tests failed" % component
+        skipped = len(self._results['skipped'])
+        if skipped: 
+            msg += " (skipped=%d)" % skipped
+        self.logger.info(msg)
+
+    def _getDetailsNotPassed(self, case, type, desc):
+        found = False
+
+        for (scase, msg) in self._results[type]:
+            # XXX: When XML reporting is enabled scase is
+            # xmlrunner.result._TestInfo instance instead of
+            # string.
+            if xmlEnabled:
+                if case.id() == scase.test_id:
+                    found = True
+                    break
+                scase_str = scase.test_id
+            else:
+                if case == scase:
+                    found = True
+                    break
+                scase_str = str(scase)
+
+            # When fails at module or class level the class name is passed as string
+            # so figure out to see if match
+            m = re.search("^setUpModule \((?P<module_name>.*)\)$", scase_str)
+            if m:
+                if case.__class__.__module__ == m.group('module_name'):
+                    found = True
+                    break
+
+            m = re.search("^setUpClass \((?P<class_name>.*)\)$", scase_str)
+            if m:
+                class_name = "%s.%s" % (case.__class__.__module__,
+                        case.__class__.__name__)
+
+                if class_name == m.group('class_name'):
+                    found = True
+                    break
+
+        if found:
+            return (found, msg)
+
+        return (found, None)
+
+    def logDetails(self):
+        self.logger.info("RESULTS:")
+        for case_name in self._registry['cases']:
+            case = self._registry['cases'][case_name]
+
+            result_types = ['failures', 'errors', 'skipped', 'expectedFailures']
+            result_desc = ['FAILED', 'ERROR', 'SKIPPED', 'EXPECTEDFAIL']
+
+            fail = False
+            desc = None
+            for idx, name in enumerate(result_types):
+                (fail, msg) = self._getDetailsNotPassed(case, result_types[idx],
+                        result_desc[idx])
+                if fail:
+                    desc = result_desc[idx]
+                    break
+
+            oeid = -1
+            for d in case.decorators:
+                if hasattr(d, 'oeid'):
+                    oeid = d.oeid
+            
+            if fail:
+                self.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(),
+                    oeid, desc))
+                if msg:
+                    self.logger.info(msg)
+            else:
+                self.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(),
+                    oeid, 'PASSED'))
+
+class OETestContextExecutor(object):
+    _context_class = OETestContext
+
+    name = 'core'
+    help = 'core test component example'
+    description = 'executes core test suite example'
+
+    default_cases = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
+            'cases/example')]
+    default_test_data = os.path.join(default_cases[0], 'data.json')
+    default_tests = None
+
+    def register_commands(self, logger, subparsers):
+        self.parser = subparsers.add_parser(self.name, help=self.help,
+                description=self.description, group='components')
+
+        self.default_output_log = '%s-results-%s.log' % (self.name,
+                time.strftime("%Y%m%d%H%M%S"))
+        self.parser.add_argument('--output-log', action='store',
+                default=self.default_output_log,
+                help="results output log, default: %s" % self.default_output_log)
+        self.parser.add_argument('--run-tests', action='store',
+                default=self.default_tests,
+                help="tests to run in <module>[.<class>[.<name>]] format. Just works for modules now")
+
+        if self.default_test_data:
+            self.parser.add_argument('--test-data-file', action='store',
+                    default=self.default_test_data,
+                    help="data file to load, default: %s" % self.default_test_data)
+        else:
+            self.parser.add_argument('--test-data-file', action='store',
+                    help="data file to load")
+
+        if self.default_cases:
+            self.parser.add_argument('CASES_PATHS', action='store',
+                    default=self.default_cases, nargs='*',
+                    help="paths to directories with test cases, default: %s"\
+                            % self.default_cases)
+        else:
+            self.parser.add_argument('CASES_PATHS', action='store',
+                    nargs='+', help="paths to directories with test cases")
+
+        self.parser.set_defaults(func=self.run)
+
+    def _setup_logger(self, logger, args):
+        formatter = logging.Formatter('%(asctime)s - ' + self.name + \
+                ' - %(levelname)s - %(message)s')
+        sh = logger.handlers[0]
+        sh.setFormatter(formatter)
+        fh = logging.FileHandler(args.output_log)
+        fh.setFormatter(formatter)
+        logger.addHandler(fh)
+
+        return logger
+
+    def _process_args(self, logger, args):
+        self.tc_kwargs = {}
+        self.tc_kwargs['init'] = {}
+        self.tc_kwargs['load'] = {}
+        self.tc_kwargs['run'] = {}
+
+        self.tc_kwargs['init']['logger'] = self._setup_logger(logger, args)
+        if args.test_data_file:
+            self.tc_kwargs['init']['td'] = json.load(
+                    open(args.test_data_file, "r"))
+        else:
+            self.tc_kwargs['init']['td'] = {}
+
+
+        if args.run_tests:
+            self.tc_kwargs['load']['modules'] = args.run_tests.split()
+        else:
+            self.tc_kwargs['load']['modules'] = None
+
+        self.module_paths = args.CASES_PATHS
+
+    def run(self, logger, args):
+        self._process_args(logger, args)
+
+        self.tc = self._context_class(**self.tc_kwargs['init'])
+        self.tc.loadTests(self.module_paths, **self.tc_kwargs['load'])
+        rc = self.tc.runTests(**self.tc_kwargs['run'])
+        self.tc.logSummary(rc, self.name)
+        self.tc.logDetails()
+
+        output_link = os.path.join(os.path.dirname(args.output_log),
+                "%s-results.log" % self.name)
+        if os.path.exists(output_link):
+            os.remove(output_link)
+        os.symlink(args.output_log, output_link)
+
+        return rc
+
+_executor_class = OETestContextExecutor
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/__init__.py
new file mode 100644
index 0000000..855b6b9
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/__init__.py
@@ -0,0 +1,71 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from functools import wraps
+from abc import abstractmethod
+
+decoratorClasses = set()
+
+def registerDecorator(obj):
+    decoratorClasses.add(obj)
+    return obj
+
+class OETestDecorator(object):
+    case = None # Reference of OETestCase decorated
+    attrs = None # Attributes to be loaded by decorator implementation
+
+    def __init__(self, *args, **kwargs):
+        if not self.attrs:
+            return
+
+        for idx, attr in enumerate(self.attrs):
+            if attr in kwargs:
+                value = kwargs[attr]
+            else:
+                value = args[idx]
+            setattr(self, attr, value)
+
+    def __call__(self, func):
+        @wraps(func)
+        def wrapped_f(*args, **kwargs):
+            self.attrs = self.attrs # XXX: Enables OETestLoader discover
+            return func(*args, **kwargs)
+        return wrapped_f
+
+    # OETestLoader call it when is loading test cases.
+    # XXX: Most methods would change the registry for later
+    # processing; be aware that filtrate method needs to
+    # run later than bind, so there could be data (in the
+    # registry) of a cases that were filtered.
+    def bind(self, registry, case):
+        self.case = case
+        self.logger = case.tc.logger
+        self.case.decorators.append(self)
+
+    # OETestRunner call this method when tries to run
+    # the test case.
+    def setUpDecorator(self):
+        pass
+
+    # OETestRunner call it after a test method has been
+    # called even if the method raised an exception.
+    def tearDownDecorator(self):
+        pass
+
+class OETestDiscover(OETestDecorator):
+
+    # OETestLoader call it after discover test cases
+    # needs to return the cases to be run.
+    @staticmethod
+    def discover(registry):
+        return registry['cases']
+
+class OETestFilter(OETestDecorator):
+
+    # OETestLoader call it while loading the tests
+    # in loadTestsFromTestCase method, it needs to
+    # return a bool, True if needs to be filtered.
+    # This method must consume the filter used.
+    @abstractmethod
+    def filtrate(self, filters):
+        return False
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/data.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/data.py
new file mode 100644
index 0000000..ff7bdd9
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/data.py
@@ -0,0 +1,98 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.exception import OEQAMissingVariable
+
+from . import OETestDecorator, registerDecorator
+
+def has_feature(td, feature):
+    """
+        Checks for feature in DISTRO_FEATURES or IMAGE_FEATURES.
+    """
+
+    if (feature in td.get('DISTRO_FEATURES', '') or
+        feature in td.get('IMAGE_FEATURES', '')):
+        return True
+    return False
+
+@registerDecorator
+class skipIfDataVar(OETestDecorator):
+    """
+        Skip test based on value of a data store's variable.
+
+        It will get the info of var from the data store and will
+        check it against value; if are equal it will skip the test
+        with msg as the reason.
+    """
+
+    attrs = ('var', 'value', 'msg')
+
+    def setUpDecorator(self):
+        msg = ('Checking if %r value is %r to skip test' %
+               (self.var, self.value))
+        self.logger.debug(msg)
+        if self.case.td.get(self.var) == self.value:
+            self.case.skipTest(self.msg)
+
+@registerDecorator
+class skipIfNotDataVar(OETestDecorator):
+    """
+        Skip test based on value of a data store's variable.
+
+        It will get the info of var from the data store and will
+        check it against value; if are not equal it will skip the
+        test with msg as the reason.
+    """
+
+    attrs = ('var', 'value', 'msg')
+
+    def setUpDecorator(self):
+        msg = ('Checking if %r value is not %r to skip test' %
+               (self.var, self.value))
+        self.logger.debug(msg)
+        if not self.case.td.get(self.var) == self.value:
+            self.case.skipTest(self.msg)
+
+@registerDecorator
+class skipIfNotInDataVar(OETestDecorator):
+    """
+        Skip test if value is not in data store's variable.
+    """
+
+    attrs = ('var', 'value', 'msg')
+    def setUpDecorator(self):
+        msg = ('Checking if %r value is in %r to run '
+              'the test' % (self.var, self.value))
+        self.logger.debug(msg)
+        if not self.value in self.case.td.get(self.var):
+            self.case.skipTest(self.msg)
+
+@registerDecorator
+class OETestDataDepends(OETestDecorator):
+    attrs = ('td_depends',)
+
+    def setUpDecorator(self):
+        for v in self.td_depends:
+            try:
+                value = self.case.td[v]
+            except KeyError:
+                raise OEQAMissingVariable("Test case need %s variable but"\
+                        " isn't into td" % v)
+
+@registerDecorator
+class skipIfNotFeature(OETestDecorator):
+    """
+        Skip test based on DISTRO_FEATURES.
+
+        value must be in distro features or it will skip the test
+        with msg as the reason.
+    """
+
+    attrs = ('value', 'msg')
+
+    def setUpDecorator(self):
+        msg = ('Checking if %s is in DISTRO_FEATURES '
+               'or IMAGE_FEATURES' % (self.value))
+        self.logger.debug(msg)
+        if not has_feature(self.case.td, self.value):
+            self.case.skipTest(self.msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/depends.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/depends.py
new file mode 100644
index 0000000..195711c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/depends.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from unittest import SkipTest
+
+from oeqa.core.exception import OEQADependency
+
+from . import OETestDiscover, registerDecorator
+
+def _add_depends(registry, case, depends):
+    module_name = case.__module__
+    class_name = case.__class__.__name__
+
+    case_id = case.id()
+
+    for depend in depends:
+        dparts = depend.split('.')
+
+        if len(dparts) == 1:
+            depend_id = ".".join((module_name, class_name, dparts[0]))
+        elif len(dparts) == 2:
+            depend_id = ".".join((module_name, dparts[0], dparts[1]))
+        else:
+            depend_id = depend
+
+        if not case_id in registry:
+            registry[case_id] = []
+        if not depend_id in registry[case_id]:
+            registry[case_id].append(depend_id)
+
+def _validate_test_case_depends(cases, depends):
+    for case in depends:
+        if not case in cases:
+            continue
+        for dep in depends[case]:
+            if not dep in cases:
+                raise OEQADependency("TestCase %s depends on %s and isn't available"\
+                       ", cases available %s." % (case, dep, str(cases.keys())))
+
+def _order_test_case_by_depends(cases, depends):
+    def _dep_resolve(graph, node, resolved, seen):
+        seen.append(node)
+        for edge in graph[node]:
+            if edge not in resolved:
+                if edge in seen:
+                    raise OEQADependency("Test cases %s and %s have a circular" \
+                                       " dependency." % (node, edge))
+                _dep_resolve(graph, edge, resolved, seen)
+        resolved.append(node)
+
+    dep_graph = {}
+    dep_graph['__root__'] = cases.keys()
+    for case in cases:
+        if case in depends:
+            dep_graph[case] = depends[case]
+        else:
+            dep_graph[case] = []
+
+    cases_ordered = []
+    _dep_resolve(dep_graph, '__root__', cases_ordered, [])
+    cases_ordered.remove('__root__')
+
+    return [cases[case_id] for case_id in cases_ordered]
+
+def _skipTestDependency(case, depends):
+    results = case.tc._results
+    skipReasons = ['errors', 'failures', 'skipped']
+
+    for reason in skipReasons:
+        for test, _ in results[reason]:
+            if test.id() in depends:
+                raise SkipTest("Test case %s depends on %s and was in %s." \
+                        % (case.id(), test.id(), reason))
+
+@registerDecorator
+class OETestDepends(OETestDiscover):
+    attrs = ('depends',)
+
+    def bind(self, registry, case):
+        super(OETestDepends, self).bind(registry, case)
+        if not registry.get('depends'):
+            registry['depends'] = {}
+        _add_depends(registry['depends'], case, self.depends)
+
+    @staticmethod
+    def discover(registry):
+        if registry.get('depends'):
+            _validate_test_case_depends(registry['cases'], registry['depends'])
+            return _order_test_case_by_depends(registry['cases'], registry['depends'])
+        else:
+            return [registry['cases'][case_id] for case_id in registry['cases']]
+
+    def setUpDecorator(self):
+        _skipTestDependency(self.case, self.depends)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oeid.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oeid.py
new file mode 100644
index 0000000..ea8017a
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oeid.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from . import OETestFilter, registerDecorator
+from oeqa.core.utils.misc import intToList
+
+def _idFilter(oeid, filters):
+     return False if oeid in filters else True
+
+@registerDecorator
+class OETestID(OETestFilter):
+    attrs = ('oeid',)
+
+    def bind(self, registry, case):
+        super(OETestID, self).bind(registry, case)
+
+    def filtrate(self, filters):
+        if filters.get('oeid'):
+            filterx = intToList(filters['oeid'], 'oeid')
+            del filters['oeid']
+            if _idFilter(self.oeid, filterx):
+                return True
+        return False
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetag.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetag.py
new file mode 100644
index 0000000..ad38ab7
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetag.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from . import OETestFilter, registerDecorator
+from oeqa.core.utils.misc import strToList
+
+def _tagFilter(tags, filters):
+    return False if set(tags) & set(filters) else True
+
+@registerDecorator
+class OETestTag(OETestFilter):
+    attrs = ('oetag',)
+
+    def bind(self, registry, case):
+        super(OETestTag, self).bind(registry, case)
+        self.oetag = strToList(self.oetag, 'oetag')
+
+    def filtrate(self, filters):
+        if filters.get('oetag'):
+            filterx = strToList(filters['oetag'], 'oetag')
+            del filters['oetag']
+            if _tagFilter(self.oetag, filterx):
+                return True
+        return False
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetimeout.py b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetimeout.py
new file mode 100644
index 0000000..a247583
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/decorator/oetimeout.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import signal
+from . import OETestDecorator, registerDecorator
+from oeqa.core.exception import OEQATimeoutError
+
+@registerDecorator
+class OETimeout(OETestDecorator):
+    attrs = ('oetimeout',)
+
+    def setUpDecorator(self):
+        timeout = self.oetimeout
+        def _timeoutHandler(signum, frame):
+            raise OEQATimeoutError("Timed out after %s "
+                    "seconds of execution" % timeout)
+
+        self.logger.debug("Setting up a %d second(s) timeout" % self.oetimeout)
+        self.alarmSignal = signal.signal(signal.SIGALRM, _timeoutHandler)
+        signal.alarm(self.oetimeout)
+
+    def tearDownDecorator(self):
+        signal.alarm(0)
+        signal.signal(signal.SIGALRM, self.alarmSignal)
+        self.logger.debug("Removed SIGALRM handler")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/exception.py b/import-layers/yocto-poky/meta/lib/oeqa/core/exception.py
new file mode 100644
index 0000000..2dfd840
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/exception.py
@@ -0,0 +1,14 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+class OEQAException(Exception):
+    pass
+
+class OEQATimeoutError(OEQAException):
+    pass
+
+class OEQAMissingVariable(OEQAException):
+    pass
+
+class OEQADependency(OEQAException):
+    pass
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/loader.py b/import-layers/yocto-poky/meta/lib/oeqa/core/loader.py
new file mode 100644
index 0000000..63a1703
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/loader.py
@@ -0,0 +1,272 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import sys
+import unittest
+
+from oeqa.core.utils.path import findFile
+from oeqa.core.utils.test import getSuiteModules, getCaseID
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator import decoratorClasses, OETestDecorator, \
+        OETestFilter, OETestDiscover
+
+def _make_failed_test(classname, methodname, exception, suiteClass):
+    """
+        When loading tests unittest framework stores the exception in a new
+        class created for be displayed into run().
+
+        For our purposes will be better to raise the exception in loading 
+        step instead of wait to run the test suite.
+    """
+    raise exception
+unittest.loader._make_failed_test = _make_failed_test
+
+def _find_duplicated_modules(suite, directory):
+    for module in getSuiteModules(suite):
+        path = findFile('%s.py' % module, directory)
+        if path:
+            raise ImportError("Duplicated %s module found in %s" % (module, path))
+
+class OETestLoader(unittest.TestLoader):
+    caseClass = OETestCase
+
+    kwargs_names = ['testMethodPrefix', 'sortTestMethodUsing', 'suiteClass',
+            '_top_level_dir']
+
+    def __init__(self, tc, module_paths, modules, tests, modules_required,
+            filters, *args, **kwargs):
+        self.tc = tc
+
+        self.modules = modules
+        self.tests = tests
+        self.modules_required = modules_required
+
+        self.filters = filters
+        self.decorator_filters = [d for d in decoratorClasses if \
+                issubclass(d, OETestFilter)]
+        self._validateFilters(self.filters, self.decorator_filters)
+        self.used_filters = [d for d in self.decorator_filters
+                             for f in self.filters
+                             if f in d.attrs]
+
+        if isinstance(module_paths, str):
+            module_paths = [module_paths]
+        elif not isinstance(module_paths, list):
+            raise TypeError('module_paths must be a str or a list of str')
+        self.module_paths = module_paths
+
+        for kwname in self.kwargs_names:
+            if kwname in kwargs:
+                setattr(self, kwname, kwargs[kwname])
+
+        self._patchCaseClass(self.caseClass)
+
+    def _patchCaseClass(self, testCaseClass):
+        # Adds custom attributes to the OETestCase class
+        setattr(testCaseClass, 'tc', self.tc)
+        setattr(testCaseClass, 'td', self.tc.td)
+        setattr(testCaseClass, 'logger', self.tc.logger)
+
+    def _validateFilters(self, filters, decorator_filters):
+        # Validate if filter isn't empty
+        for key,value in filters.items():
+            if not value:
+                raise TypeError("Filter %s specified is empty" % key)
+
+        # Validate unique attributes
+        attr_filters = [attr for clss in decorator_filters \
+                                for attr in clss.attrs]
+        dup_attr = [attr for attr in attr_filters
+                    if attr_filters.count(attr) > 1]
+        if dup_attr:
+            raise TypeError('Detected duplicated attribute(s) %s in filter'
+                            ' decorators' % ' ,'.join(dup_attr))
+
+        # Validate if filter is supported
+        for f in filters:
+            if f not in attr_filters:
+                classes = ', '.join([d.__name__ for d in decorator_filters])
+                raise TypeError('Found "%s" filter but not declared in any of '
+                                '%s decorators' % (f, classes))
+
+    def _registerTestCase(self, case):
+        case_id = case.id()
+        self.tc._registry['cases'][case_id] = case
+
+    def _handleTestCaseDecorators(self, case):
+        def _handle(obj):
+            if isinstance(obj, OETestDecorator):
+                if not obj.__class__ in decoratorClasses:
+                    raise Exception("Decorator %s isn't registered" \
+                            " in decoratorClasses." % obj.__name__)
+                obj.bind(self.tc._registry, case)
+
+        def _walk_closure(obj):
+            if hasattr(obj, '__closure__') and obj.__closure__:
+                for f in obj.__closure__:
+                    obj = f.cell_contents
+                    _handle(obj)
+                    _walk_closure(obj)
+        method = getattr(case, case._testMethodName, None)
+        _walk_closure(method)
+
+    def _filterTest(self, case):
+        """
+            Returns True if test case must be filtered, False otherwise.
+        """
+        if self.filters:
+            filters = self.filters.copy()
+            case_decorators = [cd for cd in case.decorators
+                               if cd.__class__ in self.used_filters]
+
+            # Iterate over case decorators to check if needs to be filtered.
+            for cd in case_decorators:
+                if cd.filtrate(filters):
+                    return True
+
+            # Case is missing one or more decorators for all the filters
+            # being used, so filter test case.
+            if filters:
+                return True
+
+        return False
+
+    def _getTestCase(self, testCaseClass, tcName):
+        if not hasattr(testCaseClass, '__oeqa_loader'):
+            # In order to support data_vars validation
+            # monkey patch the default setUp/tearDown{Class} to use
+            # the ones provided by OETestCase
+            setattr(testCaseClass, 'setUpClassMethod',
+                    getattr(testCaseClass, 'setUpClass'))
+            setattr(testCaseClass, 'tearDownClassMethod',
+                    getattr(testCaseClass, 'tearDownClass'))
+            setattr(testCaseClass, 'setUpClass',
+                    testCaseClass._oeSetUpClass)
+            setattr(testCaseClass, 'tearDownClass',
+                    testCaseClass._oeTearDownClass)
+
+            # In order to support decorators initialization
+            # monkey patch the default setUp/tearDown to use
+            # a setUpDecorators/tearDownDecorators that methods
+            # will call setUp/tearDown original methods.
+            setattr(testCaseClass, 'setUpMethod',
+                    getattr(testCaseClass, 'setUp')) 
+            setattr(testCaseClass, 'tearDownMethod',
+                    getattr(testCaseClass, 'tearDown'))
+            setattr(testCaseClass, 'setUp', testCaseClass._oeSetUp)
+            setattr(testCaseClass, 'tearDown', testCaseClass._oeTearDown)
+
+            setattr(testCaseClass, '__oeqa_loader', True)
+
+        case = testCaseClass(tcName)
+        setattr(case, 'decorators', [])
+
+        return case
+
+    def loadTestsFromTestCase(self, testCaseClass):
+        """
+            Returns a suite of all tests cases contained in testCaseClass.
+        """
+        if issubclass(testCaseClass, unittest.suite.TestSuite):
+            raise TypeError("Test cases should not be derived from TestSuite." \
+                                " Maybe you meant to derive %s from TestCase?" \
+                                % testCaseClass.__name__)
+        if not issubclass(testCaseClass, self.caseClass):
+            raise TypeError("Test %s is not derived from %s" % \
+                    (testCaseClass.__name__, self.caseClass.__name__))
+
+        testCaseNames = self.getTestCaseNames(testCaseClass)
+        if not testCaseNames and hasattr(testCaseClass, 'runTest'):
+            testCaseNames = ['runTest']
+
+        suite = []
+        for tcName in testCaseNames:
+            case = self._getTestCase(testCaseClass, tcName)
+            # Filer by case id
+            if not (self.tests and not 'all' in self.tests
+                    and not getCaseID(case) in self.tests):
+                self._handleTestCaseDecorators(case)
+
+                # Filter by decorators
+                if not self._filterTest(case):
+                    self._registerTestCase(case)
+                    suite.append(case)
+
+        return self.suiteClass(suite)
+
+    def discover(self):
+        big_suite = self.suiteClass()
+        for path in self.module_paths:
+            _find_duplicated_modules(big_suite, path)
+            suite = super(OETestLoader, self).discover(path,
+                    pattern='*.py', top_level_dir=path)
+            big_suite.addTests(suite)
+
+        cases = None
+        discover_classes = [clss for clss in decoratorClasses
+                            if issubclass(clss, OETestDiscover)]
+        for clss in discover_classes:
+            cases = clss.discover(self.tc._registry)
+
+        return self.suiteClass(cases) if cases else big_suite
+
+    # XXX After Python 3.5, remove backward compatibility hacks for
+    # use_load_tests deprecation via *args and **kws.  See issue 16662.
+    if sys.version_info >= (3,5):
+        def loadTestsFromModule(self, module, *args, pattern=None, **kws):
+            """
+                Returns a suite of all tests cases contained in module.
+            """
+            if module.__name__ in sys.builtin_module_names:
+                msg = 'Tried to import %s test module but is a built-in'
+                raise ImportError(msg % module.__name__)
+
+            # Normal test modules are loaded if no modules were specified,
+            # if module is in the specified module list or if 'all' is in
+            # module list.
+            # Underscore modules are loaded only if specified in module list.
+            load_module = True if not module.__name__.startswith('_') \
+                                  and (not self.modules \
+                                       or module.__name__ in self.modules \
+                                       or 'all' in self.modules) \
+                               else False
+
+            load_underscore = True if module.__name__.startswith('_') \
+                                      and module.__name__ in self.modules \
+                                   else False
+
+            if load_module or load_underscore:
+                return super(OETestLoader, self).loadTestsFromModule(
+                        module, *args, pattern=pattern, **kws)
+            else:
+                return self.suiteClass()
+    else:
+        def loadTestsFromModule(self, module, use_load_tests=True):
+            """
+                Returns a suite of all tests cases contained in module.
+            """
+            if module.__name__ in sys.builtin_module_names:
+                msg = 'Tried to import %s test module but is a built-in'
+                raise ImportError(msg % module.__name__)
+
+            # Normal test modules are loaded if no modules were specified,
+            # if module is in the specified module list or if 'all' is in
+            # module list.
+            # Underscore modules are loaded only if specified in module list.
+            load_module = True if not module.__name__.startswith('_') \
+                                  and (not self.modules \
+                                       or module.__name__ in self.modules \
+                                       or 'all' in self.modules) \
+                               else False
+
+            load_underscore = True if module.__name__.startswith('_') \
+                                      and module.__name__ in self.modules \
+                                   else False
+
+            if load_module or load_underscore:
+                return super(OETestLoader, self).loadTestsFromModule(
+                        module, use_load_tests)
+            else:
+                return self.suiteClass()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/runner.py b/import-layers/yocto-poky/meta/lib/oeqa/core/runner.py
new file mode 100644
index 0000000..44ffecb
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/runner.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import time
+import unittest
+import logging
+
+xmlEnabled = False
+try:
+    import xmlrunner
+    from xmlrunner.result import _XMLTestResult as _TestResult
+    from xmlrunner.runner import XMLTestRunner as _TestRunner
+    xmlEnabled = True
+except ImportError:
+    # use the base runner instead
+    from unittest import TextTestResult as _TestResult
+    from unittest import TextTestRunner as _TestRunner
+
+class OEStreamLogger(object):
+    def __init__(self, logger):
+        self.logger = logger
+        self.buffer = ""
+
+    def write(self, msg):
+        if len(msg) > 1 and msg[0] != '\n':
+            self.buffer += msg
+        else:
+            self.logger.log(logging.INFO, self.buffer.rstrip("\n"))
+            self.buffer = ""
+
+    def flush(self):
+        for handler in self.logger.handlers:
+            handler.flush()
+
+class OETestResult(_TestResult):
+    def __init__(self, tc, *args, **kwargs):
+        super(OETestResult, self).__init__(*args, **kwargs)
+
+        self.tc = tc
+
+        self.tc._results['failures'] = self.failures
+        self.tc._results['errors'] = self.errors
+        self.tc._results['skipped'] = self.skipped
+        self.tc._results['expectedFailures'] = self.expectedFailures
+
+    def startTest(self, test):
+        super(OETestResult, self).startTest(test)
+
+class OETestRunner(_TestRunner):
+    def __init__(self, tc, *args, **kwargs):
+        if xmlEnabled:
+            if not kwargs.get('output'):
+                kwargs['output'] = os.path.join(os.getcwd(),
+                        'TestResults_%s_%s' % (time.strftime("%Y%m%d%H%M%S"), os.getpid()))
+
+        super(OETestRunner, self).__init__(*args, **kwargs)
+        self.tc = tc
+        self.resultclass = OETestResult
+
+    # XXX: The unittest-xml-reporting package defines _make_result method instead
+    # of _makeResult standard on unittest.
+    if xmlEnabled:
+        def _make_result(self):
+            """
+            Creates a TestResult object which will be used to store
+            information about the executed tests.
+            """
+            # override in subclasses if necessary.
+            return self.resultclass(self.tc,
+                self.stream, self.descriptions, self.verbosity, self.elapsed_times
+            )
+    else:
+        def _makeResult(self):
+            return self.resultclass(self.tc, self.stream, self.descriptions,
+                    self.verbosity)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/target/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/target/__init__.py
new file mode 100644
index 0000000..d2468bc
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/target/__init__.py
@@ -0,0 +1,33 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from abc import abstractmethod
+
+class OETarget(object):
+
+    def __init__(self, logger, *args, **kwargs):
+        self.logger = logger
+
+    @abstractmethod
+    def start(self):
+        pass
+
+    @abstractmethod
+    def stop(self):
+        pass
+
+    @abstractmethod
+    def run(self, cmd, timeout=None):
+        pass
+
+    @abstractmethod
+    def copyTo(self, localSrc, remoteDst):
+        pass
+
+    @abstractmethod
+    def copyFrom(self, remoteSrc, localDst):
+        pass
+
+    @abstractmethod
+    def copyDirTo(self, localSrc, remoteDst):
+        pass
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/target/qemu.py b/import-layers/yocto-poky/meta/lib/oeqa/core/target/qemu.py
new file mode 100644
index 0000000..2dc521c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/target/qemu.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import sys
+import signal
+import time
+
+from .ssh import OESSHTarget
+from oeqa.utils.qemurunner import QemuRunner
+
+supported_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic', 'elf']
+
+class OEQemuTarget(OESSHTarget):
+    def __init__(self, logger, ip, server_ip, timeout=300, user='root',
+            port=None, machine='', rootfs='', kernel='', kvm=False,
+            dump_dir='', dump_host_cmds='', display='', bootlog='',
+            tmpdir='', dir_image='', boottime=60, **kwargs):
+
+        super(OEQemuTarget, self).__init__(logger, ip, server_ip, timeout,
+                user, port)
+
+        self.ip = ip
+        self.server_ip = server_ip
+        self.machine = machine
+        self.rootfs = rootfs
+        self.kernel = kernel
+        self.kvm = kvm
+
+        self.runner = QemuRunner(machine=machine, rootfs=rootfs, tmpdir=tmpdir,
+                                 deploy_dir_image=dir_image, display=display,
+                                 logfile=bootlog, boottime=boottime,
+                                 use_kvm=kvm, dump_dir=dump_dir,
+                                 dump_host_cmds=dump_host_cmds)
+
+    def start(self, params=None, extra_bootparams=None):
+        if self.runner.start(params, extra_bootparams=extra_bootparams):
+            self.ip = self.runner.ip
+            self.server_ip = self.runner.server_ip
+        else:
+            self.stop()
+            raise RuntimeError("FAILED to start qemu - check the task log and the boot log")
+
+    def stop(self):
+        self.runner.stop()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/target/ssh.py b/import-layers/yocto-poky/meta/lib/oeqa/core/target/ssh.py
new file mode 100644
index 0000000..b80939c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/target/ssh.py
@@ -0,0 +1,266 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import time
+import select
+import logging
+import subprocess
+
+from . import OETarget
+
+class OESSHTarget(OETarget):
+    def __init__(self, logger, ip, server_ip, timeout=300, user='root',
+                 port=None, **kwargs):
+        if not logger:
+            logger = logging.getLogger('target')
+            logger.setLevel(logging.INFO)
+            filePath = os.path.join(os.getcwd(), 'remoteTarget.log')
+            fileHandler = logging.FileHandler(filePath, 'w', 'utf-8')
+            formatter = logging.Formatter(
+                        '%(asctime)s.%(msecs)03d %(levelname)s: %(message)s',
+                        '%H:%M:%S')
+            fileHandler.setFormatter(formatter)
+            logger.addHandler(fileHandler)
+
+        super(OESSHTarget, self).__init__(logger)
+        self.ip = ip
+        self.server_ip = server_ip
+        self.timeout = timeout
+        self.user = user
+        ssh_options = [
+                '-o', 'UserKnownHostsFile=/dev/null',
+                '-o', 'StrictHostKeyChecking=no',
+                '-o', 'LogLevel=ERROR'
+                ]
+        self.ssh = ['ssh', '-l', self.user ] + ssh_options
+        self.scp = ['scp'] + ssh_options
+        if port:
+            self.ssh = self.ssh + [ '-p', port ]
+            self.scp = self.scp + [ '-P', port ]
+
+    def start(self, **kwargs):
+        pass
+
+    def stop(self, **kwargs):
+        pass
+
+    def _run(self, command, timeout=None, ignore_status=True):
+        """
+            Runs command in target using SSHProcess.
+        """
+        self.logger.debug("[Running]$ %s" % " ".join(command))
+
+        starttime = time.time()
+        status, output = SSHCall(command, self.logger, timeout)
+        self.logger.debug("[Command returned '%d' after %.2f seconds]"
+                 "" % (status, time.time() - starttime))
+
+        if status and not ignore_status:
+            raise AssertionError("Command '%s' returned non-zero exit "
+                                 "status %d:\n%s" % (command, status, output))
+
+        return (status, output)
+
+    def run(self, command, timeout=None):
+        """
+            Runs command in target.
+
+            command:    Command to run on target.
+            timeout:    <value>:    Kill command after <val> seconds.
+                        None:       Kill command default value seconds.
+                        0:          No timeout, runs until return.
+        """
+        targetCmd = 'export PATH=/usr/sbin:/sbin:/usr/bin:/bin; %s' % command
+        sshCmd = self.ssh + [self.ip, targetCmd]
+
+        if timeout:
+            processTimeout = timeout
+        elif timeout==0:
+            processTimeout = None
+        else:
+            processTimeout = self.timeout
+
+        status, output = self._run(sshCmd, processTimeout, True)
+        self.logger.info('\nCommand: %s\nOutput:  %s\n' % (command, output))
+        return (status, output)
+
+    def copyTo(self, localSrc, remoteDst):
+        """
+            Copy file to target.
+
+            If local file is symlink, recreate symlink in target.
+        """
+        if os.path.islink(localSrc):
+            link = os.readlink(localSrc)
+            dstDir, dstBase = os.path.split(remoteDst)
+            sshCmd = 'cd %s; ln -s %s %s' % (dstDir, link, dstBase)
+            return self.run(sshCmd)
+
+        else:
+            remotePath = '%s@%s:%s' % (self.user, self.ip, remoteDst)
+            scpCmd = self.scp + [localSrc, remotePath]
+            return self._run(scpCmd, ignore_status=False)
+
+    def copyFrom(self, remoteSrc, localDst):
+        """
+            Copy file from target.
+        """
+        remotePath = '%s@%s:%s' % (self.user, self.ip, remoteSrc)
+        scpCmd = self.scp + [remotePath, localDst]
+        return self._run(scpCmd, ignore_status=False)
+
+    def copyDirTo(self, localSrc, remoteDst):
+        """
+            Copy recursively localSrc directory to remoteDst in target.
+        """
+
+        for root, dirs, files in os.walk(localSrc):
+            # Create directories in the target as needed
+            for d in dirs:
+                tmpDir = os.path.join(root, d).replace(localSrc, "")
+                newDir = os.path.join(remoteDst, tmpDir.lstrip("/"))
+                cmd = "mkdir -p %s" % newDir
+                self.run(cmd)
+
+            # Copy files into the target
+            for f in files:
+                tmpFile = os.path.join(root, f).replace(localSrc, "")
+                dstFile = os.path.join(remoteDst, tmpFile.lstrip("/"))
+                srcFile = os.path.join(root, f)
+                self.copyTo(srcFile, dstFile)
+
+    def deleteFiles(self, remotePath, files):
+        """
+            Deletes files in target's remotePath.
+        """
+
+        cmd = "rm"
+        if not isinstance(files, list):
+            files = [files]
+
+        for f in files:
+            cmd = "%s %s" % (cmd, os.path.join(remotePath, f))
+
+        self.run(cmd)
+
+
+    def deleteDir(self, remotePath):
+        """
+            Deletes target's remotePath directory.
+        """
+
+        cmd = "rmdir %s" % remotePath
+        self.run(cmd)
+
+
+    def deleteDirStructure(self, localPath, remotePath):
+        """
+        Delete recursively localPath structure directory in target's remotePath.
+
+        This function is very usefult to delete a package that is installed in
+        the DUT and the host running the test has such package extracted in tmp
+        directory.
+
+        Example:
+            pwd: /home/user/tmp
+            tree:   .
+                    └── work
+                        ├── dir1
+                        │   └── file1
+                        └── dir2
+
+            localpath = "/home/user/tmp" and remotepath = "/home/user"
+
+            With the above variables this function will try to delete the
+            directory in the DUT in this order:
+                /home/user/work/dir1/file1
+                /home/user/work/dir1        (if dir is empty)
+                /home/user/work/dir2        (if dir is empty)
+                /home/user/work             (if dir is empty)
+        """
+
+        for root, dirs, files in os.walk(localPath, topdown=False):
+            # Delete files first
+            tmpDir = os.path.join(root).replace(localPath, "")
+            remoteDir = os.path.join(remotePath, tmpDir.lstrip("/"))
+            self.deleteFiles(remoteDir, files)
+
+            # Remove dirs if empty
+            for d in dirs:
+                tmpDir = os.path.join(root, d).replace(localPath, "")
+                remoteDir = os.path.join(remotePath, tmpDir.lstrip("/"))
+                self.deleteDir(remoteDir)
+
+def SSHCall(command, logger, timeout=None, **opts):
+
+    def run():
+        nonlocal output
+        nonlocal process
+        starttime = time.time()
+        process = subprocess.Popen(command, **options)
+        if timeout:
+            endtime = starttime + timeout
+            eof = False
+            while time.time() < endtime and not eof:
+                logger.debug('time: %s, endtime: %s' % (time.time(), endtime))
+                try:
+                    if select.select([process.stdout], [], [], 5)[0] != []:
+                        data = os.read(process.stdout.fileno(), 1024)
+                        if not data:
+                            process.stdout.close()
+                            eof = True
+                        else:
+                            data = data.decode("utf-8")
+                            output += data
+                            logger.debug('Partial data from SSH call: %s' % data)
+                            endtime = time.time() + timeout
+                except InterruptedError:
+                    continue
+
+            # process hasn't returned yet
+            if not eof:
+                process.terminate()
+                time.sleep(5)
+                try:
+                    process.kill()
+                except OSError:
+                    pass
+                endtime = time.time() - starttime
+                lastline = ("\nProcess killed - no output for %d seconds. Total"
+                            " running time: %d seconds." % (timeout, endtime))
+                logger.debug('Received data from SSH call %s ' % lastline)
+                output += lastline
+
+        else:
+            output = process.communicate()[0].decode("utf-8")
+            logger.debug('Data from SSH call: %s' % output.rstrip())
+
+    options = {
+        "stdout": subprocess.PIPE,
+        "stderr": subprocess.STDOUT,
+        "stdin": None,
+        "shell": False,
+        "bufsize": -1,
+        "preexec_fn": os.setsid,
+    }
+    options.update(opts)
+    output = ''
+    process = None
+
+    # Unset DISPLAY which means we won't trigger SSH_ASKPASS
+    env = os.environ.copy()
+    if "DISPLAY" in env:
+        del env['DISPLAY']
+    options['env'] = env
+
+    try:
+        run()
+    except:
+        # Need to guard against a SystemExit or other exception ocurring
+        # whilst running and ensure we don't leave a process behind.
+        if process.poll() is None:
+            process.kill()
+        logger.debug('Something went wrong, killing SSH process')
+        raise
+    return (process.wait(), output.rstrip())
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/data.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/data.py
new file mode 100644
index 0000000..88003a6
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/data.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.oetag import OETestTag
+from oeqa.core.decorator.data import OETestDataDepends
+
+class DataTest(OETestCase):
+    data_vars = ['IMAGE', 'ARCH']
+
+    @OETestDataDepends(['MACHINE',])
+    @OETestTag('dataTestOk')
+    def testDataOk(self):
+        self.assertEqual(self.td.get('IMAGE'), 'core-image-minimal')
+        self.assertEqual(self.td.get('ARCH'), 'x86')
+        self.assertEqual(self.td.get('MACHINE'), 'qemuarm')
+
+    @OETestTag('dataTestFail')
+    def testDataFail(self):
+        pass
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/depends.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/depends.py
new file mode 100644
index 0000000..17cdd90
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/depends.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.depends import OETestDepends
+
+class DependsTest(OETestCase):
+
+    def testDependsFirst(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsFirst'])
+    def testDependsSecond(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsSecond'])
+    def testDependsThird(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsSecond'])
+    def testDependsFourth(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsThird', 'testDependsFourth'])
+    def testDependsFifth(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsCircular3'])
+    def testDependsCircular1(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsCircular1'])
+    def testDependsCircular2(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestDepends(['testDependsCircular2'])
+    def testDependsCircular3(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/invalid/oeid.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/invalid/oeid.py
new file mode 100644
index 0000000..038d445
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/invalid/oeid.py
@@ -0,0 +1,15 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+
+class AnotherIDTest(OETestCase):
+
+    def testAnotherIdGood(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    def testAnotherIdOther(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    def testAnotherIdNone(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/valid/another.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/valid/another.py
new file mode 100644
index 0000000..c9ffd17
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/loader/valid/another.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+
+class AnotherTest(OETestCase):
+
+    def testAnother(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oeid.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oeid.py
new file mode 100644
index 0000000..c2d3d32
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oeid.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.oeid import OETestID
+
+class IDTest(OETestCase):
+
+    @OETestID(101)
+    def testIdGood(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestID(102)
+    def testIdOther(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    def testIdNone(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oetag.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oetag.py
new file mode 100644
index 0000000..0cae02e
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/oetag.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.oetag import OETestTag
+
+class TagTest(OETestCase):
+
+    @OETestTag('goodTag')
+    def testTagGood(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETestTag('otherTag')
+    def testTagOther(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    def testTagNone(self):
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/timeout.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/timeout.py
new file mode 100644
index 0000000..870c315
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/cases/timeout.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from time import sleep
+
+from oeqa.core.case import OETestCase
+from oeqa.core.decorator.oetimeout import OETimeout
+
+class TimeoutTest(OETestCase):
+
+    @OETimeout(1)
+    def testTimeoutPass(self):
+        self.assertTrue(True, msg='How is this possible?')
+
+    @OETimeout(1)
+    def testTimeoutFail(self):
+        sleep(2)
+        self.assertTrue(True, msg='How is this possible?')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/common.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/common.py
new file mode 100644
index 0000000..52b18a1
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/common.py
@@ -0,0 +1,35 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import sys
+import os
+
+import unittest
+import logging
+import os
+
+logger = logging.getLogger("oeqa")
+logger.setLevel(logging.INFO)
+consoleHandler = logging.StreamHandler()
+formatter = logging.Formatter('OEQATest: %(message)s')
+consoleHandler.setFormatter(formatter)
+logger.addHandler(consoleHandler)
+
+def setup_sys_path():
+    directory = os.path.dirname(os.path.abspath(__file__))
+    oeqa_lib = os.path.realpath(os.path.join(directory, '../../../'))
+    if not oeqa_lib in sys.path:
+        sys.path.insert(0, oeqa_lib)
+
+class TestBase(unittest.TestCase):
+    def setUp(self):
+        self.logger = logger
+        directory = os.path.dirname(os.path.abspath(__file__))
+        self.cases_path = os.path.join(directory, 'cases')
+
+    def _testLoader(self, d={}, modules=[], tests=[], filters={}):
+        from oeqa.core.context import OETestContext
+        tc = OETestContext(d, self.logger)
+        tc.loadTests(self.cases_path, modules=modules, tests=tests,
+                     filters=filters)
+        return tc
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_data.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_data.py
new file mode 100755
index 0000000..320468c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_data.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import unittest
+import logging
+import os
+
+from common import setup_sys_path, TestBase
+setup_sys_path()
+
+from oeqa.core.exception import OEQAMissingVariable
+from oeqa.core.utils.test import getCaseMethod, getSuiteCasesNames
+
+class TestData(TestBase):
+    modules = ['data']
+
+    def test_data_fail_missing_variable(self):
+        expectedException = "oeqa.core.exception.OEQAMissingVariable"
+
+        tc = self._testLoader(modules=self.modules)
+        self.assertEqual(False, tc.runTests().wasSuccessful())
+        for test, data in tc._results['errors']:
+            expect = False
+            if expectedException in data:
+                expect = True
+
+            self.assertTrue(expect)
+
+    def test_data_fail_wrong_variable(self):
+        expectedError = 'AssertionError'
+        d = {'IMAGE' : 'core-image-sato', 'ARCH' : 'arm'}
+
+        tc = self._testLoader(d=d, modules=self.modules)
+        self.assertEqual(False, tc.runTests().wasSuccessful())
+        for test, data in tc._results['failures']:
+            expect = False
+            if expectedError in data:
+                expect = True
+
+            self.assertTrue(expect)
+
+    def test_data_ok(self):
+        d = {'IMAGE' : 'core-image-minimal', 'ARCH' : 'x86', 'MACHINE' : 'qemuarm'}
+
+        tc = self._testLoader(d=d, modules=self.modules)
+        self.assertEqual(True, tc.runTests().wasSuccessful())
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_decorators.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_decorators.py
new file mode 100755
index 0000000..f7d11e8
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_decorators.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import signal
+import unittest
+
+from common import setup_sys_path, TestBase
+setup_sys_path()
+
+from oeqa.core.exception import OEQADependency
+from oeqa.core.utils.test import getCaseMethod, getSuiteCasesNames, getSuiteCasesIDs
+
+class TestFilterDecorator(TestBase):
+
+    def _runFilterTest(self, modules, filters, expect, msg):
+        tc = self._testLoader(modules=modules, filters=filters)
+        test_loaded = set(getSuiteCasesNames(tc.suites))
+        self.assertEqual(expect, test_loaded, msg=msg)
+
+    def test_oetag(self):
+        # Get all cases without filtering.
+        filter_all = {}
+        test_all = {'testTagGood', 'testTagOther', 'testTagNone'}
+        msg_all = 'Failed to get all oetag cases without filtering.'
+
+        # Get cases with 'goodTag'.
+        filter_good = {'oetag':'goodTag'}
+        test_good = {'testTagGood'}
+        msg_good = 'Failed to get just one test filtering with "goodTag" oetag.'
+
+        # Get cases with an invalid tag.
+        filter_invalid = {'oetag':'invalidTag'}
+        test_invalid = set()
+        msg_invalid = 'Failed to filter all test using an invalid oetag.'
+
+        tests = ((filter_all, test_all, msg_all),
+                 (filter_good, test_good, msg_good),
+                 (filter_invalid, test_invalid, msg_invalid))
+
+        for test in tests:
+            self._runFilterTest(['oetag'], test[0], test[1], test[2])
+
+    def test_oeid(self):
+        # Get all cases without filtering.
+        filter_all = {}
+        test_all = {'testIdGood', 'testIdOther', 'testIdNone'}
+        msg_all = 'Failed to get all oeid cases without filtering.'
+
+        # Get cases with '101' oeid.
+        filter_good = {'oeid': 101}
+        test_good = {'testIdGood'}
+        msg_good = 'Failed to get just one tes filtering with "101" oeid.'
+
+        # Get cases with an invalid id.
+        filter_invalid = {'oeid':999}
+        test_invalid = set()
+        msg_invalid = 'Failed to filter all test using an invalid oeid.'
+
+        tests = ((filter_all, test_all, msg_all),
+                 (filter_good, test_good, msg_good),
+                 (filter_invalid, test_invalid, msg_invalid))
+
+        for test in tests:
+            self._runFilterTest(['oeid'], test[0], test[1], test[2])
+
+class TestDependsDecorator(TestBase):
+    modules = ['depends']
+
+    def test_depends_order(self):
+        tests =  ['depends.DependsTest.testDependsFirst',
+                  'depends.DependsTest.testDependsSecond',
+                  'depends.DependsTest.testDependsThird',
+                  'depends.DependsTest.testDependsFourth',
+                  'depends.DependsTest.testDependsFifth']
+        tests2 = list(tests)
+        tests2[2], tests2[3] = tests[3], tests[2]
+        tc = self._testLoader(modules=self.modules, tests=tests)
+        test_loaded = getSuiteCasesIDs(tc.suites)
+        result = True if test_loaded == tests or test_loaded == tests2 else False
+        msg = 'Failed to order tests using OETestDepends decorator.\nTest order:'\
+              ' %s.\nExpected:   %s\nOr:         %s' % (test_loaded, tests, tests2)
+        self.assertTrue(result, msg=msg)
+
+    def test_depends_fail_missing_dependency(self):
+        expect = "TestCase depends.DependsTest.testDependsSecond depends on "\
+                 "depends.DependsTest.testDependsFirst and isn't available"
+        tests =  ['depends.DependsTest.testDependsSecond']
+        try:
+            # Must throw OEQADependency because missing 'testDependsFirst'
+            tc = self._testLoader(modules=self.modules, tests=tests)
+            self.fail('Expected OEQADependency exception')
+        except OEQADependency as e:
+            result = True if expect in str(e) else False
+            msg = 'Expected OEQADependency exception missing testDependsFirst test'
+            self.assertTrue(result, msg=msg)
+
+    def test_depends_fail_circular_dependency(self):
+        expect = 'have a circular dependency'
+        tests =  ['depends.DependsTest.testDependsCircular1',
+                  'depends.DependsTest.testDependsCircular2',
+                  'depends.DependsTest.testDependsCircular3']
+        try:
+            # Must throw OEQADependency because circular dependency
+            tc = self._testLoader(modules=self.modules, tests=tests)
+            self.fail('Expected OEQADependency exception')
+        except OEQADependency as e:
+            result = True if expect in str(e) else False
+            msg = 'Expected OEQADependency exception having a circular dependency'
+            self.assertTrue(result, msg=msg)
+
+class TestTimeoutDecorator(TestBase):
+    modules = ['timeout']
+
+    def test_timeout(self):
+        tests = ['timeout.TimeoutTest.testTimeoutPass']
+        msg = 'Failed to run test using OETestTimeout'
+        alarm_signal = signal.getsignal(signal.SIGALRM)
+        tc = self._testLoader(modules=self.modules, tests=tests)
+        self.assertTrue(tc.runTests().wasSuccessful(), msg=msg)
+        msg = "OETestTimeout didn't restore SIGALRM"
+        self.assertIs(alarm_signal, signal.getsignal(signal.SIGALRM), msg=msg)
+
+    def test_timeout_fail(self):
+        tests = ['timeout.TimeoutTest.testTimeoutFail']
+        msg = "OETestTimeout test didn't timeout as expected"
+        alarm_signal = signal.getsignal(signal.SIGALRM)
+        tc = self._testLoader(modules=self.modules, tests=tests)
+        self.assertFalse(tc.runTests().wasSuccessful(), msg=msg)
+        msg = "OETestTimeout didn't restore SIGALRM"
+        self.assertIs(alarm_signal, signal.getsignal(signal.SIGALRM), msg=msg)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_loader.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_loader.py
new file mode 100755
index 0000000..b79b8ba
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_loader.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import unittest
+
+from common import setup_sys_path, TestBase
+setup_sys_path()
+
+from oeqa.core.exception import OEQADependency
+from oeqa.core.utils.test import getSuiteModules, getSuiteCasesIDs
+
+class TestLoader(TestBase):
+
+    def test_fail_empty_filter(self):
+        filters = {'oetag' : ''}
+        expect = 'Filter oetag specified is empty'
+        msg = 'Expected TypeError exception for having invalid filter'
+        try:
+            # Must throw TypeError because empty filter
+            tc = self._testLoader(filters=filters)
+            self.fail(msg)
+        except TypeError as e:
+            result = True if expect in str(e) else False
+            self.assertTrue(result, msg=msg)
+
+    def test_fail_invalid_filter(self):
+        filters = {'invalid' : 'good'}
+        expect = 'filter but not declared in any of'
+        msg = 'Expected TypeError exception for having invalid filter'
+        try:
+            # Must throw TypeError because invalid filter
+            tc = self._testLoader(filters=filters)
+            self.fail(msg)
+        except TypeError as e:
+            result = True if expect in str(e) else False
+            self.assertTrue(result, msg=msg)
+
+    def test_fail_duplicated_module(self):
+        cases_path = self.cases_path
+        invalid_path = os.path.join(cases_path, 'loader', 'invalid')
+        self.cases_path = [self.cases_path, invalid_path]
+        expect = 'Duplicated oeid module found in'
+        msg = 'Expected ImportError exception for having duplicated module'
+        try:
+            # Must throw ImportEror because duplicated module
+            tc = self._testLoader()
+            self.fail(msg)
+        except ImportError as e:
+            result = True if expect in str(e) else False
+            self.assertTrue(result, msg=msg)
+        finally:
+            self.cases_path = cases_path
+
+    def test_filter_modules(self):
+        expected_modules = {'oeid', 'oetag'}
+        tc = self._testLoader(modules=expected_modules)
+        modules = getSuiteModules(tc.suites)
+        msg = 'Expected just %s modules' % ', '.join(expected_modules)
+        self.assertEqual(modules, expected_modules, msg=msg)
+
+    def test_filter_cases(self):
+        modules = ['oeid', 'oetag', 'data']
+        expected_cases = {'data.DataTest.testDataOk',
+                          'oetag.TagTest.testTagGood',
+                          'oeid.IDTest.testIdGood'}
+        tc = self._testLoader(modules=modules, tests=expected_cases)
+        cases = set(getSuiteCasesIDs(tc.suites))
+        msg = 'Expected just %s cases' % ', '.join(expected_cases)
+        self.assertEqual(cases, expected_cases, msg=msg)
+
+    def test_import_from_paths(self):
+        cases_path = self.cases_path
+        cases2_path = os.path.join(cases_path, 'loader', 'valid')
+        expected_modules = {'oeid', 'another'}
+        self.cases_path = [self.cases_path, cases2_path]
+        tc = self._testLoader(modules=expected_modules)
+        modules = getSuiteModules(tc.suites)
+        self.cases_path = cases_path
+        msg = 'Expected modules from two different paths'
+        self.assertEqual(modules, expected_modules, msg=msg)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_runner.py b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_runner.py
new file mode 100755
index 0000000..a3f3861
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/tests/test_runner.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import unittest
+import logging
+import tempfile
+
+from common import setup_sys_path, TestBase
+setup_sys_path()
+
+from oeqa.core.runner import OEStreamLogger
+
+class TestRunner(TestBase):
+    def test_stream_logger(self):
+        fp = tempfile.TemporaryFile(mode='w+')
+
+        logging.basicConfig(format='%(message)s', stream=fp)
+        logger = logging.getLogger()
+        logger.setLevel(logging.INFO)
+
+        oeSL = OEStreamLogger(logger)
+
+        lines = ['init', 'bigline_' * 65535, 'morebigline_' * 65535 * 4, 'end']
+        for line in lines:
+            oeSL.write(line)
+
+        fp.seek(0)
+        fp_lines = fp.readlines()
+        for i, fp_line in enumerate(fp_lines):
+            fp_line = fp_line.strip()
+            self.assertEqual(lines[i], fp_line)
+
+        fp.close()
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/utils/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/utils/misc.py b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/misc.py
new file mode 100644
index 0000000..0b223b5
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/misc.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+def toList(obj, obj_type, obj_name="Object"):
+    if isinstance(obj, obj_type):
+        return [obj]
+    elif isinstance(obj, list):
+        return obj
+    else:
+        raise TypeError("%s must be %s or list" % (obj_name, obj_type))
+
+def toSet(obj, obj_type, obj_name="Object"):
+    if isinstance(obj, obj_type):
+        return {obj}
+    elif isinstance(obj, list):
+        return set(obj)
+    elif isinstance(obj, set):
+        return obj
+    else:
+        raise TypeError("%s must be %s or set" % (obj_name, obj_type))
+
+def strToList(obj, obj_name="Object"):
+    return toList(obj, str, obj_name)
+
+def strToSet(obj, obj_name="Object"):
+    return toSet(obj, str, obj_name)
+
+def intToList(obj, obj_name="Object"):
+    return toList(obj, int, obj_name)
+
+def dataStoteToDict(d, variables):
+    data = {}
+
+    for v in variables:
+        data[v] = d.getVar(v)
+
+    return data
+
+def updateTestData(d, td, variables):
+    """
+    Updates variables with values of data store to test data.
+    """
+    for var in variables:
+        td[var] = d.getVar(var)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/utils/path.py b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/path.py
new file mode 100644
index 0000000..a21caad
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/path.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import sys
+
+def findFile(file_name, directory):
+    """
+        Search for a file in directory and returns its complete path.
+    """
+    for r, d, f in os.walk(directory):
+        if file_name in f:
+            return os.path.join(r, file_name)
+    return None
+
+def remove_safe(path):
+    if os.path.exists(path):
+        os.remove(path)
+
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/core/utils/test.py b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/test.py
new file mode 100644
index 0000000..88d5d13
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/core/utils/test.py
@@ -0,0 +1,86 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import inspect
+import unittest
+
+def getSuiteCases(suite):
+    """
+        Returns individual test from a test suite.
+    """
+    tests = []
+
+    if isinstance(suite, unittest.TestCase):
+        tests.append(suite)
+    elif isinstance(suite, unittest.suite.TestSuite):
+        for item in suite:
+            tests.extend(getSuiteCases(item))
+
+    return tests
+
+def getSuiteModules(suite):
+    """
+        Returns modules in a test suite.
+    """
+    modules = set()
+    for test in getSuiteCases(suite):
+        modules.add(getCaseModule(test))
+    return modules
+
+def getSuiteCasesInfo(suite, func):
+    """
+        Returns test case info from suite. Info is fetched from func.
+    """
+    tests = []
+    for test in getSuiteCases(suite):
+        tests.append(func(test))
+    return tests
+
+def getSuiteCasesNames(suite):
+    """
+        Returns test case names from suite.
+    """
+    return getSuiteCasesInfo(suite, getCaseMethod)
+
+def getSuiteCasesIDs(suite):
+    """
+        Returns test case ids from suite.
+    """
+    return getSuiteCasesInfo(suite, getCaseID)
+
+def getSuiteCasesFiles(suite):
+    """
+        Returns test case files paths from suite.
+    """
+    return getSuiteCasesInfo(suite, getCaseFile)
+
+def getCaseModule(test_case):
+    """
+        Returns test case module name.
+    """
+    return test_case.__module__
+
+def getCaseClass(test_case):
+    """
+        Returns test case class name.
+    """
+    return test_case.__class__.__name__
+
+def getCaseID(test_case):
+    """
+        Returns test case complete id.
+    """
+    return test_case.id()
+
+def getCaseFile(test_case):
+    """
+        Returns test case file path.
+    """
+    return inspect.getsourcefile(test_case.__class__)
+
+def getCaseMethod(test_case):
+    """
+        Returns test case method name.
+    """
+    return getCaseID(test_case).split('.')[-1]
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.c b/import-layers/yocto-poky/meta/lib/oeqa/files/test.c
similarity index 100%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.c
rename to import-layers/yocto-poky/meta/lib/oeqa/files/test.c
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.cpp b/import-layers/yocto-poky/meta/lib/oeqa/files/test.cpp
similarity index 100%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.cpp
rename to import-layers/yocto-poky/meta/lib/oeqa/files/test.cpp
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.pl b/import-layers/yocto-poky/meta/lib/oeqa/files/test.pl
similarity index 100%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.pl
rename to import-layers/yocto-poky/meta/lib/oeqa/files/test.pl
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py b/import-layers/yocto-poky/meta/lib/oeqa/files/test.py
similarity index 100%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/files/test.py
rename to import-layers/yocto-poky/meta/lib/oeqa/files/test.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/oetest.py b/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
index 95d3bf7..f717126 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/oetest.py
@@ -27,7 +27,6 @@
 except ImportError:
     pass
 from oeqa.utils.decorators import LogResults, gettag, getResults
-from oeqa.utils import avoid_paths_in_environ
 
 logger = logging.getLogger("BitBake")
 
@@ -107,7 +106,7 @@
         pass
 
     def tearDown(self):
-        # Unistall packages in the DUT
+        # Uninstall packages in the DUT
         self.tc.install_uninstall_packages(self.id(), False)
 
         res = getResults()
@@ -129,48 +128,6 @@
     def tearDownLocal(self):
         pass
 
-    #TODO: use package_manager.py to install packages on any type of image
-    def install_packages(self, packagelist):
-        for package in packagelist:
-            (status, result) = self.target.run("smart install -y "+package)
-            if status != 0:
-                return status
-
-class OETestCalledProcessError(subprocess.CalledProcessError):
-    def __str__(self):
-        if hasattr(self, "stderr"):
-            return "Command '%s' returned non-zero exit status %d with output %s and stderr %s" % (self.cmd, self.returncode, self.output, self.stderr)
-        else:
-            return "Command '%s' returned non-zero exit status %d with output %s" % (self.cmd, self.returncode, self.output)
-
-subprocess.CalledProcessError = OETestCalledProcessError
-
-class oeSDKTest(oeTest):
-    def __init__(self, methodName='runTest'):
-        self.sdktestdir = oeSDKTest.tc.sdktestdir
-        super(oeSDKTest, self).__init__(methodName)
-
-    @classmethod
-    def hasHostPackage(self, pkg):
-        if re.search(pkg, oeTest.tc.hostpkgmanifest):
-            return True
-        return False
-
-    def _run(self, cmd):
-        return subprocess.check_output(". %s > /dev/null; %s;" % (self.tc.sdkenv, cmd), shell=True, stderr=subprocess.STDOUT).decode("utf-8")
-
-class oeSDKExtTest(oeSDKTest):
-    def _run(self, cmd):
-        # extensible sdk shows a warning if found bitbake in the path
-        # because can cause contamination, i.e. use devtool from
-        # poky/scripts instead of eSDK one.
-        env = os.environ.copy()
-        paths_to_avoid = ['bitbake/bin', 'poky/scripts']
-        env['PATH'] = avoid_paths_in_environ(paths_to_avoid)
-
-        return subprocess.check_output(". %s > /dev/null;"\
-            " %s;" % (self.tc.sdkenv, cmd), stderr=subprocess.STDOUT, shell=True, env=env).decode("utf-8")
-
 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
@@ -221,15 +178,16 @@
             path = [os.path.dirname(os.path.abspath(__file__))]
             extrapath = ""
         else:
-            path = d.getVar("BBPATH", True).split(':')
+            path = d.getVar("BBPATH").split(':')
             extrapath = "lib/oeqa"
 
         self.testslist = self._get_tests_list(path, extrapath)
         self.testsrequired = self._get_test_suites_required()
 
         self.filesdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "runtime/files")
-        self.imagefeatures = d.getVar("IMAGE_FEATURES", True).split()
-        self.distrofeatures = d.getVar("DISTRO_FEATURES", True).split()
+        self.corefilesdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files")
+        self.imagefeatures = d.getVar("IMAGE_FEATURES").split()
+        self.distrofeatures = d.getVar("DISTRO_FEATURES").split()
 
     # get testcase list from specified file
     # if path is a relative path, then relative to build/conf/
@@ -406,9 +364,9 @@
         self.target = target
 
         self.pkgmanifest = {}
-        manifest = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True),
-                d.getVar("IMAGE_LINK_NAME", True) + ".manifest")
-        nomanifest = d.getVar("IMAGE_NO_MANIFEST", True)
+        manifest = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"),
+                d.getVar("IMAGE_LINK_NAME") + ".manifest")
+        nomanifest = d.getVar("IMAGE_NO_MANIFEST")
         if nomanifest is None or nomanifest != "1":
             try:
                 with open(manifest) as f:
@@ -424,19 +382,19 @@
     def _get_test_suites(self):
         testsuites = []
 
-        manifests = (self.d.getVar("TEST_SUITES_MANIFEST", True) or '').split()
+        manifests = (self.d.getVar("TEST_SUITES_MANIFEST") or '').split()
         if manifests:
             for manifest in manifests:
                 testsuites.extend(self._read_testlist(manifest,
-                                  self.d.getVar("TOPDIR", True)).split())
+                                  self.d.getVar("TOPDIR")).split())
 
         else:
-            testsuites = self.d.getVar("TEST_SUITES", True).split()
+            testsuites = self.d.getVar("TEST_SUITES").split()
 
         return testsuites
 
     def _get_test_suites_required(self):
-        return [t for t in self.d.getVar("TEST_SUITES", True).split() if t != "auto"]
+        return [t for t in self.d.getVar("TEST_SUITES").split() if t != "auto"]
 
     def loadTests(self):
         super(RuntimeTestContext, self).loadTests()
@@ -449,10 +407,10 @@
         """
 
         modules = self.getTestModules()
-        bbpaths = self.d.getVar("BBPATH", True).split(":")
+        bbpaths = self.d.getVar("BBPATH").split(":")
 
-        shutil.rmtree(self.d.getVar("TEST_EXTRACTED_DIR", True))
-        shutil.rmtree(self.d.getVar("TEST_PACKAGED_DIR", True))
+        shutil.rmtree(self.d.getVar("TEST_EXTRACTED_DIR"))
+        shutil.rmtree(self.d.getVar("TEST_PACKAGED_DIR"))
         for module in modules:
             json_file = self._getJsonFile(module)
             if json_file:
@@ -466,8 +424,8 @@
 
         import oe.path
 
-        extracted_path = self.d.getVar("TEST_EXTRACTED_DIR", True)
-        packaged_path = self.d.getVar("TEST_PACKAGED_DIR", True)
+        extracted_path = self.d.getVar("TEST_EXTRACTED_DIR")
+        packaged_path = self.d.getVar("TEST_PACKAGED_DIR")
 
         for key,value in needed_packages.items():
             packages = ()
@@ -548,7 +506,7 @@
 
         from oeqa.utils.package_manager import get_package_manager
 
-        pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR", True), pkg)
+        pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR"), pkg)
         pm = get_package_manager(self.d, pkg_path)
         extract_dir = pm.extract(pkg)
         shutil.rmtree(pkg_path)
@@ -562,8 +520,8 @@
 
         from oeqa.utils.package_manager import get_package_manager
 
-        pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR", True), pkg)
-        dst_dir = self.d.getVar("TEST_PACKAGED_DIR", True)
+        pkg_path = os.path.join(self.d.getVar("TEST_INSTALL_TMP_DIR"), pkg)
+        dst_dir = self.d.getVar("TEST_PACKAGED_DIR")
         pm = get_package_manager(self.d, pkg_path)
         pkg_info = pm.package_info(pkg)
         file_path = pkg_info[pkg]["filepath"]
@@ -572,7 +530,7 @@
 
     def install_uninstall_packages(self, test_id, pkg_dir, install):
         """
-        Check if the test requires a package and Install/Unistall it in the DUT
+        Check if the test requires a package and Install/Uninstall it in the DUT
         """
 
         test = test_id.split(".")[4]
@@ -585,7 +543,7 @@
 
     def _install_uninstall_packages(self, needed_packages, pkg_dir, install=True):
         """
-        Install/Unistall packages in the DUT without using a package manager
+        Install/Uninstall packages in the DUT without using a package manager
         """
 
         if isinstance(needed_packages, dict):
@@ -603,7 +561,7 @@
             if install and extract:
                 self.target.connection.copy_dir_to(src_dir, "/")
 
-            # Unistall package
+            # Uninstall package
             elif not install and rm:
                 self.target.connection.delete_dir_structure(src_dir, "/")
 
@@ -611,7 +569,7 @@
     def __init__(self, d, target, host_dumper):
         super(ImageTestContext, self).__init__(d, target)
 
-        self.tagexp = d.getVar("TEST_SUITES_TAGS", True)
+        self.tagexp = d.getVar("TEST_SUITES_TAGS")
 
         self.host_dumper = host_dumper
 
@@ -626,10 +584,10 @@
 
     def install_uninstall_packages(self, test_id, install=True):
         """
-        Check if the test requires a package and Install/Unistall it in the DUT
+        Check if the test requires a package and Install/Uninstall it in the DUT
         """
 
-        pkg_dir = self.d.getVar("TEST_EXTRACTED_DIR", True)
+        pkg_dir = self.d.getVar("TEST_EXTRACTED_DIR")
         super(ImageTestContext, self).install_uninstall_packages(test_id, pkg_dir, install)
 
 class ExportTestContext(RuntimeTestContext):
@@ -643,80 +601,16 @@
         super(ExportTestContext, self).__init__(d, target, exported)
 
         tag = parsedArgs.get("tag", None)
-        self.tagexp = tag if tag != None else d.getVar("TEST_SUITES_TAGS", True)
+        self.tagexp = tag if tag != None else d.getVar("TEST_SUITES_TAGS")
 
         self.sigterm = None
 
     def install_uninstall_packages(self, test_id, install=True):
         """
-        Check if the test requires a package and Install/Unistall it in the DUT
+        Check if the test requires a package and Install/Uninstall it in the DUT
         """
 
         export_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-        extracted_dir = self.d.getVar("TEST_EXPORT_EXTRACTED_DIR", True)
+        extracted_dir = self.d.getVar("TEST_EXPORT_EXTRACTED_DIR")
         pkg_dir = os.path.join(export_dir, extracted_dir)
         super(ExportTestContext, self).install_uninstall_packages(test_id, pkg_dir, install)
-
-class SDKTestContext(TestContext):
-    def __init__(self, d, sdktestdir, sdkenv, tcname, *args):
-        super(SDKTestContext, self).__init__(d)
-
-        self.sdktestdir = sdktestdir
-        self.sdkenv = sdkenv
-        self.tcname = tcname
-
-        if not hasattr(self, 'target_manifest'):
-            self.target_manifest = d.getVar("SDK_TARGET_MANIFEST", True)
-        try:
-            self.pkgmanifest = {}
-            with open(self.target_manifest) as f:
-                for line in f:
-                    (pkg, arch, version) = line.strip().split()
-                    self.pkgmanifest[pkg] = (version, arch)
-        except IOError as e:
-            bb.fatal("No package manifest file found. Did you build the sdk image?\n%s" % e)
-
-        if not hasattr(self, 'host_manifest'):
-            self.host_manifest = d.getVar("SDK_HOST_MANIFEST", True)
-        try:
-            with open(self.host_manifest) as f:
-                self.hostpkgmanifest = f.read()
-        except IOError as e:
-            bb.fatal("No host package manifest file found. Did you build the sdk image?\n%s" % e)
-
-    def _get_test_namespace(self):
-        return "sdk"
-
-    def _get_test_suites(self):
-        return (self.d.getVar("TEST_SUITES_SDK", True) or "auto").split()
-
-    def _get_test_suites_required(self):
-        return [t for t in (self.d.getVar("TEST_SUITES_SDK", True) or \
-                "auto").split() if t != "auto"]
-
-class SDKExtTestContext(SDKTestContext):
-    def __init__(self, d, sdktestdir, sdkenv, tcname, *args):
-        self.target_manifest = d.getVar("SDK_EXT_TARGET_MANIFEST", True)
-        self.host_manifest = d.getVar("SDK_EXT_HOST_MANIFEST", True)
-        if args:
-            self.cm = args[0] # Compatibility mode for run SDK tests
-        else:
-            self.cm = False
-
-        super(SDKExtTestContext, self).__init__(d, sdktestdir, sdkenv, tcname)
-
-        self.sdkextfilesdir = os.path.join(os.path.dirname(os.path.abspath(
-            oeqa.sdkext.__file__)), "files")
-
-    def _get_test_namespace(self):
-        if self.cm:
-            return "sdk"
-        else:
-            return "sdkext"
-
-    def _get_test_suites(self):
-        return (self.d.getVar("TEST_SUITES_SDK_EXT", True) or "auto").split()
-
-    def _get_test_suites_required(self):
-        return [t for t in (self.d.getVar("TEST_SUITES_SDK_EXT", True) or \
-                "auto").split() if t != "auto"]
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runexported.py b/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
index 7e245c4..9cfea0f 100755
--- a/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runexported.py
@@ -43,8 +43,8 @@
         self.ip = None
         self.server_ip = None
         self.datetime = time.strftime('%Y%m%d%H%M%S',time.gmtime())
-        self.testdir = d.getVar("TEST_LOG_DIR", True)
-        self.pn = d.getVar("PN", True)
+        self.testdir = d.getVar("TEST_LOG_DIR")
+        self.pn = d.getVar("PN")
 
     def exportStart(self):
         self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
@@ -130,8 +130,8 @@
     """
 
     export_dir = os.path.dirname(os.path.realpath(__file__))
-    tools_dir = d.getVar("TEST_EXPORT_SDK_DIR", True)
-    tarball_name = "%s.sh" % d.getVar("TEST_EXPORT_SDK_NAME", True)
+    tools_dir = d.getVar("TEST_EXPORT_SDK_DIR")
+    tarball_name = "%s.sh" % d.getVar("TEST_EXPORT_SDK_NAME")
     tarball_path = os.path.join(export_dir, tools_dir, tarball_name)
     extract_path = os.path.join(export_dir, "sysroot")
     if os.path.isfile(tarball_path):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py
deleted file mode 100644
index 71324d3..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_ptest.py
+++ /dev/null
@@ -1,125 +0,0 @@
-import unittest, os, shutil
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.logparser import *
-from oeqa.utils.httpserver import HTTPService
-import bb
-import glob
-from oe.package_manager import RpmPkgsList
-import subprocess
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("package-management"):
-        skipModule("Image doesn't have package management feature")
-    if not oeRuntimeTest.hasPackage("smartpm"):
-        skipModule("Image doesn't have smart installed")
-    if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
-        skipModule("Rpm is not the primary package manager")
-
-class PtestRunnerTest(oeRuntimeTest):
-
-    # a ptest log parser
-    def parse_ptest(self, logfile):
-        parser = Lparser(test_0_pass_regex="^PASS:(.+)", test_0_fail_regex="^FAIL:(.+)", section_0_begin_regex="^BEGIN: .*/(.+)/ptest", section_0_end_regex="^END: .*/(.+)/ptest")
-        parser.init()
-        result = Result()
-
-        with open(logfile) as f:
-            for line in f:
-                result_tuple = parser.parse_line(line)
-                if not result_tuple:
-                    continue
-                result_tuple = line_type, category, status, name = parser.parse_line(line)
-
-                if line_type == 'section' and status == 'begin':
-                    current_section = name
-                    continue
-
-                if line_type == 'section' and status == 'end':
-                    current_section = None
-                    continue
-
-                if line_type == 'test' and status == 'pass':
-                    result.store(current_section, name, status)
-                    continue
-
-                if line_type == 'test' and status == 'fail':
-                    result.store(current_section, name, status)
-                    continue
-
-        result.sort_tests()
-        return result
-
-    @classmethod
-    def setUpClass(self):
-        #note the existing channels that are on the board before creating new ones
-#        self.existingchannels = set()
-#        (status, result) = oeRuntimeTest.tc.target.run('smart channel --show | grep "\["', 0)
-#        for x in result.split("\n"):
-#            self.existingchannels.add(x)
-        self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), oeRuntimeTest.tc.target.server_ip)
-        self.repo_server.start()
-
-    @classmethod
-    def tearDownClass(self):
-        self.repo_server.stop()
-        #remove created channels to be able to repeat the tests on same image
-#        (status, result) = oeRuntimeTest.tc.target.run('smart channel --show | grep "\["', 0)
-#        for x in result.split("\n"):
-#            if x not in self.existingchannels:
-#                oeRuntimeTest.tc.target.run('smart channel --remove '+x[1:-1]+' -y', 0)
-
-    def add_smart_channel(self):
-        image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
-        deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
-        pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True).replace("-","_").split()
-        for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
-            if arch in pkgarchs:
-                self.target.run('smart channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url), 0)
-        self.target.run('smart update', 0)
-
-    def install_complementary(self, globs=None):
-        installed_pkgs_file = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True),
-                                           "installed_pkgs.txt")
-        self.pkgs_list = RpmPkgsList(oeRuntimeTest.tc.d, oeRuntimeTest.tc.d.getVar('IMAGE_ROOTFS', True), oeRuntimeTest.tc.d.getVar('arch_var', True), oeRuntimeTest.tc.d.getVar('os_var', True))
-        with open(installed_pkgs_file, "w+") as installed_pkgs:
-            installed_pkgs.write(self.pkgs_list.list("arch"))
-
-        cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
-               "-p", oeRuntimeTest.tc.d.getVar('PKGDATA_DIR', True), "glob", installed_pkgs_file,
-               globs]
-        try:
-            bb.note("Installing complementary packages ...")
-            complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
-        except subprocess.CalledProcessError as e:
-            bb.fatal("Could not compute complementary packages list. Command "
-                     "'%s' returned %d:\n%s" %
-                     (' '.join(cmd), e.returncode, e.output))
-
-        return complementary_pkgs.split()
-
-    def setUpLocal(self):
-        self.ptest_log = os.path.join(oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR",True), "ptest-%s.log" % oeRuntimeTest.tc.d.getVar('DATETIME', True))
-
-    @skipUnlessPassed('test_ssh')
-    def test_ptestrunner(self):
-        self.add_smart_channel()
-        (runnerstatus, result) = self.target.run('which ptest-runner', 0)
-        cond = oeRuntimeTest.hasPackage("ptest-runner") and oeRuntimeTest.hasFeature("ptest") and oeRuntimeTest.hasPackageMatch("-ptest") and (runnerstatus != 0)
-        if cond:
-            self.install_packages(self.install_complementary("*-ptest"))
-            self.install_packages(['ptest-runner'])
-
-        (runnerstatus, result) = self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
-        #exit code is !=0 even if ptest-runner executes because some ptest tests fail.
-        self.assertTrue(runnerstatus != 127, msg="Cannot execute ptest-runner!")
-        self.target.copy_from('/tmp/ptest.log', self.ptest_log)
-        shutil.copyfile(self.ptest_log, "ptest.log")
-
-        result = self.parse_ptest("ptest.log")
-        log_results_to_location = "./results"
-        if os.path.exists(log_results_to_location):
-            shutil.rmtree(log_results_to_location)
-        os.makedirs(log_results_to_location)
-
-        result.log_as_files(log_results_to_location, test_status = ['pass','fail'])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_qemutiny.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/_qemutiny.py
deleted file mode 100644
index a3c29f3..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/_qemutiny.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest
-from oeqa.utils.qemutinyrunner import *
-
-class QemuTinyTest(oeRuntimeTest):
-
-    def test_boot_tiny(self):
-        (status, output) = self.target.run_serial('uname -a')
-        self.assertTrue("yocto-tiny" in output, msg="Cannot detect poky tiny boot!")
\ No newline at end of file
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildcvs.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildcvs.py
deleted file mode 100644
index fe6cbfb..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildcvs.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import TargetBuildProject
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("tools-sdk"):
-        skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
-
-class BuildCvsTest(oeRuntimeTest):
-
-    @classmethod
-    def setUpClass(self):
-        self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
-                        "http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2")
-        self.project.download_archive()
-
-    @testcase(205)
-    @skipUnlessPassed("test_ssh")
-    def test_cvs(self):
-        self.assertEqual(self.project.run_configure(), 0,
-                        msg="Running configure failed")
-
-        self.assertEqual(self.project.run_make(), 0,
-                        msg="Running make failed")
-
-        self.assertEqual(self.project.run_install(), 0,
-                        msg="Running make install failed")
-
-    @classmethod
-    def tearDownClass(self):
-        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py
deleted file mode 100644
index 28ba29e..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildgalculator.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import TargetBuildProject
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("tools-sdk"):
-        skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
-
-class GalculatorTest(oeRuntimeTest):
-    @skipUnlessPassed("test_ssh")
-    def test_galculator(self):
-        try:
-            project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
-                                      "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2")
-            project.download_archive()
-
-            self.assertEqual(project.run_configure(), 0,
-                            msg="Running configure failed")
-
-            self.assertEqual(project.run_make(), 0,
-                            msg="Running make failed")
-        finally:
-            project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py
deleted file mode 100644
index bc75d0a..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/buildiptables.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import TargetBuildProject
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("tools-sdk"):
-        skipModule("Image doesn't have tools-sdk in IMAGE_FEATURES")
-
-class BuildIptablesTest(oeRuntimeTest):
-
-    @classmethod
-    def setUpClass(self):
-        self.project = TargetBuildProject(oeRuntimeTest.tc.target, oeRuntimeTest.tc.d,
-                        "http://downloads.yoctoproject.org/mirror/sources/iptables-1.4.13.tar.bz2")
-        self.project.download_archive()
-
-    @testcase(206)
-    @skipUnlessPassed("test_ssh")
-    def test_iptables(self):
-        self.assertEqual(self.project.run_configure(), 0,
-                        msg="Running configure failed")
-
-        self.assertEqual(self.project.run_make(), 0,
-                        msg="Running make failed")
-
-        self.assertEqual(self.project.run_install(), 0,
-                        msg="Running make install failed")
-
-    @classmethod
-    def tearDownClass(self):
-        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/case.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/case.py
new file mode 100644
index 0000000..c1485c9
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/case.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.case import OETestCase
+from oeqa.utils.package_manager import install_package, uninstall_package
+
+class OERuntimeTestCase(OETestCase):
+    # target instance set by OERuntimeTestLoader.
+    target = None
+
+    def _oeSetUp(self):
+        super(OERuntimeTestCase, self)._oeSetUp()
+        install_package(self)
+
+    def _oeTearDown(self):
+        super(OERuntimeTestCase, self)._oeTearDown()
+        uninstall_package(self)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_ptest.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_ptest.py
new file mode 100644
index 0000000..aaed9a5
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_ptest.py
@@ -0,0 +1,103 @@
+import os
+import shutil
+import subprocess
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotDataVar, skipIfNotFeature
+from oeqa.runtime.decorator.package import OEHasPackage
+
+from oeqa.runtime.cases.dnf import DnfTest
+from oeqa.utils.logparser import *
+from oeqa.utils.httpserver import HTTPService
+
+class PtestRunnerTest(DnfTest):
+
+    @classmethod
+    def setUpClass(cls):
+        rpm_deploy = os.path.join(cls.tc.td['DEPLOY_DIR'], 'rpm')
+        cls.repo_server = HTTPService(rpm_deploy, cls.tc.target.server_ip)
+        cls.repo_server.start()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.repo_server.stop()
+
+    # a ptest log parser
+    def parse_ptest(self, logfile):
+        parser = Lparser(test_0_pass_regex="^PASS:(.+)",
+                         test_0_fail_regex="^FAIL:(.+)",
+                         section_0_begin_regex="^BEGIN: .*/(.+)/ptest",
+                         section_0_end_regex="^END: .*/(.+)/ptest")
+        parser.init()
+        result = Result()
+
+        with open(logfile, errors='replace') as f:
+            for line in f:
+                result_tuple = parser.parse_line(line)
+                if not result_tuple:
+                    continue
+                result_tuple = line_type, category, status, name = parser.parse_line(line)
+
+                if line_type == 'section' and status == 'begin':
+                    current_section = name
+                    continue
+
+                if line_type == 'section' and status == 'end':
+                    current_section = None
+                    continue
+
+                if line_type == 'test' and status == 'pass':
+                    result.store(current_section, name, status)
+                    continue
+
+                if line_type == 'test' and status == 'fail':
+                    result.store(current_section, name, status)
+                    continue
+
+        result.sort_tests()
+        return result
+
+    def _install_ptest_packages(self):
+        # Get ptest packages that can be installed in the image.
+        packages_dir = os.path.join(self.tc.td['DEPLOY_DIR'], 'rpm')
+        ptest_pkgs = [pkg[:pkg.find('-ptest')+6]
+                          for _, _, filenames in os.walk(packages_dir)
+                          for pkg in filenames
+                          if 'ptest' in pkg
+                          and pkg[:pkg.find('-ptest')] in self.tc.image_packages]
+
+        repo_url = 'http://%s:%s' % (self.target.server_ip,
+                                     self.repo_server.port)
+        dnf_options = ('--repofrompath=oe-ptest-repo,%s '
+                       '--nogpgcheck '
+                       'install -y' % repo_url)
+        self.dnf('%s %s ptest-runner' % (dnf_options, ' '.join(ptest_pkgs)))
+
+    @skipIfNotFeature('package-management',
+                      'Test requires package-management to be in DISTRO_FEATURES')
+    @skipIfNotFeature('ptest',
+                      'Test requires package-management to be in DISTRO_FEATURES')
+    @skipIfNotDataVar('IMAGE_PKGTYPE', 'rpm',
+                      'RPM is not the primary package manager')
+    @OEHasPackage(['dnf'])
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_ptestrunner(self):
+        self.ptest_log = os.path.join(self.tc.td['TEST_LOG_DIR'],
+                                      'ptest-%s.log' % self.tc.td['DATETIME'])
+        self._install_ptest_packages()
+
+        (runnerstatus, result) = self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0)
+        #exit code is !=0 even if ptest-runner executes because some ptest tests fail.
+        self.assertTrue(runnerstatus != 127, msg="Cannot execute ptest-runner!")
+        self.target.copyFrom('/tmp/ptest.log', self.ptest_log)
+        shutil.copyfile(self.ptest_log, "ptest.log")
+
+        result = self.parse_ptest("ptest.log")
+        log_results_to_location = "./results"
+        if os.path.exists(log_results_to_location):
+            shutil.rmtree(log_results_to_location)
+        os.makedirs(log_results_to_location)
+
+        result.log_as_files(log_results_to_location, test_status = ['pass','fail'])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_qemutiny.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_qemutiny.py
new file mode 100644
index 0000000..7b5b481
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/_qemutiny.py
@@ -0,0 +1,8 @@
+from oeqa.runtime.case import OERuntimeTestCase
+
+class QemuTinyTest(OERuntimeTestCase):
+
+    def test_boot_tiny(self):
+        status, output = self.target.run_serial('uname -a')
+        msg = "Cannot detect poky tiny boot!"
+        self.assertTrue("yocto-tiny" in output, msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildcpio.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildcpio.py
new file mode 100644
index 0000000..59edc9c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildcpio.py
@@ -0,0 +1,30 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+from oeqa.runtime.utils.targetbuildproject import TargetBuildProject
+
+class BuildCpioTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        uri = 'https://ftp.gnu.org/gnu/cpio'
+        uri = '%s/cpio-2.12.tar.bz2' % uri
+        cls.project = TargetBuildProject(cls.tc.target,
+                                         uri,
+                                         dl_dir = cls.tc.td['DL_DIR'])
+        cls.project.download_archive()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.project.clean()
+
+    @OETestID(205)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_cpio(self):
+        self.project.run_configure()
+        self.project.run_make()
+        self.project.run_install()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildgalculator.py
new file mode 100644
index 0000000..7c9d4a3
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildgalculator.py
@@ -0,0 +1,28 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+from oeqa.runtime.utils.targetbuildproject import TargetBuildProject
+
+class GalculatorTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        uri = 'http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2'
+        cls.project = TargetBuildProject(cls.tc.target,
+                                         uri,
+                                         dl_dir = cls.tc.td['DL_DIR'])
+        cls.project.download_archive()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.project.clean()
+
+    @OETestID(1526)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_galculator(self):
+        self.project.run_configure()
+        self.project.run_make()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildlzip.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildlzip.py
new file mode 100644
index 0000000..ca3fead
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/buildlzip.py
@@ -0,0 +1,34 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+from oeqa.runtime.utils.targetbuildproject import TargetBuildProject
+
+class BuildLzipTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        uri = 'http://downloads.yoctoproject.org/mirror/sources'
+        uri = '%s/lzip-1.19.tar.gz' % uri
+        cls.project = TargetBuildProject(cls.tc.target,
+                                         uri,
+                                         dl_dir = cls.tc.td['DL_DIR'])
+        cls.project.download_archive()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.project.clean()
+
+    @OETestID(206)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_lzip(self):
+        self.project.run_configure()
+        self.project.run_make()
+        self.project.run_install()
+
+    @classmethod
+    def tearDownClass(self):
+        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/connman.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/connman.py
new file mode 100644
index 0000000..12456b4
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/connman.py
@@ -0,0 +1,30 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class ConnmanTest(OERuntimeTestCase):
+
+    def service_status(self, service):
+        if 'systemd' in self.tc.td['DISTRO_FEATURES']:
+            (_, output) = self.target.run('systemctl status -l %s' % service)
+            return output
+        else:
+            return "Unable to get status or logs for %s" % service
+
+    @OETestID(961)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(["connman"])
+    def test_connmand_help(self):
+        (status, output) = self.target.run('/usr/sbin/connmand --help')
+        msg = 'Failed to get connman help. Output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(221)
+    @OETestDepends(['connman.ConnmanTest.test_connmand_help'])
+    def test_connmand_running(self):
+        cmd = '%s | grep [c]onnmand' % self.tc.target_cmds['ps']
+        (status, output) = self.target.run(cmd)
+        if status != 0:
+            self.logger.info(self.service_status("connman"))
+            self.fail("No connmand process running")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/date.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/date.py
new file mode 100644
index 0000000..ece7338
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/date.py
@@ -0,0 +1,38 @@
+import re
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+
+class DateTest(OERuntimeTestCase):
+
+    def setUp(self):
+        if self.tc.td.get('VIRTUAL-RUNTIME_init_manager') == 'systemd':
+            self.logger.debug('Stopping systemd-timesyncd daemon')
+            self.target.run('systemctl stop systemd-timesyncd')
+
+    def tearDown(self):
+        if self.tc.td.get('VIRTUAL-RUNTIME_init_manager') == 'systemd':
+            self.logger.debug('Starting systemd-timesyncd daemon')
+            self.target.run('systemctl start systemd-timesyncd')
+
+    @OETestID(211)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_date(self):
+        (status, output) = self.target.run('date +"%Y-%m-%d %T"')
+        msg = 'Failed to get initial date, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+        oldDate = output
+
+        sampleDate = '"2016-08-09 10:00:00"'
+        (status, output) = self.target.run("date -s %s" % sampleDate)
+        self.assertEqual(status, 0, msg='Date set failed, output: %s' % output)
+
+        (status, output) = self.target.run("date -R")
+        p = re.match('Tue, 09 Aug 2016 10:00:.. \+0000', output)
+        msg = 'The date was not set correctly, output: %s' % output
+        self.assertTrue(p, msg=msg)
+
+        (status, output) = self.target.run('date -s "%s"' % oldDate)
+        msg = 'Failed to reset date, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/df.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/df.py
new file mode 100644
index 0000000..aecc32d
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/df.py
@@ -0,0 +1,13 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+
+class DfTest(OERuntimeTestCase):
+
+    @OETestID(234)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_df(self):
+        cmd = "df / | sed -n '2p' | awk '{print $4}'"
+        (status,output) = self.target.run(cmd)
+        msg = 'Not enough space on image. Current size is %s' % output
+        self.assertTrue(int(output)>5120, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/dnf.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/dnf.py
new file mode 100644
index 0000000..2f87296
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/dnf.py
@@ -0,0 +1,123 @@
+import os
+import re
+import subprocess
+from oeqa.utils.httpserver import HTTPService
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotDataVar, skipIfNotFeature
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class DnfTest(OERuntimeTestCase):
+
+    def dnf(self, command, expected = 0):
+        command = 'dnf %s' % command
+        status, output = self.target.run(command, 1500)
+        message = os.linesep.join([command, output])
+        self.assertEqual(status, expected, message)
+        return output
+
+class DnfBasicTest(DnfTest):
+
+    @skipIfNotFeature('package-management',
+                      'Test requires package-management to be in IMAGE_FEATURES')
+    @skipIfNotDataVar('IMAGE_PKGTYPE', 'rpm',
+                      'RPM is not the primary package manager')
+    @OEHasPackage(['dnf'])
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OETestID(1735)
+    def test_dnf_help(self):
+        self.dnf('--help')
+
+    @OETestDepends(['dnf.DnfBasicTest.test_dnf_help'])
+    @OETestID(1739)
+    def test_dnf_version(self):
+        self.dnf('--version')
+
+    @OETestDepends(['dnf.DnfBasicTest.test_dnf_help'])
+    @OETestID(1737)
+    def test_dnf_info(self):
+        self.dnf('info dnf')
+
+    @OETestDepends(['dnf.DnfBasicTest.test_dnf_help'])
+    @OETestID(1738)
+    def test_dnf_search(self):
+        self.dnf('search dnf')
+
+    @OETestDepends(['dnf.DnfBasicTest.test_dnf_help'])
+    @OETestID(1736)
+    def test_dnf_history(self):
+        self.dnf('history')
+
+class DnfRepoTest(DnfTest):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.repo_server = HTTPService(os.path.join(cls.tc.td['WORKDIR'], 'oe-testimage-repo'),
+                                      cls.tc.target.server_ip)
+        cls.repo_server.start()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.repo_server.stop()
+
+    def dnf_with_repo(self, command):
+        pkgarchs = os.listdir(os.path.join(self.tc.td['WORKDIR'], 'oe-testimage-repo'))
+        deploy_url = 'http://%s:%s/' %(self.target.server_ip, self.repo_server.port)
+        cmdlinerepoopts = ["--repofrompath=oe-testimage-repo-%s,%s%s" %(arch, deploy_url, arch) for arch in pkgarchs]
+
+        self.dnf(" ".join(cmdlinerepoopts) + " --nogpgcheck " + command)
+
+    @OETestDepends(['dnf.DnfBasicTest.test_dnf_help'])
+    @OETestID(1744)
+    def test_dnf_makecache(self):
+        self.dnf_with_repo('makecache')
+
+
+# Does not work when repo is specified on the command line
+#    @OETestDepends(['dnf.DnfRepoTest.test_dnf_makecache'])
+#    def test_dnf_repolist(self):
+#        self.dnf_with_repo('repolist')
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_makecache'])
+    @OETestID(1746)
+    def test_dnf_repoinfo(self):
+        self.dnf_with_repo('repoinfo')
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_makecache'])
+    @OETestID(1740)
+    def test_dnf_install(self):
+        self.dnf_with_repo('install -y run-postinsts-dev')
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_install'])
+    @OETestID(1741)
+    def test_dnf_install_dependency(self):
+        self.dnf_with_repo('remove -y run-postinsts')
+        self.dnf_with_repo('install -y run-postinsts-dev')
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_install_dependency'])
+    @OETestID(1742)
+    def test_dnf_install_from_disk(self):
+        self.dnf_with_repo('remove -y run-postinsts-dev')
+        self.dnf_with_repo('install -y --downloadonly run-postinsts-dev')
+        status, output = self.target.run('find /var/cache/dnf -name run-postinsts-dev*rpm', 1500)
+        self.assertEqual(status, 0, output)
+        self.dnf_with_repo('install -y %s' % output)
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_install_from_disk'])
+    @OETestID(1743)
+    def test_dnf_install_from_http(self):
+        output = subprocess.check_output('%s %s -name run-postinsts-dev*' % (bb.utils.which(os.getenv('PATH'), "find"),
+                                                                           os.path.join(self.tc.td['WORKDIR'], 'oe-testimage-repo')), shell=True).decode("utf-8")
+        rpm_path = output.split("/")[-2] + "/" + output.split("/")[-1]
+        url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, rpm_path)
+        self.dnf_with_repo('remove -y run-postinsts-dev')
+        self.dnf_with_repo('install -y %s' % url)
+
+    @OETestDepends(['dnf.DnfRepoTest.test_dnf_install'])
+    @OETestID(1745)
+    def test_dnf_reinstall(self):
+        self.dnf_with_repo('reinstall -y run-postinsts-dev')
+
+
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/gcc.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/gcc.py
new file mode 100644
index 0000000..9110831
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/gcc.py
@@ -0,0 +1,73 @@
+import os
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class GccCompileTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        dst = '/tmp/'
+        src = os.path.join(cls.tc.files_dir, 'test.c')
+        cls.tc.target.copyTo(src, dst)
+
+        src = os.path.join(cls.tc.runtime_files_dir, 'testmakefile')
+        cls.tc.target.copyTo(src, dst)
+
+        src = os.path.join(cls.tc.files_dir, 'test.cpp')
+        cls.tc.target.copyTo(src, dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        files = '/tmp/test.c /tmp/test.o /tmp/test /tmp/testmakefile'
+        cls.tc.target.run('rm %s' % files)
+
+    @OETestID(203)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_gcc_compile(self):
+        status, output = self.target.run('gcc /tmp/test.c -o /tmp/test -lm')
+        msg = 'gcc compile failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        status, output = self.target.run('/tmp/test')
+        msg = 'running compiled file failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(200)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_gpp_compile(self):
+        status, output = self.target.run('g++ /tmp/test.c -o /tmp/test -lm')
+        msg = 'g++ compile failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        status, output = self.target.run('/tmp/test')
+        msg = 'running compiled file failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(1142)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_gpp2_compile(self):
+        status, output = self.target.run('g++ /tmp/test.cpp -o /tmp/test -lm')
+        msg = 'g++ compile failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        status, output = self.target.run('/tmp/test')
+        msg = 'running compiled file failed, output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(204)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_make(self):
+        status, output = self.target.run('cd /tmp; make -f testmakefile')
+        msg = 'running make failed, output %s' % output
+        self.assertEqual(status, 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/kernelmodule.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/kernelmodule.py
new file mode 100644
index 0000000..11ad7b7
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/kernelmodule.py
@@ -0,0 +1,40 @@
+import os
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class KernelModuleTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        src = os.path.join(cls.tc.runtime_files_dir, 'hellomod.c')
+        dst = '/tmp/hellomod.c'
+        cls.tc.target.copyTo(src, dst)
+
+        src = os.path.join(cls.tc.runtime_files_dir, 'hellomod_makefile')
+        dst = '/tmp/Makefile'
+        cls.tc.target.copyTo(src, dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        files = '/tmp/Makefile /tmp/hellomod.c'
+        cls.tc.target.run('rm %s' % files)
+
+    @OETestID(1541)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['gcc.GccCompileTest.test_gcc_compile'])
+    def test_kernel_module(self):
+        cmds = [
+            'cd /usr/src/kernel && make scripts',
+            'cd /tmp && make',
+            'cd /tmp && insmod hellomod.ko',
+            'lsmod | grep hellomod',
+            'dmesg | grep Hello',
+            'rmmod hellomod', 'dmesg | grep "Cleaning up hellomod"'
+            ]
+        for cmd in cmds:
+            status, output = self.target.run(cmd, 900)
+            self.assertEqual(status, 0, msg='\n'.join([cmd, output]))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ldd.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ldd.py
new file mode 100644
index 0000000..c6d92fd
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ldd.py
@@ -0,0 +1,25 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class LddTest(OERuntimeTestCase):
+
+    @OETestID(962)
+    @skipIfNotFeature('tools-sdk',
+                      'Test requires tools-sdk to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_ldd_exists(self):
+        status, output = self.target.run('which ldd')
+        msg = 'ldd does not exist in PATH: which ldd: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(239)
+    @OETestDepends(['ldd.LddTest.test_ldd_exists'])
+    def test_ldd_rtldlist_check(self):
+        cmd = ('for i in $(which ldd | xargs cat | grep "^RTLDLIST"| '
+              'cut -d\'=\' -f2|tr -d \'"\'); '
+              'do test -f $i && echo $i && break; done')
+        status, output = self.target.run(cmd)
+        msg = "ldd path not correct or RTLDLIST files don't exist."
+        self.assertEqual(status, 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/logrotate.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/logrotate.py
new file mode 100644
index 0000000..992fef2
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/logrotate.py
@@ -0,0 +1,42 @@
+# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=289 testcase
+# Note that the image under test must have logrotate installed
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class LogrotateTest(OERuntimeTestCase):
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.tc.target.run('rm -rf $HOME/logrotate_dir')
+
+    @OETestID(1544)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(['logrotate'])
+    def test_1_logrotate_setup(self):
+        status, output = self.target.run('mkdir $HOME/logrotate_dir')
+        msg = 'Could not create logrotate_dir. Output: %s' % output
+        self.assertEqual(status, 0, msg = msg)
+
+        cmd = ('sed -i "s#wtmp {#wtmp {\\n    olddir $HOME/logrotate_dir#"'
+               ' /etc/logrotate.conf')
+        status, output = self.target.run(cmd)
+        msg = ('Could not write to logrotate.conf file. Status and output: '
+               ' %s and %s' % (status, output))
+        self.assertEqual(status, 0, msg = msg)
+
+    @OETestID(1542)
+    @OETestDepends(['logrotate.LogrotateTest.test_1_logrotate_setup'])
+    def test_2_logrotate(self):
+        status, output = self.target.run('logrotate -f /etc/logrotate.conf')
+        msg = ('logrotate service could not be reloaded. Status and output: '
+                '%s and %s' % (status, output))
+        self.assertEqual(status, 0, msg = msg)
+
+        _, output = self.target.run('ls -la $HOME/logrotate_dir/ | wc -l')
+        msg = ('new logfile could not be created. List of files within log '
+               'directory: %s' % (
+                self.target.run('ls -la $HOME/logrotate_dir')[1]))
+        self.assertTrue(int(output)>=3, msg = msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/multilib.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/multilib.py
new file mode 100644
index 0000000..8c167f1
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/multilib.py
@@ -0,0 +1,41 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotInDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class MultilibTest(OERuntimeTestCase):
+
+    def archtest(self, binary, arch):
+        """
+        Check that ``binary`` has the ELF class ``arch`` (e.g. ELF32/ELF64).
+        """
+
+        status, output = self.target.run('readelf -h %s' % binary)
+        self.assertEqual(status, 0, 'Failed to readelf %s' % binary)
+
+        l = [l.split()[1] for l in output.split('\n') if "Class:" in l]
+        if l:
+            theclass = l[0]
+        else:
+            self.fail('Cannot parse readelf. Output:\n%s' % output)
+
+        msg = "%s isn't %s (is %s)" % (binary, arch, theclass)
+        self.assertEqual(theclass, arch, msg=msg)
+
+    @OETestID(1593)
+    @skipIfNotInDataVar('MULTILIBS', 'multilib:lib32',
+                        "This isn't a multilib:lib32 image")
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_check_multilib_libc(self):
+        """
+        Check that a multilib image has both 32-bit and 64-bit libc in.
+        """
+        self.archtest("/lib/libc.so.6", "ELF32")
+        self.archtest("/lib64/libc.so.6", "ELF64")
+
+    @OETestID(279)
+    @OETestDepends(['multilib.MultilibTest.test_check_multilib_libc'])
+    @OEHasPackage(['lib32-connman'])
+    def test_file_connman(self):
+        self.archtest("/usr/sbin/connmand", "ELF32")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/oe_syslog.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/oe_syslog.py
new file mode 100644
index 0000000..005b697
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/oe_syslog.py
@@ -0,0 +1,66 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class SyslogTest(OERuntimeTestCase):
+
+    @OETestID(201)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(["busybox-syslog", "sysklogd"])
+    def test_syslog_running(self):
+        cmd = '%s  | grep -i [s]yslogd' % self.tc.target_cmds['ps']
+        status, output = self.target.run(cmd)
+        msg = "No syslogd process; ps output: %s" % output
+        self.assertEqual(status, 0, msg=msg)
+
+class SyslogTestConfig(OERuntimeTestCase):
+
+    @OETestID(1149)
+    @OETestDepends(['oe_syslog.SyslogTest.test_syslog_running'])
+    def test_syslog_logger(self):
+        status, output = self.target.run('logger foobar')
+        msg = "Can't log into syslog. Output: %s " % output
+        self.assertEqual(status, 0, msg=msg)
+
+        status, output = self.target.run('grep foobar /var/log/messages')
+        if status != 0:
+            if self.tc.td.get("VIRTUAL-RUNTIME_init_manager") == "systemd":
+                status, output = self.target.run('journalctl -o cat | grep foobar')
+            else:
+                status, output = self.target.run('logread | grep foobar')
+        msg = ('Test log string not found in /var/log/messages or logread.'
+               ' Output: %s ' % output)
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(1150)
+    @OETestDepends(['oe_syslog.SyslogTest.test_syslog_running'])
+    def test_syslog_restart(self):
+        if "systemd" != self.tc.td.get("VIRTUAL-RUNTIME_init_manager", ""):
+            (_, _) = self.target.run('/etc/init.d/syslog restart')
+        else:
+            (_, _) = self.target.run('systemctl restart syslog.service')
+
+
+    @OETestID(202)
+    @OETestDepends(['oe_syslog.SyslogTestConfig.test_syslog_logger'])
+    @OEHasPackage(["!sysklogd", "busybox"])
+    @skipIfDataVar('VIRTUAL-RUNTIME_init_manager', 'systemd',
+                   'Not appropiate for systemd image')
+    def test_syslog_startup_config(self):
+        cmd = 'echo "LOGFILE=/var/log/test" >> /etc/syslog-startup.conf'
+        self.target.run(cmd)
+        status, output = self.target.run('/etc/init.d/syslog restart')
+        msg = ('Could not restart syslog service. Status and output:'
+               ' %s and %s' % (status,output))
+        self.assertEqual(status, 0, msg)
+
+        cmd = 'logger foobar && grep foobar /var/log/test'
+        status,output = self.target.run(cmd)
+        msg = 'Test log string not found. Output: %s ' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        cmd = "sed -i 's#LOGFILE=/var/log/test##' /etc/syslog-startup.conf"
+        self.target.run(cmd)
+        self.target.run('/etc/init.d/syslog restart')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/pam.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/pam.py
new file mode 100644
index 0000000..3654cdc
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/pam.py
@@ -0,0 +1,33 @@
+# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=287 testcase
+# Note that the image under test must have "pam" in DISTRO_FEATURES
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class PamBasicTest(OERuntimeTestCase):
+
+    @OETestID(1543)
+    @skipIfNotFeature('pam', 'Test requires pam to be in DISTRO_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_pam(self):
+        status, output = self.target.run('login --help')
+        msg = ('login command does not work as expected. '
+               'Status and output:%s and %s' % (status, output))
+        self.assertEqual(status, 1, msg = msg)
+
+        status, output = self.target.run('passwd --help')
+        msg = ('passwd command does not work as expected. '
+               'Status and output:%s and %s' % (status, output))
+        self.assertEqual(status, 0, msg = msg)
+
+        status, output = self.target.run('su --help')
+        msg = ('su command does not work as expected. '
+               'Status and output:%s and %s' % (status, output))
+        self.assertEqual(status, 0, msg = msg)
+
+        status, output = self.target.run('useradd --help')
+        msg = ('useradd command does not work as expected. '
+               'Status and output:%s and %s' % (status, output))
+        self.assertEqual(status, 0, msg = msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/parselogs.py
similarity index 60%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py
rename to import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/parselogs.py
index aa5008b..6e92946 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/parselogs.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/parselogs.py
@@ -1,8 +1,12 @@
 import os
-import unittest
-import subprocess
-from oeqa.oetest import oeRuntimeTest
-from oeqa.utils.decorators import *
+
+from subprocess import check_output
+from shutil import rmtree
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
 
 #in the future these lists could be moved outside of module
 errors = ["error", "cannot", "can\'t", "failed"]
@@ -44,6 +48,7 @@
     "stmmac_dvr_probe: warning: cannot get CSR clock",
     "error: couldn\'t mount because of unsupported optional features",
     "GPT: Use GNU Parted to correct GPT errors",
+    "Cannot set xattr user.Librepo.DownloadInProgress",
     ]
 
 video_related = [
@@ -134,7 +139,19 @@
         'dmi: Firmware registration failed.',
         'ioremap error for 0x78',
         ] + x86_common,
-    'intel-corei7-64' : x86_common,
+    'intel-corei7-64' : [
+        'can\'t set Max Payload Size to 256',
+        'intel_punit_ipc: can\'t request region for resource',
+        '[drm] parse error at position 4 in video mode \'efifb\'',
+        'ACPI Error: Could not enable RealTimeClock event',
+        'ACPI Warning: Could not enable fixed event - RealTimeClock',
+        'hci_intel INT33E1:00: Unable to retrieve gpio',
+        'hci_intel: probe of INT33E1:00 failed',
+        'can\'t derive routing for PCI INT A',
+        'failed to read out thermal zone',
+        'Bluetooth: hci0: Setting Intel event mask failed',
+        'ttyS2 - failed to request DMA',
+        ] + x86_common,
     'crownbay' : x86_common,
     'genericx86' : x86_common,
     'genericx86-64' : [
@@ -156,144 +173,173 @@
 
 log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"]
 
-class ParseLogsTest(oeRuntimeTest):
+class ParseLogsTest(OERuntimeTestCase):
 
     @classmethod
-    def setUpClass(self):
-        self.errors = errors
+    def setUpClass(cls):
+        cls.errors = errors
 
         # When systemd is enabled we need to notice errors on
         # circular dependencies in units.
-        if self.hasFeature("systemd"):
-            self.errors.extend([
+        if 'systemd' in cls.td.get('DISTRO_FEATURES', ''):
+            cls.errors.extend([
                 'Found ordering cycle on',
                 'Breaking ordering cycle by deleting job',
                 'deleted to break ordering cycle',
                 'Ordering cycle found, skipping',
                 ])
 
-        self.ignore_errors = ignore_errors
-        self.log_locations = log_locations
-        self.msg = ""
-        (is_lsb, location) = oeRuntimeTest.tc.target.run("which LSB_Test.sh")
+        cls.ignore_errors = ignore_errors
+        cls.log_locations = log_locations
+        cls.msg = ''
+        is_lsb, _ = cls.tc.target.run("which LSB_Test.sh")
         if is_lsb == 0:
-            for machine in self.ignore_errors:
-                self.ignore_errors[machine] = self.ignore_errors[machine] + video_related
+            for machine in cls.ignore_errors:
+                cls.ignore_errors[machine] = cls.ignore_errors[machine] \
+                                             + video_related
 
     def getMachine(self):
-        return oeRuntimeTest.tc.d.getVar("MACHINE", True)
+        return self.td.get('MACHINE', '')
 
     def getWorkdir(self):
-        return oeRuntimeTest.tc.d.getVar("WORKDIR", True)
+        return self.td.get('WORKDIR', '')
 
-    #get some information on the CPU of the machine to display at the beginning of the output. This info might be useful in some cases.
+    # Get some information on the CPU of the machine to display at the
+    # beginning of the output. This info might be useful in some cases.
     def getHardwareInfo(self):
         hwi = ""
-        (status, cpu_name) = self.target.run("cat /proc/cpuinfo | grep \"model name\" | head -n1 | awk 'BEGIN{FS=\":\"}{print $2}'")
-        (status, cpu_physical_cores) = self.target.run("cat /proc/cpuinfo | grep \"cpu cores\" | head -n1 | awk {'print $4'}")
-        (status, cpu_logical_cores) = self.target.run("cat /proc/cpuinfo | grep \"processor\" | wc -l")
-        (status, cpu_arch) = self.target.run("uname -m")
-        hwi += "Machine information: \n"
-        hwi += "*******************************\n"
-        hwi += "Machine name: "+self.getMachine()+"\n"
-        hwi += "CPU: "+str(cpu_name)+"\n"
-        hwi += "Arch: "+str(cpu_arch)+"\n"
-        hwi += "Physical cores: "+str(cpu_physical_cores)+"\n"
-        hwi += "Logical cores: "+str(cpu_logical_cores)+"\n"
-        hwi += "*******************************\n"
+        cmd = ('cat /proc/cpuinfo | grep "model name" | head -n1 | '
+               " awk 'BEGIN{FS=\":\"}{print $2}'")
+        _, cpu_name = self.target.run(cmd)
+
+        cmd = ('cat /proc/cpuinfo | grep "cpu cores" | head -n1 | '
+               "awk {'print $4'}")
+        _, cpu_physical_cores = self.target.run(cmd)
+
+        cmd = 'cat /proc/cpuinfo | grep "processor" | wc -l'
+        _, cpu_logical_cores = self.target.run(cmd)
+
+        _, cpu_arch = self.target.run('uname -m')
+
+        hwi += 'Machine information: \n'
+        hwi += '*******************************\n'
+        hwi += 'Machine name: ' + self.getMachine() + '\n'
+        hwi += 'CPU: ' + str(cpu_name) + '\n'
+        hwi += 'Arch: ' + str(cpu_arch)+ '\n'
+        hwi += 'Physical cores: ' + str(cpu_physical_cores) + '\n'
+        hwi += 'Logical cores: ' + str(cpu_logical_cores) + '\n'
+        hwi += '*******************************\n'
+
         return hwi
 
-    #go through the log locations provided and if it's a folder create a list with all the .log files in it, if it's a file just add
-    #it to that list
+    # Go through the log locations provided and if it's a folder
+    # create a list with all the .log files in it, if it's a file
+    # just add it to that list.
     def getLogList(self, log_locations):
         logs = []
         for location in log_locations:
-            (status, output) = self.target.run("test -f "+str(location))
-            if (status == 0):
+            status, _ = self.target.run('test -f ' + str(location))
+            if status == 0:
                 logs.append(str(location))
             else:
-                (status, output) = self.target.run("test -d "+str(location))
-                if (status == 0):
-                    (status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f")
-                    if (status == 0):
+                status, _ = self.target.run('test -d ' + str(location))
+                if status == 0:
+                    cmd = 'find ' + str(location) + '/*.log -maxdepth 1 -type f'
+                    status, output = self.target.run(cmd)
+                    if status == 0:
                         output = output.splitlines()
                         for logfile in output:
-                            logs.append(os.path.join(location,str(logfile)))
+                            logs.append(os.path.join(location, str(logfile)))
         return logs
 
-    #copy the log files to be parsed locally
+    # Copy the log files to be parsed locally
     def transfer_logs(self, log_list):
         workdir = self.getWorkdir()
         self.target_logs = workdir + '/' + 'target_logs'
         target_logs = self.target_logs
-        if not os.path.exists(target_logs):
-            os.makedirs(target_logs)
-        bb.utils.remove(self.target_logs + "/*")
+        if os.path.exists(target_logs):
+            rmtree(self.target_logs)
+        os.makedirs(target_logs)
         for f in log_list:
-            self.target.copy_from(f, target_logs)
+            self.target.copyFrom(str(f), target_logs)
 
-    #get the local list of logs
+    # Get the local list of logs
     def get_local_log_list(self, log_locations):
         self.transfer_logs(self.getLogList(log_locations))
-        logs = [ os.path.join(self.target_logs, f) for f in os.listdir(self.target_logs) if os.path.isfile(os.path.join(self.target_logs, f)) ]
+        list_dir = os.listdir(self.target_logs)
+        dir_files = [os.path.join(self.target_logs, f) for f in list_dir]
+        logs = [f for f in dir_files if os.path.isfile(f)]
         return logs
 
-    #build the grep command to be used with filters and exclusions
+    # Build the grep command to be used with filters and exclusions
     def build_grepcmd(self, errors, ignore_errors, log):
-        grepcmd = "grep "
-        grepcmd +="-Ei \""
+        grepcmd = 'grep '
+        grepcmd += '-Ei "'
         for error in errors:
-            grepcmd += error+"|"
+            grepcmd += error + '|'
         grepcmd = grepcmd[:-1]
-        grepcmd += "\" "+str(log)+" | grep -Eiv \'"
+        grepcmd += '" ' + str(log) + " | grep -Eiv \'"
+
         try:
             errorlist = ignore_errors[self.getMachine()]
         except KeyError:
-            self.msg += "No ignore list found for this machine, using default\n"
+            self.msg += 'No ignore list found for this machine, using default\n'
             errorlist = ignore_errors['default']
+
         for ignore_error in errorlist:
-            ignore_error = ignore_error.replace("(", "\(")
-            ignore_error = ignore_error.replace(")", "\)")
-            ignore_error = ignore_error.replace("'", ".")
-            ignore_error = ignore_error.replace("?", "\?")
-            ignore_error = ignore_error.replace("[", "\[")
-            ignore_error = ignore_error.replace("]", "\]")
-            ignore_error = ignore_error.replace("*", "\*")
-            ignore_error = ignore_error.replace("0-9", "[0-9]")
-            grepcmd += ignore_error+"|"
+            ignore_error = ignore_error.replace('(', '\(')
+            ignore_error = ignore_error.replace(')', '\)')
+            ignore_error = ignore_error.replace("'", '.')
+            ignore_error = ignore_error.replace('?', '\?')
+            ignore_error = ignore_error.replace('[', '\[')
+            ignore_error = ignore_error.replace(']', '\]')
+            ignore_error = ignore_error.replace('*', '\*')
+            ignore_error = ignore_error.replace('0-9', '[0-9]')
+            grepcmd += ignore_error + '|'
         grepcmd = grepcmd[:-1]
         grepcmd += "\'"
+
         return grepcmd
 
-    #grep only the errors so that their context could be collected. Default context is 10 lines before and after the error itself
-    def parse_logs(self, errors, ignore_errors, logs, lines_before = 10, lines_after = 10):
+    # Grep only the errors so that their context could be collected.
+    # Default context is 10 lines before and after the error itself
+    def parse_logs(self, errors, ignore_errors, logs,
+                   lines_before = 10, lines_after = 10):
         results = {}
         rez = []
         grep_output = ''
+
         for log in logs:
             result = None
             thegrep = self.build_grepcmd(errors, ignore_errors, log)
+
             try:
-                result = subprocess.check_output(thegrep, shell=True).decode("utf-8")
+                result = check_output(thegrep, shell=True).decode('utf-8')
             except:
                 pass
-            if (result is not None):
+
+            if result is not None:
                 results[log.replace('target_logs/','')] = {}
                 rez = result.splitlines()
+
                 for xrez in rez:
                     try:
-                        grep_output = subprocess.check_output(['grep', '-F', xrez, '-B', str(lines_before), '-A', str(lines_after), log]).decode("utf-8")
+                        cmd = ['grep', '-F', xrez, '-B', str(lines_before)]
+                        cmd += ['-A', str(lines_after), log]
+                        grep_output = check_output(cmd).decode('utf-8')
                     except:
                         pass
                     results[log.replace('target_logs/','')][xrez]=grep_output
+
         return results
 
-    #get the output of dmesg and write it in a file. This file is added to log_locations.
+    # Get the output of dmesg and write it in a file.
+    # This file is added to log_locations.
     def write_dmesg(self):
-        (status, dmesg) = self.target.run("dmesg > /tmp/dmesg_output.log")
+        (status, dmesg) = self.target.run('dmesg > /tmp/dmesg_output.log')
 
-    @testcase(1059)
-    @skipUnlessPassed('test_ssh')
+    @OETestID(1059)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
     def test_parselogs(self):
         self.write_dmesg()
         log_list = self.get_local_log_list(self.log_locations)
@@ -301,13 +347,13 @@
         print(self.getHardwareInfo())
         errcount = 0
         for log in result:
-            self.msg += "Log: "+log+"\n"
-            self.msg += "-----------------------\n"
+            self.msg += 'Log: ' + log + '\n'
+            self.msg += '-----------------------\n'
             for error in result[log]:
                 errcount += 1
-                self.msg += "Central error: "+str(error)+"\n"
-                self.msg +=  "***********************\n"
-                self.msg +=  result[str(log)][str(error)]+"\n"
-                self.msg +=  "***********************\n"
-        self.msg += "%s errors found in logs." % errcount
+                self.msg += 'Central error: ' + str(error) + '\n'
+                self.msg +=  '***********************\n'
+                self.msg +=  result[str(log)][str(error)] + '\n'
+                self.msg +=  '***********************\n'
+        self.msg += '%s errors found in logs.' % errcount
         self.assertEqual(errcount, 0, msg=self.msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/perl.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/perl.py
new file mode 100644
index 0000000..d0b7e8e
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/perl.py
@@ -0,0 +1,37 @@
+import os
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class PerlTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        src = os.path.join(cls.tc.files_dir, 'test.pl')
+        dst = '/tmp/test.pl'
+        cls.tc.target.copyTo(src, dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        dst = '/tmp/test.pl'
+        cls.tc.target.run('rm %s' % dst)
+
+    @OETestID(1141)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(['perl'])
+    def test_perl_exists(self):
+        status, output = self.target.run('which perl')
+        msg = 'Perl binary not in PATH or not on target.'
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(208)
+    @OETestDepends(['perl.PerlTest.test_perl_exists'])
+    def test_perl_works(self):
+        status, output = self.target.run('perl /tmp/test.pl')
+        msg = 'Exit status was not 0. Output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        msg = 'Incorrect output: %s' % output
+        self.assertEqual(output, "the value of a is 0.01", msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ping.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ping.py
new file mode 100644
index 0000000..02f580a
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ping.py
@@ -0,0 +1,24 @@
+from subprocess import Popen, PIPE
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.oetimeout import OETimeout
+
+class PingTest(OERuntimeTestCase):
+
+    @OETimeout(30)
+    @OETestID(964)
+    def test_ping(self):
+        output = ''
+        count = 0
+        while count < 5:
+            cmd = 'ping -c 1 %s' % self.target.ip
+            proc = Popen(cmd, shell=True, stdout=PIPE)
+            output += proc.communicate()[0].decode('utf-8')
+            if proc.poll() == 0:
+                count += 1
+            else:
+                count = 0
+        msg = ('Expected 5 consecutive, got %d.\n'
+               'ping output is:\n%s' % (count,output))
+        self.assertEqual(count, 5, msg = msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/python.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/python.py
new file mode 100644
index 0000000..bf3e179
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/python.py
@@ -0,0 +1,43 @@
+import os
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class PythonTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        src = os.path.join(cls.tc.files_dir, 'test.py')
+        dst = '/tmp/test.py'
+        cls.tc.target.copyTo(src, dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        dst = '/tmp/test.py'
+        cls.tc.target.run('rm %s' % dst)
+
+    @OETestID(1145)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(['python-core'])
+    def test_python_exists(self):
+        status, output = self.target.run('which python')
+        msg = 'Python binary not in PATH or not on target.'
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(965)
+    @OETestDepends(['python.PythonTest.test_python_exists'])
+    def test_python_stdout(self):
+        status, output = self.target.run('python /tmp/test.py')
+        msg = 'Exit status was not 0. Output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        msg = 'Incorrect output: %s' % output
+        self.assertEqual(output, "the value of a is 0.01", msg=msg)
+
+    @OETestID(1146)
+    @OETestDepends(['python.PythonTest.test_python_stdout'])
+    def test_python_testfile(self):
+        status, output = self.target.run('ls /tmp/testfile.python')
+        self.assertEqual(status, 0, msg='Python test file generate failed.')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/rpm.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/rpm.py
new file mode 100644
index 0000000..05b94c7
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/rpm.py
@@ -0,0 +1,142 @@
+import os
+import fnmatch
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
+from oeqa.core.utils.path import findFile
+
+class RpmBasicTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        if cls.tc.td['PACKAGE_CLASSES'].split()[0] != 'package_rpm':
+            cls.skipTest('Tests require image to be build from rpm')
+
+    @OETestID(960)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_rpm_help(self):
+        status, output = self.target.run('rpm --help')
+        msg = 'status and output: %s and %s' % (status, output)
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(191)
+    @OETestDepends(['rpm.RpmBasicTest.test_rpm_help'])
+    def test_rpm_query(self):
+        status, output = self.target.run('rpm -q rpm')
+        msg = 'status and output: %s and %s' % (status, output)
+        self.assertEqual(status, 0, msg=msg)
+
+class RpmInstallRemoveTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        if cls.tc.td['PACKAGE_CLASSES'].split()[0] != 'package_rpm':
+            cls.skipTest('Tests require image to be build from rpm')
+
+        pkgarch = cls.td['TUNE_PKGARCH'].replace('-', '_')
+        rpmdir = os.path.join(cls.tc.td['DEPLOY_DIR'], 'rpm', pkgarch)
+        # Pick rpm-doc as a test file to get installed, because it's small
+        # and it will always be built for standard targets
+        rpm_doc = 'rpm-doc-*.%s.rpm' % pkgarch
+        for f in fnmatch.filter(os.listdir(rpmdir), rpm_doc):
+            test_file = os.path.join(rpmdir, f)
+        dst = '/tmp/rpm-doc.rpm'
+        cls.tc.target.copyTo(test_file, dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        dst = '/tmp/rpm-doc.rpm'
+        cls.tc.target.run('rm -f %s' % dst)
+
+    @OETestID(192)
+    @OETestDepends(['rpm.RpmBasicTest.test_rpm_help'])
+    def test_rpm_install(self):
+        status, output = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
+        msg = 'Failed to install rpm-doc package: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(194)
+    @OETestDepends(['rpm.RpmInstallRemoveTest.test_rpm_install'])
+    def test_rpm_remove(self):
+        status,output = self.target.run('rpm -e rpm-doc')
+        msg = 'Failed to remove rpm-doc package: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(1096)
+    @OETestDepends(['rpm.RpmBasicTest.test_rpm_query'])
+    def test_rpm_query_nonroot(self):
+
+        def set_up_test_user(u):
+            status, output = self.target.run('id -u %s' % u)
+            if status:
+                status, output = self.target.run('useradd %s' % u)
+                msg = 'Failed to create new user: %s' % output
+                self.assertTrue(status == 0, msg=msg)
+
+        def exec_as_test_user(u):
+            status, output = self.target.run('su -c id %s' % u)
+            msg = 'Failed to execute as new user'
+            self.assertTrue("({0})".format(u) in output, msg=msg)
+
+            status, output = self.target.run('su -c "rpm -qa" %s ' % u)
+            msg = 'status: %s. Cannot run rpm -qa: %s' % (status, output)
+            self.assertEqual(status, 0, msg=msg)
+
+        def unset_up_test_user(u):
+            status, output = self.target.run('userdel -r %s' % u)
+            msg = 'Failed to erase user: %s' % output
+            self.assertTrue(status == 0, msg=msg)
+
+        tuser = 'test1'
+
+        try:
+            set_up_test_user(tuser)
+            exec_as_test_user(tuser)
+        finally:
+            unset_up_test_user(tuser)
+
+    @OETestID(195)
+    @OETestDepends(['rpm.RpmInstallRemoveTest.test_rpm_remove'])
+    def test_check_rpm_install_removal_log_file_size(self):
+        """
+        Summary:     Check that rpm writes into /var/log/messages
+        Expected:    There should be some RPM prefixed entries in the above file.
+        Product:     BSPs
+        Author:      Alexandru Georgescu <alexandru.c.georgescu@intel.com>
+        Author:      Alexander Kanavin <alexander.kanavin@intel.com>
+        AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+        """
+        db_files_cmd = 'ls /var/lib/rpm/__db.*'
+        check_log_cmd = "grep RPM /var/log/messages | wc -l"
+
+        # Make sure that some database files are under /var/lib/rpm as '__db.xxx'
+        status, output = self.target.run(db_files_cmd)
+        msg =  'Failed to find database files under /var/lib/rpm/ as __db.xxx'
+        self.assertEqual(0, status, msg=msg)
+
+        # Remove the package just in case
+        self.target.run('rpm -e rpm-doc')
+
+        # Install/Remove a package 10 times
+        for i in range(10):
+            status, output = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
+            msg = 'Failed to install rpm-doc package. Reason: {}'.format(output)
+            self.assertEqual(0, status, msg=msg)
+
+            status, output = self.target.run('rpm -e rpm-doc')
+            msg = 'Failed to remove rpm-doc package. Reason: {}'.format(output)
+            self.assertEqual(0, status, msg=msg)
+
+        # if using systemd this should ensure all entries are flushed to /var
+        status, output = self.target.run("journalctl --sync")
+        # Get the amount of entries in the log file
+        status, output = self.target.run(check_log_cmd)
+        msg = 'Failed to get the final size of the log file.'
+        self.assertEqual(0, status, msg=msg)
+
+        # Check that there's enough of them
+        self.assertGreaterEqual(int(output), 80,
+                                   'Cound not find sufficient amount of rpm entries in /var/log/messages, found {} entries'.format(output))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scanelf.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scanelf.py
new file mode 100644
index 0000000..3ba1f78
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scanelf.py
@@ -0,0 +1,26 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class ScanelfTest(OERuntimeTestCase):
+    scancmd = 'scanelf --quiet --recursive --mount --ldpath --path'
+
+    @OETestID(966)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(['pax-utils'])
+    def test_scanelf_textrel(self):
+        # print TEXTREL information
+        cmd = '%s --textrel' % self.scancmd
+        status, output = self.target.run(cmd)
+        msg = '\n'.join([cmd, output])
+        self.assertEqual(output.strip(), '', msg=msg)
+
+    @OETestID(967)
+    @OETestDepends(['scanelf.ScanelfTest.test_scanelf_textrel'])
+    def test_scanelf_rpath(self):
+        # print RPATH information
+        cmd = '%s --textrel --rpath' % self.scancmd
+        status, output = self.target.run(cmd)
+        msg = '\n'.join([cmd, output])
+        self.assertEqual(output.strip(), '', msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scp.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scp.py
new file mode 100644
index 0000000..f488a61
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/scp.py
@@ -0,0 +1,33 @@
+import os
+from tempfile import mkstemp
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+
+class ScpTest(OERuntimeTestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.tmp_fd, cls.tmp_path = mkstemp()
+        with os.fdopen(cls.tmp_fd, 'w') as f:
+            f.seek(2 ** 22 -1)
+            f.write(os.linesep)
+
+    @classmethod
+    def tearDownClass(cls):
+        os.remove(cls.tmp_path)
+
+    @OETestID(220)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_scp_file(self):
+        dst = '/tmp/test_scp_file'
+
+        (status, output) = self.target.copyTo(self.tmp_path, dst)
+        msg = 'File could not be copied. Output: %s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        (status, output) = self.target.run('ls -la %s' % dst)
+        self.assertEqual(status, 0, msg = 'SCP test failed')
+
+        self.target.run('rm %s' % dst)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/skeletoninit.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/skeletoninit.py
new file mode 100644
index 0000000..4fdcf03
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/skeletoninit.py
@@ -0,0 +1,33 @@
+# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=284
+# testcase. Image under test must have meta-skeleton layer in bblayers and
+# IMAGE_INSTALL_append = " service" in local.conf
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
+
+class SkeletonBasicTest(OERuntimeTestCase):
+
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    @OEHasPackage(['service'])
+    @skipIfDataVar('VIRTUAL-RUNTIME_init_manager', 'systemd',
+                   'Not appropiate for systemd image')
+    def test_skeleton_availability(self):
+        status, output = self.target.run('ls /etc/init.d/skeleton')
+        msg = 'skeleton init script not found. Output:\n%s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+        status, output =  self.target.run('ls /usr/sbin/skeleton-test')
+        msg = 'skeleton-test not found. Output:\n%s' % output
+        self.assertEqual(status, 0, msg=msg)
+
+    @OETestID(284)
+    @OETestDepends(['skeletoninit.SkeletonBasicTest.test_skeleton_availability'])
+    def test_skeleton_script(self):
+        output1 = self.target.run("/etc/init.d/skeleton start")[1]
+        cmd = '%s | grep [s]keleton-test' % self.tc.target_cmds['ps']
+        status, output2 = self.target.run(cmd)
+        msg = ('Skeleton script could not be started:'
+               '\n%s\n%s' % (output1, output2))
+        self.assertEqual(status, 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ssh.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ssh.py
new file mode 100644
index 0000000..eca1679
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/ssh.py
@@ -0,0 +1,15 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+
+class SSHTest(OERuntimeTestCase):
+
+    @OETestID(224)
+    @OETestDepends(['ping.PingTest.test_ping'])
+    def test_ssh(self):
+        (status, output) = self.target.run('uname -a')
+        self.assertEqual(status, 0, msg='SSH Test failed: %s' % output)
+        (status, output) = self.target.run('cat /etc/masterimage')
+        msg = "This isn't the right image  - /etc/masterimage " \
+              "shouldn't be here %s" % output
+        self.assertEqual(status, 1, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/systemd.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/systemd.py
new file mode 100644
index 0000000..db69384
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/systemd.py
@@ -0,0 +1,181 @@
+import re
+import time
+
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfDataVar, skipIfNotDataVar
+from oeqa.runtime.decorator.package import OEHasPackage
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class SystemdTest(OERuntimeTestCase):
+
+    def systemctl(self, action='', target='', expected=0, verbose=False):
+        command = 'systemctl %s %s' % (action, target)
+        status, output = self.target.run(command)
+        message = '\n'.join([command, output])
+        if status != expected and verbose:
+            cmd = 'systemctl status --full %s' % target
+            message += self.target.run(cmd)[1]
+        self.assertEqual(status, expected, message)
+        return output
+
+    #TODO: use pyjournalctl instead
+    def journalctl(self, args='',l_match_units=None):
+        """
+        Request for the journalctl output to the current target system
+
+        Arguments:
+        -args, an optional argument pass through argument
+        -l_match_units, an optional list of units to filter the output
+        Returns:
+        -string output of the journalctl command
+        Raises:
+        -AssertionError, on remote commands that fail
+        -ValueError, on a journalctl call with filtering by l_match_units that
+        returned no entries
+        """
+
+        query_units=''
+        if l_match_units:
+            query_units = ['_SYSTEMD_UNIT='+unit for unit in l_match_units]
+            query_units = ' '.join(query_units)
+        command = 'journalctl %s %s' %(args, query_units)
+        status, output = self.target.run(command)
+        if status:
+            raise AssertionError("Command '%s' returned non-zero exit "
+                    'code %d:\n%s' % (command, status, output))
+        if len(output) == 1 and "-- No entries --" in output:
+            raise ValueError('List of units to match: %s, returned no entries'
+                    % l_match_units)
+        return output
+
+class SystemdBasicTests(SystemdTest):
+
+    def settle(self):
+        """
+        Block until systemd has finished activating any units being activated,
+        or until two minutes has elapsed.
+
+        Returns a tuple, either (True, '') if all units have finished
+        activating, or (False, message string) if there are still units
+        activating (generally, failing units that restart).
+        """
+        endtime = time.time() + (60 * 2)
+        while True:
+            status, output = self.target.run('systemctl --state=activating')
+            if "0 loaded units listed" in output:
+                return (True, '')
+            if time.time() >= endtime:
+                return (False, output)
+            time.sleep(10)
+
+    @skipIfNotFeature('systemd',
+                      'Test requires systemd to be in DISTRO_FEATURES')
+    @skipIfNotDataVar('VIRTUAL-RUNTIME_init_manager', 'systemd',
+                      'systemd is not the init manager for this image')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_systemd_basic(self):
+        self.systemctl('--version')
+
+    @OETestID(551)
+    @OETestDepends(['systemd.SystemdBasicTests.test_systemd_basic'])
+    def test_systemd_list(self):
+        self.systemctl('list-unit-files')
+
+    @OETestID(550)
+    @OETestDepends(['systemd.SystemdBasicTests.test_systemd_basic'])
+    def test_systemd_failed(self):
+        settled, output = self.settle()
+        msg = "Timed out waiting for systemd to settle:\n%s" % output
+        self.assertTrue(settled, msg=msg)
+
+        output = self.systemctl('list-units', '--failed')
+        match = re.search('0 loaded units listed', output)
+        if not match:
+            output += self.systemctl('status --full --failed')
+        self.assertTrue(match, msg='Some systemd units failed:\n%s' % output)
+
+
+class SystemdServiceTests(SystemdTest):
+
+    @OEHasPackage(['avahi-daemon'])
+    @OETestDepends(['systemd.SystemdBasicTests.test_systemd_basic'])
+    def test_systemd_status(self):
+        self.systemctl('status --full', 'avahi-daemon.service')
+
+    @OETestID(695)
+    @OETestDepends(['systemd.SystemdServiceTests.test_systemd_status'])
+    def test_systemd_stop_start(self):
+        self.systemctl('stop', 'avahi-daemon.service')
+        self.systemctl('is-active', 'avahi-daemon.service',
+                       expected=3, verbose=True)
+        self.systemctl('start','avahi-daemon.service')
+        self.systemctl('is-active', 'avahi-daemon.service', verbose=True)
+
+    @OETestID(696)
+    @OETestDepends(['systemd.SystemdServiceTests.test_systemd_status'])
+    def test_systemd_disable_enable(self):
+        self.systemctl('disable', 'avahi-daemon.service')
+        self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
+        self.systemctl('enable', 'avahi-daemon.service')
+        self.systemctl('is-enabled', 'avahi-daemon.service')
+
+class SystemdJournalTests(SystemdTest):
+
+    @OETestDepends(['systemd.SystemdBasicTests.test_systemd_basic'])
+    def test_systemd_journal(self):
+        status, output = self.target.run('journalctl')
+        self.assertEqual(status, 0, output)
+
+    @OETestDepends(['systemd.SystemdBasicTests.test_systemd_basic'])
+    def test_systemd_boot_time(self, systemd_TimeoutStartSec=90):
+        """
+        Get the target boot time from journalctl and log it
+
+        Arguments:
+        -systemd_TimeoutStartSec, an optional argument containing systemd's
+        unit start timeout to compare against
+        """
+
+        # The expression chain that uniquely identifies the time boot message.
+        expr_items=['Startup finished', 'kernel', 'userspace','\.$']
+        try:
+            output = self.journalctl(args='-o cat --reverse')
+        except AssertionError:
+            self.fail('Error occurred while calling journalctl')
+        if not len(output):
+            self.fail('Error, unable to get startup time from systemd journal')
+
+        # Check for the regular expression items that match the startup time.
+        for line in output.split('\n'):
+            check_match = ''.join(re.findall('.*'.join(expr_items), line))
+            if check_match:
+                break
+        # Put the startup time in the test log
+        if check_match:
+            self.tc.logger.info('%s' % check_match)
+        else:
+            self.skipTest('Error at obtaining the boot time from journalctl')
+        boot_time_sec = 0
+
+        # Get the numeric values from the string and convert them to seconds
+        # same data will be placed in list and string for manipulation.
+        l_boot_time = check_match.split(' ')[-2:]
+        s_boot_time = ' '.join(l_boot_time)
+        try:
+            # Obtain the minutes it took to boot.
+            if l_boot_time[0].endswith('min') and l_boot_time[0][0].isdigit():
+                boot_time_min = s_boot_time.split('min')[0]
+                # Convert to seconds and accumulate it.
+                boot_time_sec += int(boot_time_min) * 60
+            # Obtain the seconds it took to boot and accumulate.
+            boot_time_sec += float(l_boot_time[1].split('s')[0])
+        except ValueError:
+            self.skipTest('Error when parsing time from boot string')
+
+        # Assert the target boot time against systemd's unit start timeout.
+        if boot_time_sec > systemd_TimeoutStartSec:
+            msg = ("Target boot time %s exceeds systemd's TimeoutStartSec %s"
+                    % (boot_time_sec, systemd_TimeoutStartSec))
+            self.tc.logger.info(msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/x32lib.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/x32lib.py
new file mode 100644
index 0000000..8da0154
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/x32lib.py
@@ -0,0 +1,19 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotInDataVar
+
+class X32libTest(OERuntimeTestCase):
+
+    @skipIfNotInDataVar('DEFAULTTUNE', 'x86-64-x32',
+                        'DEFAULTTUNE is not set to x86-64-x32')
+    @OETestID(281)
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_x32_file(self):
+        cmd = 'readelf -h /bin/ls | grep Class | grep ELF32'
+        status1 = self.target.run(cmd)[0]
+        cmd = 'readelf -h /bin/ls | grep Machine | grep X86-64'
+        status2 = self.target.run(cmd)[0]
+        msg = ("/bin/ls isn't an X86-64 ELF32 binary. readelf says: %s" % 
+                self.target.run("readelf -h /bin/ls")[1])
+        self.assertTrue(status1 == 0 and status2 == 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/xorg.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/xorg.py
new file mode 100644
index 0000000..2124813
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/cases/xorg.py
@@ -0,0 +1,17 @@
+from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.core.decorator.data import skipIfNotFeature
+
+class XorgTest(OERuntimeTestCase):
+
+    @OETestID(1151)
+    @skipIfNotFeature('x11-base',
+                      'Test requires x11 to be in IMAGE_FEATURES')
+    @OETestDepends(['ssh.SSHTest.test_ssh'])
+    def test_xorg_running(self):
+        cmd ='%s | grep -v xinit | grep [X]org' % self.tc.target_cmds['ps']
+        status, output = self.target.run(cmd)
+        msg = ('Xorg does not appear to be running %s' %
+              self.target.run(self.tc.target_cmds['ps'])[1])
+        self.assertEqual(status, 0, msg=msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py
deleted file mode 100644
index 003fefe..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/connman.py
+++ /dev/null
@@ -1,31 +0,0 @@
-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):
-
-    def service_status(self, service):
-        if oeRuntimeTest.hasFeature("systemd"):
-            (status, output) = self.target.run('systemctl status -l %s' % service)
-            return output
-        else:
-            return "Unable to get status or logs for %s" % service
-
-    @testcase(961)
-    @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))
-
-    @testcase(221)
-    @skipUnlessPassed('test_connmand_help')
-    def test_connmand_running(self):
-        (status, output) = self.target.run(oeRuntimeTest.pscmd + ' | grep [c]onnmand')
-        if status != 0:
-            print(self.service_status("connman"))
-            self.fail("No connmand process running")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/context.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/context.py
new file mode 100644
index 0000000..c4cd76c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/context.py
@@ -0,0 +1,220 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+
+from oeqa.core.context import OETestContext, OETestContextExecutor
+from oeqa.core.target.ssh import OESSHTarget
+from oeqa.core.target.qemu import OEQemuTarget
+from oeqa.utils.dump import HostDumper
+
+from oeqa.runtime.loader import OERuntimeTestLoader
+
+class OERuntimeTestContext(OETestContext):
+    loaderClass = OERuntimeTestLoader
+    runtime_files_dir = os.path.join(
+                        os.path.dirname(os.path.abspath(__file__)), "files")
+
+    def __init__(self, td, logger, target,
+                 host_dumper, image_packages, extract_dir):
+        super(OERuntimeTestContext, self).__init__(td, logger)
+
+        self.target = target
+        self.image_packages = image_packages
+        self.host_dumper = host_dumper
+        self.extract_dir = extract_dir
+        self._set_target_cmds()
+
+    def _set_target_cmds(self):
+        self.target_cmds = {}
+
+        self.target_cmds['ps'] = 'ps'
+        if 'procps' in self.image_packages:
+            self.target_cmds['ps'] = self.target_cmds['ps'] + ' -ef'
+
+class OERuntimeTestContextExecutor(OETestContextExecutor):
+    _context_class = OERuntimeTestContext
+
+    name = 'runtime'
+    help = 'runtime test component'
+    description = 'executes runtime tests over targets'
+
+    default_cases = os.path.join(os.path.abspath(os.path.dirname(__file__)),
+            'cases')
+    default_data = None
+    default_test_data = 'data/testdata.json'
+    default_tests = ''
+
+    default_target_type = 'simpleremote'
+    default_manifest = 'data/manifest'
+    default_server_ip = '192.168.7.1'
+    default_target_ip = '192.168.7.2'
+    default_host_dumper_dir = '/tmp/oe-saved-tests'
+    default_extract_dir = 'packages/extracted'
+
+    def register_commands(self, logger, subparsers):
+        super(OERuntimeTestContextExecutor, self).register_commands(logger, subparsers)
+
+        runtime_group = self.parser.add_argument_group('runtime options')
+
+        runtime_group.add_argument('--target-type', action='store',
+                default=self.default_target_type, choices=['simpleremote', 'qemu'],
+                help="Target type of device under test, default: %s" \
+                % self.default_target_type)
+        runtime_group.add_argument('--target-ip', action='store',
+                default=self.default_target_ip,
+                help="IP address of device under test, default: %s" \
+                % self.default_target_ip)
+        runtime_group.add_argument('--server-ip', action='store',
+                default=self.default_target_ip,
+                help="IP address of device under test, default: %s" \
+                % self.default_server_ip)
+
+        runtime_group.add_argument('--host-dumper-dir', action='store',
+                default=self.default_host_dumper_dir,
+                help="Directory where host status is dumped, if tests fails, default: %s" \
+                % self.default_host_dumper_dir)
+
+        runtime_group.add_argument('--packages-manifest', action='store',
+                default=self.default_manifest,
+                help="Package manifest of the image under testi, default: %s" \
+                % self.default_manifest)
+
+        runtime_group.add_argument('--extract-dir', action='store',
+                default=self.default_extract_dir,
+                help='Directory where extracted packages reside, default: %s' \
+                % self.default_extract_dir)
+
+        runtime_group.add_argument('--qemu-boot', action='store',
+                help="Qemu boot configuration, only needed when target_type is QEMU.")
+
+    @staticmethod
+    def getTarget(target_type, logger, target_ip, server_ip, **kwargs):
+        target = None
+
+        if target_type == 'simpleremote':
+            target = OESSHTarget(logger, target_ip, server_ip, **kwargs)
+        elif target_type == 'qemu':
+            target = OEQemuTarget(logger, target_ip, server_ip, **kwargs)
+        else:
+            # XXX: This code uses the old naming convention for controllers and
+            # targets, the idea it is to leave just targets as the controller
+            # most of the time was just a wrapper.
+            # XXX: This code tries to import modules from lib/oeqa/controllers
+            # directory and treat them as controllers, it will less error prone
+            # to use introspection to load such modules.
+            # XXX: Don't base your targets on this code it will be refactored
+            # in the near future.
+            # Custom target module loading
+            try:
+                target_modules_path = kwargs.get('target_modules_path', '')
+                controller = OERuntimeTestContextExecutor.getControllerModule(target_type, target_modules_path)
+                target = controller(logger, target_ip, server_ip, **kwargs)
+            except ImportError as e:
+                raise TypeError("Failed to import %s from available controller modules" % target_type)
+
+        return target
+
+    # Search oeqa.controllers module directory for and return a controller
+    # corresponding to the given target name.
+    # AttributeError raised if not found.
+    # ImportError raised if a provided module can not be imported.
+    @staticmethod
+    def getControllerModule(target, target_modules_path):
+        controllerslist = OERuntimeTestContextExecutor._getControllerModulenames(target_modules_path)
+        controller = OERuntimeTestContextExecutor._loadControllerFromName(target, controllerslist)
+        return controller
+
+    # Return a list of all python modules in lib/oeqa/controllers for each
+    # layer in bbpath
+    @staticmethod
+    def _getControllerModulenames(target_modules_path):
+
+        controllerslist = []
+
+        def add_controller_list(path):
+            if not os.path.exists(os.path.join(path, '__init__.py')):
+                raise OSError('Controllers directory %s exists but is missing __init__.py' % path)
+            files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_')])
+            for f in files:
+                module = 'oeqa.controllers.' + f[:-3]
+                if module not in controllerslist:
+                    controllerslist.append(module)
+                else:
+                    raise RuntimeError("Duplicate controller module found for %s. Layers should create unique controller module names" % module)
+
+        extpath = target_modules_path.split(':')
+        for p in extpath:
+            controllerpath = os.path.join(p, 'lib', 'oeqa', 'controllers')
+            if os.path.exists(controllerpath):
+                add_controller_list(controllerpath)
+        return controllerslist
+
+    # Search for and return a controller from given target name and
+    # set of module names.
+    # Raise AttributeError if not found.
+    # Raise ImportError if a provided module can not be imported
+    @staticmethod
+    def _loadControllerFromName(target, modulenames):
+        for name in modulenames:
+            obj = OERuntimeTestContextExecutor._loadControllerFromModule(target, name)
+            if obj:
+                return obj
+        raise AttributeError("Unable to load {0} from available modules: {1}".format(target, str(modulenames)))
+
+    # Search for and return a controller or None from given module name
+    @staticmethod
+    def _loadControllerFromModule(target, modulename):
+        obj = None
+        # import module, allowing it to raise import exception
+        try:
+            module = __import__(modulename, globals(), locals(), [target])
+        except Exception as e:
+            return obj
+        # look for target class in the module, catching any exceptions as it
+        # is valid that a module may not have the target class.
+        try:
+            obj = getattr(module, target)
+        except:
+            obj = None
+        return obj
+        
+    @staticmethod
+    def readPackagesManifest(manifest):
+        if not manifest or not os.path.exists(manifest):
+            raise OSError("Manifest file not exists: %s" % manifest)
+
+        image_packages = set()
+        with open(manifest, 'r') as f:
+            for line in f.readlines():
+                line = line.strip()
+                if line and not line.startswith("#"):
+                    image_packages.add(line.split()[0])
+
+        return image_packages
+
+    @staticmethod
+    def getHostDumper(cmds, directory):
+        return HostDumper(cmds, directory)
+
+    def _process_args(self, logger, args):
+        if not args.packages_manifest:
+            raise TypeError('Manifest file not provided')
+
+        super(OERuntimeTestContextExecutor, self)._process_args(logger, args)
+
+        target_kwargs = {}
+        target_kwargs['qemuboot'] = args.qemu_boot
+
+        self.tc_kwargs['init']['target'] = \
+                OERuntimeTestContextExecutor.getTarget(args.target_type,
+                        None, args.target_ip, args.server_ip, **target_kwargs)
+        self.tc_kwargs['init']['host_dumper'] = \
+                OERuntimeTestContextExecutor.getHostDumper(None,
+                        args.host_dumper_dir)
+        self.tc_kwargs['init']['image_packages'] = \
+                OERuntimeTestContextExecutor.readPackagesManifest(
+                        args.packages_manifest)
+        self.tc_kwargs['init']['extract_dir'] = args.extract_dir
+
+_executor_class = OERuntimeTestContextExecutor
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/date.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/date.py
deleted file mode 100644
index 447987e..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/date.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from oeqa.oetest import oeRuntimeTest
-from oeqa.utils.decorators import *
-import re
-
-class DateTest(oeRuntimeTest):
-
-    def setUpLocal(self):
-        if oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", True) == "systemd":
-            self.target.run('systemctl stop systemd-timesyncd')
-
-    def tearDownLocal(self):
-        if oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", True) == "systemd":
-            self.target.run('systemctl start systemd-timesyncd')
-
-    @testcase(211)
-    @skipUnlessPassed("test_ssh")
-    def test_date(self):
-        (status, output) = self.target.run('date +"%Y-%m-%d %T"')
-        self.assertEqual(status, 0, msg="Failed to get initial date, output: %s" % output)
-        oldDate = output
-
-        sampleDate = '"2016-08-09 10:00:00"'
-        (status, output) = self.target.run("date -s %s" % sampleDate)
-        self.assertEqual(status, 0, msg="Date set failed, output: %s" % output)
-
-        (status, output) = self.target.run("date -R")
-        p = re.match('Tue, 09 Aug 2016 10:00:.. \+0000', output)
-        self.assertTrue(p, msg="The date was not set correctly, output: %s" % output)
-
-        (status, output) = self.target.run('date -s "%s"' % oldDate)
-        self.assertEqual(status, 0, msg="Failed to reset date, output: %s" % output)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/decorator/package.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/decorator/package.py
new file mode 100644
index 0000000..aa6ecb6
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/decorator/package.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.decorator import OETestDecorator, registerDecorator
+from oeqa.core.utils.misc import strToSet
+
+@registerDecorator
+class OEHasPackage(OETestDecorator):
+    """
+        Checks if image has packages (un)installed.
+
+        The argument must be a string, set, or list of packages that must be
+        installed or not present in the image.
+
+        The way to tell a package must not be in an image is using an
+        exclamation point ('!') before the name of the package.
+
+        If test depends on pkg1 or pkg2 you need to use:
+        @OEHasPackage({'pkg1', 'pkg2'})
+
+        If test depends on pkg1 and pkg2 you need to use:
+        @OEHasPackage('pkg1')
+        @OEHasPackage('pkg2')
+
+        If test depends on pkg1 but pkg2 must not be present use:
+        @OEHasPackage({'pkg1', '!pkg2'})
+    """
+
+    attrs = ('need_pkgs',)
+
+    def setUpDecorator(self):
+        need_pkgs = set()
+        unneed_pkgs = set()
+        pkgs = strToSet(self.need_pkgs)
+        for pkg in pkgs:
+            if pkg.startswith('!'):
+                unneed_pkgs.add(pkg[1:])
+            else:
+                need_pkgs.add(pkg)
+
+        if unneed_pkgs:
+            msg = 'Checking if %s is not installed' % ', '.join(unneed_pkgs)
+            self.logger.debug(msg)
+            if not self.case.tc.image_packages.isdisjoint(unneed_pkgs):
+                msg = "Test can't run with %s installed" % ', or'.join(unneed_pkgs)
+                self.case.skipTest(msg)
+
+        if need_pkgs:
+            msg = 'Checking if at least one of %s is installed' % ', '.join(need_pkgs)
+            self.logger.debug(msg)
+            if self.case.tc.image_packages.isdisjoint(need_pkgs):
+                msg = "Test requires %s to be installed" % ', or'.join(need_pkgs)
+                self.case.skipTest(msg)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/df.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/df.py
deleted file mode 100644
index 09569d5..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/df.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest
-from oeqa.utils.decorators import *
-
-
-class DfTest(oeRuntimeTest):
-
-    @testcase(234)
-    @skipUnlessPassed("test_ssh")
-    def test_df(self):
-        (status,output) = self.target.run("df / | sed -n '2p' | awk '{print $4}'")
-        self.assertTrue(int(output)>5120, msg="Not enough space on image. Current size is %s" % output)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/gcc.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/gcc.py
deleted file mode 100644
index d90cd17..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/gcc.py
+++ /dev/null
@@ -1,47 +0,0 @@
-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")
-        oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.cpp"), "/tmp/test.cpp")
-
-    @testcase(203)
-    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)
-
-    @testcase(200)
-    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)
-
-    @testcase(1142)
-    def test_gpp2_compile(self):
-        (status, output) = self.target.run('g++ /tmp/test.cpp -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)
-
-    @testcase(204)
-    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")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/kernelmodule.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/kernelmodule.py
deleted file mode 100644
index 38ca184..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/kernelmodule.py
+++ /dev/null
@@ -1,34 +0,0 @@
-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 KernelModuleTest(oeRuntimeTest):
-
-    def setUpLocal(self):
-        self.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "hellomod.c"), "/tmp/hellomod.c")
-        self.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "hellomod_makefile"), "/tmp/Makefile")
-
-    @testcase('316')
-    @skipUnlessPassed('test_ssh')
-    @skipUnlessPassed('test_gcc_compile')
-    def test_kernel_module(self):
-        cmds = [
-            'cd /usr/src/kernel && make scripts',
-            'cd /tmp && make',
-            'cd /tmp && insmod hellomod.ko',
-            'lsmod | grep hellomod',
-            'dmesg | grep Hello',
-            'rmmod hellomod', 'dmesg | grep "Cleaning up hellomod"'
-            ]
-        for cmd in cmds:
-            (status, output) = self.target.run(cmd, 900)
-            self.assertEqual(status, 0, msg="\n".join([cmd, output]))
-
-    def tearDownLocal(self):
-        self.target.run('rm -f /tmp/Makefile /tmp/hellomod.c')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ldd.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/ldd.py
deleted file mode 100644
index 47b3885..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ldd.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import unittest
-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 LddTest(oeRuntimeTest):
-
-    @testcase(962)
-    @skipUnlessPassed('test_ssh')
-    def test_ldd_exists(self):
-        (status, output) = self.target.run('which ldd')
-        self.assertEqual(status, 0, msg = "ldd does not exist in PATH: which ldd: %s" % output)
-
-    @testcase(239)
-    @skipUnlessPassed('test_ldd_exists')
-    def test_ldd_rtldlist_check(self):
-        (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')
-        self.assertEqual(status, 0, msg = "ldd path not correct or RTLDLIST files don't exist. ")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/loader.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/loader.py
new file mode 100644
index 0000000..041ef97
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/loader.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.core.loader import OETestLoader
+from oeqa.runtime.case import OERuntimeTestCase
+
+class OERuntimeTestLoader(OETestLoader):
+    caseClass = OERuntimeTestCase
+
+    def _getTestCase(self, testCaseClass, tcName):
+        case = super(OERuntimeTestLoader, self)._getTestCase(testCaseClass, tcName)
+
+        # Adds custom attributes to the OERuntimeTestCase
+        setattr(case, 'target', self.tc.target)
+
+        return case
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/logrotate.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/logrotate.py
deleted file mode 100644
index de300bf..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/logrotate.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=289 testcase
-# Note that the image under test must have logrotate installed
-
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasPackage("logrotate"):
-        skipModule("No logrotate package in image")
-
-
-class LogrotateTest(oeRuntimeTest):
-
-    @skipUnlessPassed("test_ssh")
-    def test_1_logrotate_setup(self):
-        (status, output) = self.target.run('mkdir $HOME/logrotate_dir')
-        self.assertEqual(status, 0, msg = "Could not create logrotate_dir. Output: %s" % output)
-        (status, output) = self.target.run("sed -i \"s#wtmp {#wtmp {\\n    olddir $HOME/logrotate_dir#\" /etc/logrotate.conf")
-        self.assertEqual(status, 0, msg = "Could not write to logrotate.conf file. Status and output: %s and %s)" % (status, output))
-
-    @testcase(289)
-    @skipUnlessPassed("test_1_logrotate_setup")
-    def test_2_logrotate(self):
-        (status, output) = self.target.run('logrotate -f /etc/logrotate.conf')
-        self.assertEqual(status, 0, msg = "logrotate service could not be reloaded. Status and output: %s and %s" % (status, output))
-        output = self.target.run('ls -la $HOME/logrotate_dir/ | wc -l')[1]
-        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/logrotate_dir')[1]))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/multilib.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/multilib.py
deleted file mode 100644
index 593d385..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/multilib.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    multilibs = oeRuntimeTest.tc.d.getVar("MULTILIBS", True) or ""
-    if "multilib:lib32" not in multilibs:
-        skipModule("this isn't a multilib:lib32 image")
-
-
-class MultilibTest(oeRuntimeTest):
-
-    def archtest(self, binary, arch):
-        """
-        Check that ``binary`` has the ELF class ``arch`` (e.g. ELF32/ELF64).
-        """
-
-        (status, output) = self.target.run("readelf -h %s" % binary)
-        self.assertEqual(status, 0, "Failed to readelf %s" % binary)
-
-        l = [l.split()[1] for l in output.split('\n') if "Class:" in l]
-        if l:
-            theclass = l[0]
-        else:
-            self.fail("Cannot parse readelf output\n" + s)
-
-        self.assertEqual(theclass, arch, msg="%s isn't %s (is %s)" % (binary, arch, theclass))
-
-    @skipUnlessPassed('test_ssh')
-    def test_check_multilib_libc(self):
-        """
-        Check that a multilib image has both 32-bit and 64-bit libc in.
-        """
-        self.archtest("/lib/libc.so.6", "ELF32")
-        self.archtest("/lib64/libc.so.6", "ELF64")
-
-    @testcase('279')
-    @skipUnlessPassed('test_check_multilib_libc')
-    def test_file_connman(self):
-        self.assertTrue(oeRuntimeTest.hasPackage('lib32-connman'), msg="This test assumes lib32-connman is installed")
-
-        self.archtest("/usr/sbin/connmand", "ELF32")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/pam.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/pam.py
deleted file mode 100644
index c8205c9..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/pam.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=287 testcase
-# Note that the image under test must have "pam" in DISTRO_FEATURES
-
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("pam"):
-        skipModule("target doesn't have 'pam' in DISTRO_FEATURES")
-
-
-class PamBasicTest(oeRuntimeTest):
-
-    @testcase(287)
-    @skipUnlessPassed('test_ssh')
-    def test_pam(self):
-        (status, output) = self.target.run('login --help')
-        self.assertEqual(status, 1, msg = "login command does not work as expected. Status and output:%s and %s" %(status, output))
-        (status, output) = self.target.run('passwd --help')
-        self.assertEqual(status, 0, msg = "passwd command does not work as expected. Status and output:%s and %s" %(status, output))
-        (status, output) = self.target.run('su --help')
-        self.assertEqual(status, 0, msg = "su command does not work as expected. Status and output:%s and %s" %(status, output))
-        (status, output) = self.target.run('useradd --help')
-        self.assertEqual(status, 0, msg = "useradd command does not work as expected. Status and output:%s and %s" %(status, output))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/perl.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/perl.py
deleted file mode 100644
index e044d0a..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/perl.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import unittest
-import os
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasPackage("perl"):
-        skipModule("No perl package in the image")
-
-
-class PerlTest(oeRuntimeTest):
-
-    @classmethod
-    def setUpClass(self):
-        oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.pl"), "/tmp/test.pl")
-
-    @testcase(1141)
-    def test_perl_exists(self):
-        (status, output) = self.target.run('which perl')
-        self.assertEqual(status, 0, msg="Perl binary not in PATH or not on target.")
-
-    @testcase(208)
-    def test_perl_works(self):
-        (status, output) = self.target.run('perl /tmp/test.pl')
-        self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
-        self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
-
-    @classmethod
-    def tearDownClass(self):
-        oeRuntimeTest.tc.target.run("rm /tmp/test.pl")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py
deleted file mode 100644
index 0f27447..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ping.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import subprocess
-import unittest
-import sys
-import time
-from oeqa.oetest import oeRuntimeTest
-from oeqa.utils.decorators import *
-
-class PingTest(oeRuntimeTest):
-
-    @testcase(964)
-    def test_ping(self):
-        output = ''
-        count = 0
-        endtime = time.time() + 60
-        while count < 5 and time.time() < endtime:
-            proc = subprocess.Popen("ping -c 1 %s" % self.target.ip, shell=True, stdout=subprocess.PIPE)
-            output += proc.communicate()[0].decode("utf-8")
-            if proc.poll() == 0:
-                count += 1
-            else:
-                count = 0
-        self.assertEqual(count, 5, msg = "Expected 5 consecutive replies, got %d.\nping output is:\n%s" % (count,output))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py
deleted file mode 100644
index 29a231c..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/python.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import unittest
-import os
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasPackage("python-core"):
-        skipModule("No python package in the image")
-
-
-class PythonTest(oeRuntimeTest):
-
-    @classmethod
-    def setUpClass(self):
-        oeRuntimeTest.tc.target.copy_to(os.path.join(oeRuntimeTest.tc.filesdir, "test.py"), "/tmp/test.py")
-
-    @testcase(1145)
-    def test_python_exists(self):
-        (status, output) = self.target.run('which python')
-        self.assertEqual(status, 0, msg="Python binary not in PATH or not on target.")
-
-    @testcase(965)
-    def test_python_stdout(self):
-        (status, output) = self.target.run('python /tmp/test.py')
-        self.assertEqual(status, 0, msg="Exit status was not 0. Output: %s" % output)
-        self.assertEqual(output, "the value of a is 0.01", msg="Incorrect output: %s" % output)
-
-    @testcase(1146)
-    def test_python_testfile(self):
-        (status, output) = self.target.run('ls /tmp/testfile.python')
-        self.assertEqual(status, 0, msg="Python test file generate failed.")
-
-    @classmethod
-    def tearDownClass(self):
-        oeRuntimeTest.tc.target.run("rm /tmp/test.py /tmp/testfile.python")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py
deleted file mode 100644
index 7f514ca..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/rpm.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import unittest
-import os
-import fnmatch
-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 RpmBasicTest(oeRuntimeTest):
-
-    @testcase(960)
-    @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))
-
-    @testcase(191)
-    @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))
-
-class RpmInstallRemoveTest(oeRuntimeTest):
-
-    @classmethod
-    def setUpClass(self):
-        pkgarch = oeRuntimeTest.tc.d.getVar('TUNE_PKGARCH', True).replace("-", "_")
-        rpmdir = os.path.join(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR', True), "rpm", pkgarch)
-        # pick rpm-doc as a test file to get installed, because it's small and it will always be built for standard targets
-        for f in fnmatch.filter(os.listdir(rpmdir), "rpm-doc-*.%s.rpm" % pkgarch):
-            testrpmfile = f
-        oeRuntimeTest.tc.target.copy_to(os.path.join(rpmdir,testrpmfile), "/tmp/rpm-doc.rpm")
-
-    @testcase(192)
-    @skipUnlessPassed('test_rpm_help')
-    def test_rpm_install(self):
-        (status, output) = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
-        self.assertEqual(status, 0, msg="Failed to install rpm-doc package: %s" % output)
-
-    @testcase(194)
-    @skipUnlessPassed('test_rpm_install')
-    def test_rpm_remove(self):
-        (status,output) = self.target.run('rpm -e rpm-doc')
-        self.assertEqual(status, 0, msg="Failed to remove rpm-doc package: %s" % output)
-
-    @testcase(1096)
-    @skipUnlessPassed('test_ssh')
-    def test_rpm_query_nonroot(self):
-
-        def set_up_test_user(u):
-            (status, output) = self.target.run("id -u %s" % u)
-            if status == 0:
-                pass
-            else:
-                (status, output) = self.target.run("useradd %s" % u)
-                self.assertTrue(status == 0, msg="Failed to create new user: " + output)
-
-        def exec_as_test_user(u):
-            (status, output) = self.target.run("su -c id %s" % u)
-            self.assertTrue("({0})".format(u) in output, msg="Failed to execute as new user")
-            (status, output) = self.target.run("su -c \"rpm -qa\" %s " % u)
-            self.assertEqual(status, 0, msg="status: %s. Cannot run rpm -qa: %s" % (status, output))
-
-        def unset_up_test_user(u):
-            (status, output) = self.target.run("userdel -r %s" % u)
-            self.assertTrue(status == 0, msg="Failed to erase user: %s" % output)
-
-        tuser = 'test1'
-
-        try:
-            set_up_test_user(tuser)
-            exec_as_test_user(tuser)
-        finally:
-            unset_up_test_user(tuser)
-
-    @testcase(195)
-    @skipUnlessPassed('test_rpm_install')
-    def test_check_rpm_install_removal_log_file_size(self):
-        """
-        Summary:     Check rpm install/removal log file size
-        Expected:    There should be some method to keep rpm log in a small size .
-        Product:     BSPs
-        Author:      Alexandru Georgescu <alexandru.c.georgescu@intel.com>
-        AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
-        """
-        db_files_cmd = 'ls /var/lib/rpm/__db.*'
-        get_log_size_cmd = "du /var/lib/rpm/log/log.* | awk '{print $1}'"
-
-        # Make sure that some database files are under /var/lib/rpm as '__db.xxx'
-        (status, output) = self.target.run(db_files_cmd)
-        self.assertEqual(0, status, 'Failed to find database files under /var/lib/rpm/ as __db.xxx')
-
-        # Remove the package just in case
-        self.target.run('rpm -e rpm-doc')
-
-        # Install/Remove a package 10 times
-        for i in range(10):
-            (status, output) = self.target.run('rpm -ivh /tmp/rpm-doc.rpm')
-            self.assertEqual(0, status, "Failed to install rpm-doc package. Reason: {}".format(output))
-
-            (status, output) = self.target.run('rpm -e rpm-doc')
-            self.assertEqual(0, status, "Failed to remove rpm-doc package. Reason: {}".format(output))
-
-        # Get the size of log file
-        (status, output) = self.target.run(get_log_size_cmd)
-        self.assertEqual(0, status, 'Failed to get the final size of the log file.')
-
-        # Compare each log size
-        for log_file_size in output:
-            self.assertLessEqual(int(log_file_size), 11264,
-                                   'Log file size is greater that expected (~10MB), found {} bytes'.format(log_file_size))
-
-    @classmethod
-    def tearDownClass(self):
-        oeRuntimeTest.tc.target.run('rm -f /tmp/rpm-doc.rpm')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/scanelf.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/scanelf.py
deleted file mode 100644
index 67e02ff..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/scanelf.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasPackage("pax-utils"):
-        skipModule("pax-utils package not installed")
-
-class ScanelfTest(oeRuntimeTest):
-
-    def setUpLocal(self):
-        self.scancmd = 'scanelf --quiet --recursive --mount --ldpath --path'
-
-    @testcase(966)
-    @skipUnlessPassed('test_ssh')
-    def test_scanelf_textrel(self):
-        # print TEXTREL information
-        self.scancmd += " --textrel"
-        (status, output) = self.target.run(self.scancmd)
-        self.assertEqual(output.strip(), "", "\n".join([self.scancmd, output]))
-
-    @testcase(967)
-    @skipUnlessPassed('test_ssh')
-    def test_scanelf_rpath(self):
-        # print RPATH information
-        self.scancmd += " --rpath"
-        (status, output) = self.target.run(self.scancmd)
-        self.assertEqual(output.strip(), "", "\n".join([self.scancmd, output]))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/scp.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/scp.py
deleted file mode 100644
index 48e87d2..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/scp.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import os
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import skipUnlessPassed, testcase
-
-def setUpModule():
-    if not (oeRuntimeTest.hasPackage("dropbear") or oeRuntimeTest.hasPackage("openssh-sshd")):
-        skipModule("No ssh package in image")
-
-class ScpTest(oeRuntimeTest):
-
-    @testcase(220)
-    @skipUnlessPassed('test_ssh')
-    def test_scp_file(self):
-        test_log_dir = oeRuntimeTest.tc.d.getVar("TEST_LOG_DIR", True)
-        test_file_path = os.path.join(test_log_dir, 'test_scp_file')
-        with open(test_file_path, 'w') as test_scp_file:
-            test_scp_file.seek(2 ** 22 - 1)
-            test_scp_file.write(os.linesep)
-        (status, output) = self.target.copy_to(test_file_path, '/tmp/test_scp_file')
-        self.assertEqual(status, 0, msg = "File could not be copied. Output: %s" % output)
-        (status, output) = self.target.run("ls -la /tmp/test_scp_file")
-        self.assertEqual(status, 0, msg = "SCP test failed")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/skeletoninit.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/skeletoninit.py
deleted file mode 100644
index cb0cb9b..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/skeletoninit.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# This test should cover https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=284 testcase
-# Note that the image under test must have meta-skeleton layer in bblayers and IMAGE_INSTALL_append = " service" in local.conf
-
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeRuntimeTest.hasPackage("service"):
-        skipModule("No service package in image")
-
-
-class SkeletonBasicTest(oeRuntimeTest):
-
-    @skipUnlessPassed('test_ssh')
-    @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", False), "Not appropiate for systemd image")
-    def test_skeleton_availability(self):
-        (status, output) = self.target.run('ls /etc/init.d/skeleton')
-        self.assertEqual(status, 0, msg = "skeleton init script not found. Output:\n%s " % output)
-        (status, output) =  self.target.run('ls /usr/sbin/skeleton-test')
-        self.assertEqual(status, 0, msg = "skeleton-test not found. Output:\n%s" % output)
-
-    @testcase(284)
-    @skipUnlessPassed('test_skeleton_availability')
-    @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", False), "Not appropiate for systemd image")
-    def test_skeleton_script(self):
-        output1 = self.target.run("/etc/init.d/skeleton start")[1]
-        (status, output2) = self.target.run(oeRuntimeTest.pscmd + ' | grep [s]keleton-test')
-        self.assertEqual(status, 0, msg = "Skeleton script could not be started:\n%s\n%s" % (output1, output2))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py
deleted file mode 100644
index 6cdb10d..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/smart.py
+++ /dev/null
@@ -1,218 +0,0 @@
-import unittest
-import re
-import oe
-import subprocess
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.httpserver import HTTPService
-
-def setUpModule():
-    if not oeRuntimeTest.hasFeature("package-management"):
-        skipModule("Image doesn't have package management feature")
-    if not oeRuntimeTest.hasPackage("smartpm"):
-        skipModule("Image doesn't have smart installed")
-    if "package_rpm" != oeRuntimeTest.tc.d.getVar("PACKAGE_CLASSES", True).split()[0]:
-        skipModule("Rpm is not the primary package manager")
-
-class SmartTest(oeRuntimeTest):
-
-    @skipUnlessPassed('test_smart_help')
-    def smart(self, command, expected = 0):
-        command = 'smart %s' % command
-        status, output = self.target.run(command, 1500)
-        message = os.linesep.join([command, output])
-        self.assertEqual(status, expected, message)
-        self.assertFalse("Cannot allocate memory" in output, message)
-        return output
-
-class SmartBasicTest(SmartTest):
-
-    @testcase(716)
-    @skipUnlessPassed('test_ssh')
-    def test_smart_help(self):
-        self.smart('--help')
-
-    @testcase(968)
-    def test_smart_version(self):
-        self.smart('--version')
-
-    @testcase(721)
-    def test_smart_info(self):
-        self.smart('info python-smartpm')
-
-    @testcase(421)
-    def test_smart_query(self):
-        self.smart('query python-smartpm')
-
-    @testcase(720)
-    def test_smart_search(self):
-        self.smart('search python-smartpm')
-
-    @testcase(722)
-    def test_smart_stats(self):
-        self.smart('stats')
-
-class SmartRepoTest(SmartTest):
-
-    @classmethod
-    def create_index(self, arg):
-        index_cmd = arg
-        try:
-            bb.note("Executing '%s' ..." % index_cmd)
-            result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
-        except subprocess.CalledProcessError as e:
-            return("Index creation command '%s' failed with return code %d:\n%s" %
-                    (e.cmd, e.returncode, e.output.decode("utf-8")))
-        if result:
-            bb.note(result)
-        return None
-
-    @classmethod
-    def setUpClass(self):
-        self.repolist = []
-
-        # Index RPMs
-        rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo")
-        index_cmds = []
-        rpm_dirs_found = False
-        archs = (oeRuntimeTest.tc.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
-        for arch in archs:
-            rpm_dir = os.path.join(oeRuntimeTest.tc.d.getVar('DEPLOY_DIR_RPM', True), arch)
-            idx_path = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True), 'rpm', arch)
-            db_path = os.path.join(oeRuntimeTest.tc.d.getVar('WORKDIR', True), 'rpmdb', arch)
-            if not os.path.isdir(rpm_dir):
-                continue
-            if os.path.exists(db_path):
-                bb.utils.remove(dbpath, True)
-            lockfilename = oeRuntimeTest.tc.d.getVar('DEPLOY_DIR_RPM', True) + "/rpm.lock"
-            lf = bb.utils.lockfile(lockfilename, False)
-            oe.path.copyhardlinktree(rpm_dir, idx_path)
-            # Full indexes overload a 256MB image so reduce the number of rpms
-            # in the feed. Filter to p* since we use the psplash packages and
-            # this leaves some allarch and machine arch packages too.
-            bb.utils.remove(idx_path + "*/[a-oq-z]*.rpm")
-            bb.utils.unlockfile(lf)
-            index_cmds.append("%s --dbpath %s --update -q %s" % (rpm_createrepo, db_path, idx_path))
-            rpm_dirs_found = True
-         # Create repodata¬
-        result = oe.utils.multiprocess_exec(index_cmds, self.create_index)
-        if result:
-            bb.fatal('%s' % ('\n'.join(result)))
-        self.repo_server = HTTPService(oeRuntimeTest.tc.d.getVar('WORKDIR', True), oeRuntimeTest.tc.target.server_ip)
-        self.repo_server.start()
-
-    @classmethod
-    def tearDownClass(self):
-        self.repo_server.stop()
-        for i in self.repolist:
-            oeRuntimeTest.tc.target.run('smart channel -y --remove '+str(i))
-
-    @testcase(1143)
-    def test_smart_channel(self):
-        self.smart('channel', 1)
-
-    @testcase(719)
-    def test_smart_channel_add(self):
-        image_pkgtype = self.tc.d.getVar('IMAGE_PKGTYPE', True)
-        deploy_url = 'http://%s:%s/%s' %(self.target.server_ip, self.repo_server.port, image_pkgtype)
-        pkgarchs = self.tc.d.getVar('PACKAGE_ARCHS', True).replace("-","_").split()
-        for arch in os.listdir('%s/%s' % (self.repo_server.root_dir, image_pkgtype)):
-            if arch in pkgarchs:
-                self.smart('channel -y --add {a} type=rpm-md baseurl={u}/{a}'.format(a=arch, u=deploy_url))
-                self.repolist.append(arch)
-        self.smart('update')
-
-    @testcase(969)
-    def test_smart_channel_help(self):
-        self.smart('channel --help')
-
-    @testcase(970)
-    def test_smart_channel_list(self):
-        self.smart('channel --list')
-
-    @testcase(971)
-    def test_smart_channel_show(self):
-        self.smart('channel --show')
-
-    @testcase(717)
-    def test_smart_channel_rpmsys(self):
-        self.smart('channel --show rpmsys')
-        self.smart('channel --disable rpmsys')
-        self.smart('channel --enable rpmsys')
-
-    @testcase(1144)
-    @skipUnlessPassed('test_smart_channel_add')
-    def test_smart_install(self):
-        self.smart('remove -y psplash-default')
-        self.smart('install -y psplash-default')
-
-    @testcase(728)
-    @skipUnlessPassed('test_smart_install')
-    def test_smart_install_dependency(self):
-        self.smart('remove -y psplash')
-        self.smart('install -y psplash-default')
-
-    @testcase(723)
-    @skipUnlessPassed('test_smart_channel_add')
-    def test_smart_install_from_disk(self):
-        self.smart('remove -y psplash-default')
-        self.smart('download psplash-default')
-        self.smart('install -y ./psplash-default*')
-
-    @testcase(725)
-    @skipUnlessPassed('test_smart_channel_add')
-    def test_smart_install_from_http(self):
-        output = self.smart('download --urls psplash-default')
-        url = re.search('(http://.*/psplash-default.*\.rpm)', output)
-        self.assertTrue(url, msg="Couln't find download url in %s" % output)
-        self.smart('remove -y psplash-default')
-        self.smart('install -y %s' % url.group(0))
-
-    @testcase(729)
-    @skipUnlessPassed('test_smart_install')
-    def test_smart_reinstall(self):
-        self.smart('reinstall -y psplash-default')
-
-    @testcase(727)
-    @skipUnlessPassed('test_smart_channel_add')
-    def test_smart_remote_repo(self):
-        self.smart('update')
-        self.smart('install -y psplash')
-        self.smart('remove -y psplash')
-
-    @testcase(726)
-    def test_smart_local_dir(self):
-        self.target.run('mkdir /tmp/myrpmdir')
-        self.smart('channel --add myrpmdir type=rpm-dir path=/tmp/myrpmdir -y')
-        self.target.run('cd /tmp/myrpmdir')
-        self.smart('download psplash')
-        output = self.smart('channel --list')
-        for i in output.split("\n"):
-            if ("rpmsys" != str(i)) and ("myrpmdir" != str(i)):
-                self.smart('channel --disable '+str(i))
-        self.target.run('cd $HOME')
-        self.smart('install psplash')
-        for i in output.split("\n"):
-            if ("rpmsys" != str(i)) and ("myrpmdir" != str(i)):
-                self.smart('channel --enable '+str(i))
-        self.smart('channel --remove myrpmdir -y')
-        self.target.run("rm -rf /tmp/myrpmdir")
-
-    @testcase(718)
-    def test_smart_add_rpmdir(self):
-        self.target.run('mkdir /tmp/myrpmdir')
-        self.smart('channel --add myrpmdir type=rpm-dir path=/tmp/myrpmdir -y')
-        self.smart('channel --disable myrpmdir -y')
-        output = self.smart('channel --show myrpmdir')
-        self.assertTrue("disabled = yes" in output, msg="Failed to disable rpm dir")
-        self.smart('channel --enable  myrpmdir -y')
-        output = self.smart('channel --show myrpmdir')
-        self.assertFalse("disabled = yes" in output, msg="Failed to enable rpm dir")
-        self.smart('channel --remove myrpmdir -y')
-        self.target.run("rm -rf /tmp/myrpmdir")
-
-    @testcase(731)
-    @skipUnlessPassed('test_smart_channel_add')
-    def test_smart_remove_package(self):
-        self.smart('install -y psplash')
-        self.smart('remove -y psplash')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ssh.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/ssh.py
deleted file mode 100644
index 0e76d5d..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/ssh.py
+++ /dev/null
@@ -1,19 +0,0 @@
-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):
-
-    @testcase(224)
-    @skipUnlessPassed('test_ping')
-    def test_ssh(self):
-        (status, output) = self.target.run('uname -a')
-        self.assertEqual(status, 0, msg="SSH Test failed: %s" % output)
-        (status, output) = self.target.run('cat /etc/masterimage')
-        self.assertEqual(status, 1, msg="This isn't the right image  - /etc/masterimage shouldn't be here %s" % output)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py
deleted file mode 100644
index 8f55032..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/syslog.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not (oeRuntimeTest.hasPackage("busybox-syslog") or oeRuntimeTest.hasPackage("sysklogd")):
-        skipModule("No syslog package in image")
-
-class SyslogTest(oeRuntimeTest):
-
-    @testcase(201)
-    def test_syslog_running(self):
-        (status,output) = self.target.run(oeRuntimeTest.pscmd + ' | grep -i [s]yslogd')
-        self.assertEqual(status, 0, msg="no syslogd process, ps output: %s" % self.target.run(oeRuntimeTest.pscmd)[1])
-
-class SyslogTestConfig(oeRuntimeTest):
-
-    @testcase(1149)
-    @skipUnlessPassed("test_syslog_running")
-    def test_syslog_logger(self):
-        (status, output) = self.target.run('logger foobar')
-        self.assertEqual(status, 0, msg="Can't log into syslog. Output: %s " % output)
-
-        (status, output) = self.target.run('grep foobar /var/log/messages')
-        if status != 0:
-            if oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", "") == "systemd":
-                (status, output) = self.target.run('journalctl -o cat | grep foobar')
-            else:
-                (status, output) = self.target.run('logread | grep foobar')
-        self.assertEqual(status, 0, msg="Test log string not found in /var/log/messages or logread. Output: %s " % output)
-
-    @testcase(1150)
-    @skipUnlessPassed("test_syslog_running")
-    def test_syslog_restart(self):
-        if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", False):
-            (status,output) = self.target.run('/etc/init.d/syslog restart')
-        else:
-            (status,output) = self.target.run('systemctl restart syslog.service')
-
-    @testcase(202)
-    @skipUnlessPassed("test_syslog_restart")
-    @skipUnlessPassed("test_syslog_logger")
-    @unittest.skipIf("systemd" == oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", False), "Not appropiate for systemd image")
-    @unittest.skipIf(oeRuntimeTest.hasPackage("sysklogd") or not oeRuntimeTest.hasPackage("busybox"), "Non-busybox syslog")
-    def test_syslog_startup_config(self):
-        self.target.run('echo "LOGFILE=/var/log/test" >> /etc/syslog-startup.conf')
-        (status,output) = self.target.run('/etc/init.d/syslog restart')
-        self.assertEqual(status, 0, msg="Could not restart syslog service. Status and output: %s and %s" % (status,output))
-        (status,output) = self.target.run('logger foobar && grep foobar /var/log/test')
-        self.assertEqual(status, 0, msg="Test log string not found. Output: %s " % output)
-        self.target.run("sed -i 's#LOGFILE=/var/log/test##' /etc/syslog-startup.conf")
-        self.target.run('/etc/init.d/syslog restart')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py
deleted file mode 100644
index 8de799c..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/systemd.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import unittest
-import re
-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 SystemdTest(oeRuntimeTest):
-
-    def systemctl(self, action = '', target = '', expected = 0, verbose = False):
-        command = 'systemctl %s %s' % (action, target)
-        status, output = self.target.run(command)
-        message = '\n'.join([command, output])
-        if status != expected and verbose:
-            message += self.target.run('systemctl status --full %s' % target)[1]
-        self.assertEqual(status, expected, message)
-        return output
-
-    #TODO: use pyjournalctl instead
-    def journalctl(self, args='',l_match_units=[]):
-        """
-        Request for the journalctl output to the current target system
-
-        Arguments:
-        -args, an optional argument pass through argument
-        -l_match_units, an optional list of units to filter the output
-        Returns:
-        -string output of the journalctl command
-        Raises:
-        -AssertionError, on remote commands that fail
-        -ValueError, on a journalctl call with filtering by l_match_units that
-        returned no entries
-        """
-        query_units=""
-        if len(l_match_units):
-            query_units = ['_SYSTEMD_UNIT='+unit for unit in l_match_units]
-            query_units = " ".join(query_units)
-        command = 'journalctl %s %s' %(args, query_units)
-        status, output = self.target.run(command)
-        if status:
-            raise AssertionError("Command '%s' returned non-zero exit \
-                    code %d:\n%s" % (command, status, output))
-        if len(output) == 1 and "-- No entries --" in output:
-            raise ValueError("List of units to match: %s, returned no entries"
-                    % l_match_units)
-        return output
-
-class SystemdBasicTests(SystemdTest):
-
-    @skipUnlessPassed('test_ssh')
-    def test_systemd_basic(self):
-        self.systemctl('--version')
-
-    @testcase(551)
-    @skipUnlessPassed('test_systemd_basic')
-    def test_systemd_list(self):
-        self.systemctl('list-unit-files')
-
-    def settle(self):
-        """
-        Block until systemd has finished activating any units being activated,
-        or until two minutes has elapsed.
-
-        Returns a tuple, either (True, '') if all units have finished
-        activating, or (False, message string) if there are still units
-        activating (generally, failing units that restart).
-        """
-        import time
-        endtime = time.time() + (60 * 2)
-        while True:
-            status, output = self.target.run('systemctl --state=activating')
-            if "0 loaded units listed" in output:
-                return (True, '')
-            if time.time() >= endtime:
-                return (False, output)
-            time.sleep(10)
-
-    @testcase(550)
-    @skipUnlessPassed('test_systemd_basic')
-    def test_systemd_failed(self):
-        settled, output = self.settle()
-        self.assertTrue(settled, msg="Timed out waiting for systemd to settle:\n" + output)
-
-        output = self.systemctl('list-units', '--failed')
-        match = re.search("0 loaded units listed", output)
-        if not match:
-            output += self.systemctl('status --full --failed')
-        self.assertTrue(match, msg="Some systemd units failed:\n%s" % output)
-
-
-class SystemdServiceTests(SystemdTest):
-
-    def check_for_avahi(self):
-        if not self.hasPackage('avahi-daemon'):
-            raise unittest.SkipTest("Testcase dependency not met: need avahi-daemon installed on target")
-
-    @skipUnlessPassed('test_systemd_basic')
-    def test_systemd_status(self):
-        self.check_for_avahi()
-        self.systemctl('status --full', 'avahi-daemon.service')
-
-    @testcase(695)
-    @skipUnlessPassed('test_systemd_status')
-    def test_systemd_stop_start(self):
-        self.check_for_avahi()
-        self.systemctl('stop', 'avahi-daemon.service')
-        self.systemctl('is-active', 'avahi-daemon.service', expected=3, verbose=True)
-        self.systemctl('start','avahi-daemon.service')
-        self.systemctl('is-active', 'avahi-daemon.service', verbose=True)
-
-    @testcase(696)
-    @skipUnlessPassed('test_systemd_basic')
-    def test_systemd_disable_enable(self):
-        self.check_for_avahi()
-        self.systemctl('disable', 'avahi-daemon.service')
-        self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
-        self.systemctl('enable', 'avahi-daemon.service')
-        self.systemctl('is-enabled', 'avahi-daemon.service')
-
-class SystemdJournalTests(SystemdTest):
-    @skipUnlessPassed('test_ssh')
-    def test_systemd_journal(self):
-        (status, output) = self.target.run('journalctl')
-        self.assertEqual(status, 0, output)
-
-    @skipUnlessPassed('test_systemd_basic')
-    def test_systemd_boot_time(self, systemd_TimeoutStartSec=90):
-        """
-        Get the target boot time from journalctl and log it
-
-        Arguments:
-        -systemd_TimeoutStartSec, an optional argument containing systemd's
-        unit start timeout to compare against
-        """
-
-        # the expression chain that uniquely identifies the time boot message
-        expr_items=["Startup finished","kernel", "userspace","\.$"]
-        try:
-            output = self.journalctl(args="-o cat --reverse")
-        except AssertionError:
-            self.fail("Error occurred while calling journalctl")
-        if not len(output):
-            self.fail("Error, unable to get startup time from systemd journal")
-
-        # check for the regular expression items that match the startup time
-        for line in output.split('\n'):
-            check_match = "".join(re.findall(".*".join(expr_items), line))
-            if check_match: break
-        # put the startup time in the test log
-        if check_match:
-            print("%s" % check_match)
-        else:
-            self.skipTest("Error at obtaining the boot time from journalctl")
-        boot_time_sec = 0
-
-        # get the numeric values from the string and convert them to seconds
-        # same data will be placed in list and string for manipulation
-        l_boot_time = check_match.split(" ")[-2:]
-        s_boot_time = " ".join(l_boot_time)
-        try:
-            # Obtain the minutes it took to boot
-            if l_boot_time[0].endswith('min') and l_boot_time[0][0].isdigit():
-                boot_time_min = s_boot_time.split("min")[0]
-                # convert to seconds and accumulate it
-                boot_time_sec += int(boot_time_min) * 60
-            # Obtain the seconds it took to boot and accumulate
-            boot_time_sec += float(l_boot_time[1].split("s")[0])
-        except ValueError:
-            self.skipTest("Error when parsing time from boot string")
-        #Assert the target boot time against systemd's unit start timeout
-        if boot_time_sec > systemd_TimeoutStartSec:
-            print("Target boot time %s exceeds systemd's TimeoutStartSec %s"\
-                    %(boot_time_sec, systemd_TimeoutStartSec))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/targetbuildproject.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/targetbuildproject.py
new file mode 100644
index 0000000..5af55d7
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/runtime/utils/targetbuildproject.py
@@ -0,0 +1,39 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+from oeqa.utils.buildproject import BuildProject
+
+class TargetBuildProject(BuildProject):
+
+    def __init__(self, target, uri, foldername=None, dl_dir=None):
+        self.target = target
+        self.targetdir = "~/"
+        BuildProject.__init__(self, uri, foldername, dl_dir=dl_dir)
+
+    def download_archive(self):
+        self._download_archive()
+
+        status, output = self.target.copyTo(self.localarchive, self.targetdir)
+        if status:
+            raise Exception('Failed to copy archive to target, '
+                            'output: %s' % output)
+
+        cmd = 'tar xf %s%s -C %s' % (self.targetdir,
+                                     self.archive,
+                                     self.targetdir)
+        status, output = self.target.run(cmd)
+        if status:
+            raise Exception('Failed to extract archive, '
+                            'output: %s' % output)
+
+        # Change targetdir to project folder
+        self.targetdir = self.targetdir + self.fname
+
+    # The timeout parameter of target.run is set to 0
+    # to make the ssh command run with no timeout.
+    def _run(self, cmd):
+        ret = self.target.run(cmd, 0)
+        msg = "Command %s failed with exit code %s: %s" % (cmd, ret[0], ret[1])
+        if ret[0] != 0:
+            raise Exception(msg)
+        return ret[0]
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/x32lib.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/x32lib.py
deleted file mode 100644
index ce5e214..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/x32lib.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import unittest
-from oeqa.oetest import oeRuntimeTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-        #check if DEFAULTTUNE is set and it's value is: x86-64-x32
-        defaulttune = oeRuntimeTest.tc.d.getVar("DEFAULTTUNE", True)
-        if "x86-64-x32" not in defaulttune:
-            skipModule("DEFAULTTUNE is not set to x86-64-x32")
-
-class X32libTest(oeRuntimeTest):
-
-    @testcase(281)
-    @skipUnlessPassed("test_ssh")
-    def test_x32_file(self):
-        status1 = self.target.run("readelf -h /bin/ls | grep Class | grep ELF32")[0]
-        status2 = self.target.run("readelf -h /bin/ls | grep Machine | grep X86-64")[0]
-        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/import-layers/yocto-poky/meta/lib/oeqa/runtime/xorg.py b/import-layers/yocto-poky/meta/lib/oeqa/runtime/xorg.py
deleted file mode 100644
index 12bcd37..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/runtime/xorg.py
+++ /dev/null
@@ -1,16 +0,0 @@
-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):
-
-    @testcase(1151)
-    @skipUnlessPassed('test_ssh')
-    def test_xorg_running(self):
-        (status, output) = self.target.run(oeRuntimeTest.pscmd + ' |  grep -v xinit | grep [X]org')
-        self.assertEqual(status, 0, msg="Xorg does not appear to be running %s" % self.target.run(oeRuntimeTest.pscmd)[1])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/__init__.py
index 4cf3fa7..e69de29 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/__init__.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/__init__.py
@@ -1,3 +0,0 @@
-# Enable other layers to have tests in the same named directory
-from pkgutil import extend_path
-__path__ = extend_path(__path__, __name__)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildcvs.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildcvs.py
deleted file mode 100644
index c7146fa..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildcvs.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import SDKBuildProject
-
-class BuildCvsTest(oeSDKTest):
-
-    @classmethod
-    def setUpClass(self):
-        self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/cvs/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
-                        "http://ftp.gnu.org/non-gnu/cvs/source/feature/1.12.13/cvs-1.12.13.tar.bz2")
-        self.project.download_archive()
-
-    def test_cvs(self):
-        self.assertEqual(self.project.run_configure(), 0,
-                        msg="Running configure failed")
-
-        self.assertEqual(self.project.run_make(), 0,
-                        msg="Running make failed")
-
-        self.assertEqual(self.project.run_install(), 0,
-                        msg="Running make install failed")
-
-    @classmethod
-    def tearDownClass(self):
-        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py
deleted file mode 100644
index dc2fa9c..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildgalculator.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import SDKBuildProject
-
-def setUpModule():
-    if not (oeSDKTest.hasPackage("gtk+3") or oeSDKTest.hasPackage("libgtk-3.0")):
-        skipModule("Image doesn't have gtk+3 in manifest")
-
-class GalculatorTest(oeSDKTest):
-    def test_galculator(self):
-        try:
-            project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/galculator/",
-                                      oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
-                                      "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2")
-
-            project.download_archive()
-
-            # regenerate configure to get support for --with-libtool-sysroot
-            legacy_preconf=("autoreconf -i -f -I ${OECORE_TARGET_SYSROOT}/usr/share/aclocal -I m4;")
-
-            self.assertEqual(project.run_configure(extra_cmds=legacy_preconf),
-                             0, msg="Running configure failed")
-
-            self.assertEqual(project.run_make(), 0,
-                            msg="Running make failed")
-        finally:
-            project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py
deleted file mode 100644
index f0cb8a4..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/buildiptables.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from oeqa.oetest import oeSDKTest
-from oeqa.utils.decorators import *
-from oeqa.utils.targetbuild import SDKBuildProject
-
-
-class BuildIptablesTest(oeSDKTest):
-
-    @classmethod
-    def setUpClass(self):
-        self.project = SDKBuildProject(oeSDKTest.tc.sdktestdir + "/iptables/", oeSDKTest.tc.sdkenv, oeSDKTest.tc.d,
-                        "http://downloads.yoctoproject.org/mirror/sources/iptables-1.4.13.tar.bz2")
-        self.project.download_archive()
-
-    def test_iptables(self):
-        self.assertEqual(self.project.run_configure(), 0,
-                        msg="Running configure failed")
-
-        self.assertEqual(self.project.run_make(), 0,
-                        msg="Running make failed")
-
-        self.assertEqual(self.project.run_install(), 0,
-                        msg="Running make install failed")
-
-    @classmethod
-    def tearDownClass(self):
-        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/case.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/case.py
new file mode 100644
index 0000000..963aa8d
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/case.py
@@ -0,0 +1,12 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import subprocess
+
+from oeqa.core.case import OETestCase
+
+class OESDKTestCase(OETestCase):
+    def _run(self, cmd):
+        return subprocess.check_output(". %s > /dev/null; %s;" % \
+                (self.tc.sdk_env, cmd), shell=True,
+                stderr=subprocess.STDOUT, universal_newlines=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildcpio.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildcpio.py
new file mode 100644
index 0000000..333dc7c
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildcpio.py
@@ -0,0 +1,33 @@
+import unittest
+from oeqa.sdk.case import OESDKTestCase
+from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject
+
+class BuildCpioTest(OESDKTestCase):
+    td_vars = ['DATETIME']
+
+    @classmethod
+    def setUpClass(self):
+        dl_dir = self.td.get('DL_DIR', None)
+
+        self.project = SDKBuildProject(self.tc.sdk_dir + "/cpio/", self.tc.sdk_env,
+                        "https://ftp.gnu.org/gnu/cpio/cpio-2.12.tar.gz",
+                        self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir)
+        self.project.download_archive()
+
+        machine = self.td.get("MACHINE")
+        if not self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine):
+            raise unittest.SkipTest("SDK doesn't contain a cross-canadian toolchain")
+
+    def test_cpio(self):
+        self.assertEqual(self.project.run_configure(), 0,
+                        msg="Running configure failed")
+
+        self.assertEqual(self.project.run_make(), 0,
+                        msg="Running make failed")
+
+        self.assertEqual(self.project.run_install(), 0,
+                        msg="Running make install failed")
+
+    @classmethod
+    def tearDownClass(self):
+        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildgalculator.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildgalculator.py
new file mode 100644
index 0000000..42e8ddb
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildgalculator.py
@@ -0,0 +1,35 @@
+import unittest
+
+from oeqa.sdk.case import OESDKTestCase
+from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject
+
+class GalculatorTest(OESDKTestCase):
+    td_vars = ['DATETIME']
+
+    @classmethod
+    def setUpClass(self):
+        if not (self.tc.hasTargetPackage("gtk+3") or\
+                self.tc.hasTargetPackage("libgtk-3.0")):
+            raise unittest.SkipTest("GalculatorTest class: SDK don't support gtk+3")
+
+    def test_galculator(self):
+        dl_dir = self.td.get('DL_DIR', None)
+        project = None
+        try:
+            project = SDKBuildProject(self.tc.sdk_dir + "/galculator/",
+                                      self.tc.sdk_env,
+                                      "http://galculator.mnim.org/downloads/galculator-2.1.4.tar.bz2",
+                                      self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir)
+
+            project.download_archive()
+
+            # regenerate configure to get support for --with-libtool-sysroot
+            legacy_preconf=("autoreconf -i -f -I ${OECORE_TARGET_SYSROOT}/usr/share/aclocal -I m4;")
+
+            self.assertEqual(project.run_configure(extra_cmds=legacy_preconf),
+                             0, msg="Running configure failed")
+
+            self.assertEqual(project.run_make(), 0,
+                            msg="Running make failed")
+        finally:
+            project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildlzip.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildlzip.py
new file mode 100644
index 0000000..2a53b78
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/buildlzip.py
@@ -0,0 +1,35 @@
+import unittest
+from oeqa.sdk.case import OESDKTestCase
+from oeqa.sdk.utils.sdkbuildproject import SDKBuildProject
+
+
+class BuildLzipTest(OESDKTestCase):
+    td_vars = ['DATETIME']
+
+    @classmethod
+    def setUpClass(self):
+        dl_dir = self.td.get('DL_DIR', None)
+
+        self.project = SDKBuildProject(self.tc.sdk_dir + "/lzip/", self.tc.sdk_env,
+                        "http://downloads.yoctoproject.org/mirror/sources/lzip-1.19.tar.gz",
+                        self.tc.sdk_dir, self.td['DATETIME'], dl_dir=dl_dir)
+        self.project.download_archive()
+
+        machine = self.td.get("MACHINE")
+
+        if not self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine):
+            raise unittest.SkipTest("SDK doesn't contain a cross-canadian toolchain")
+
+    def test_lzip(self):
+        self.assertEqual(self.project.run_configure(), 0,
+                        msg="Running configure failed")
+
+        self.assertEqual(self.project.run_make(), 0,
+                        msg="Running make failed")
+
+        self.assertEqual(self.project.run_install(), 0,
+                        msg="Running make install failed")
+
+    @classmethod
+    def tearDownClass(self):
+        self.project.clean()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/gcc.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/gcc.py
new file mode 100644
index 0000000..74ad2a2
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/gcc.py
@@ -0,0 +1,42 @@
+import os
+import shutil
+import unittest
+
+from oeqa.core.utils.path import remove_safe
+from oeqa.sdk.case import OESDKTestCase
+
+class GccCompileTest(OESDKTestCase):
+    td_vars = ['MACHINE']
+
+    @classmethod
+    def setUpClass(self):
+        files = {'test.c' : self.tc.files_dir, 'test.cpp' : self.tc.files_dir,
+                'testsdkmakefile' : self.tc.sdk_files_dir} 
+        for f in files:
+            shutil.copyfile(os.path.join(files[f], f),
+                    os.path.join(self.tc.sdk_dir, f))
+
+    def setUp(self):
+        machine = self.td.get("MACHINE")
+        if not self.tc.hasHostPackage("packagegroup-cross-canadian-%s" % machine):
+            raise unittest.SkipTest("GccCompileTest class: SDK doesn't contain a cross-canadian toolchain")
+
+    def test_gcc_compile(self):
+        self._run('$CC %s/test.c -o %s/test -lm' % (self.tc.sdk_dir, self.tc.sdk_dir))
+
+    def test_gpp_compile(self):
+        self._run('$CXX %s/test.c -o %s/test -lm' % (self.tc.sdk_dir, self.tc.sdk_dir))
+
+    def test_gpp2_compile(self):
+        self._run('$CXX %s/test.cpp -o %s/test -lm' % (self.tc.sdk_dir, self.tc.sdk_dir))
+
+    def test_make(self):
+        self._run('cd %s; make -f testsdkmakefile' % self.tc.sdk_dir)
+
+    @classmethod
+    def tearDownClass(self):
+        files = [os.path.join(self.tc.sdk_dir, f) \
+                for f in ['test.c', 'test.cpp', 'test.o', 'test',
+                    'testsdkmakefile']]
+        for f in files:
+            remove_safe(f)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/perl.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/perl.py
new file mode 100644
index 0000000..e1bded2
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/perl.py
@@ -0,0 +1,27 @@
+import os
+import shutil
+import unittest
+
+from oeqa.core.utils.path import remove_safe
+from oeqa.sdk.case import OESDKTestCase
+
+class PerlTest(OESDKTestCase):
+    @classmethod
+    def setUpClass(self):
+        if not self.tc.hasHostPackage("nativesdk-perl"):
+            raise unittest.SkipTest("No perl package in the SDK")
+
+        for f in ['test.pl']:
+            shutil.copyfile(os.path.join(self.tc.files_dir, f),
+                    os.path.join(self.tc.sdk_dir, f))
+        self.testfile = os.path.join(self.tc.sdk_dir, "test.pl")
+
+    def test_perl_exists(self):
+        self._run('which perl')
+
+    def test_perl_works(self):
+        self._run('perl %s' % self.testfile)
+
+    @classmethod
+    def tearDownClass(self):
+        remove_safe(self.testfile)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/python.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/python.py
new file mode 100644
index 0000000..94a296f
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/cases/python.py
@@ -0,0 +1,31 @@
+import os
+import shutil
+import unittest
+
+from oeqa.core.utils.path import remove_safe
+from oeqa.sdk.case import OESDKTestCase
+
+class PythonTest(OESDKTestCase):
+    @classmethod
+    def setUpClass(self):
+        if not self.tc.hasHostPackage("nativesdk-python"):
+            raise unittest.SkipTest("No python package in the SDK")
+
+        for f in ['test.py']:
+            shutil.copyfile(os.path.join(self.tc.files_dir, f),
+                   os.path.join(self.tc.sdk_dir, f))
+
+    def test_python_exists(self):
+        self._run('which python')
+
+    def test_python_stdout(self):
+        output = self._run('python %s/test.py' % self.tc.sdk_dir)
+        self.assertEqual(output.strip(), "the value of a is 0.01", msg="Incorrect output: %s" % output)
+
+    def test_python_testfile(self):
+        self._run('ls /tmp/testfile.python')
+
+    @classmethod
+    def tearDownClass(self):
+        remove_safe("%s/test.py" % self.tc.sdk_dir)
+        remove_safe("/tmp/testfile.python")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/context.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/context.py
new file mode 100644
index 0000000..0189ed8
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/context.py
@@ -0,0 +1,133 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import sys
+import glob
+import re
+
+from oeqa.core.context import OETestContext, OETestContextExecutor
+
+class OESDKTestContext(OETestContext):
+    sdk_files_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files")
+
+    def __init__(self, td=None, logger=None, sdk_dir=None, sdk_env=None,
+            target_pkg_manifest=None, host_pkg_manifest=None):
+        super(OESDKTestContext, self).__init__(td, logger)
+
+        self.sdk_dir = sdk_dir
+        self.sdk_env = sdk_env
+        self.target_pkg_manifest = target_pkg_manifest
+        self.host_pkg_manifest = host_pkg_manifest
+
+    def _hasPackage(self, manifest, pkg):
+        for host_pkg in manifest.keys():
+            if re.search(pkg, host_pkg):
+                return True
+        return False
+
+    def hasHostPackage(self, pkg):
+        return self._hasPackage(self.host_pkg_manifest, pkg)
+
+    def hasTargetPackage(self, pkg):
+        return self._hasPackage(self.target_pkg_manifest, pkg)
+
+class OESDKTestContextExecutor(OETestContextExecutor):
+    _context_class = OESDKTestContext
+
+    name = 'sdk'
+    help = 'sdk test component'
+    description = 'executes sdk tests'
+
+    default_cases = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
+            'cases')]
+    default_test_data = None
+
+    def register_commands(self, logger, subparsers):
+        import argparse_oe
+
+        super(OESDKTestContextExecutor, self).register_commands(logger, subparsers)
+
+        sdk_group = self.parser.add_argument_group('sdk options')
+        sdk_group.add_argument('--sdk-env', action='store',
+            help='sdk environment')
+        sdk_group.add_argument('--target-manifest', action='store',
+            help='sdk target manifest')
+        sdk_group.add_argument('--host-manifest', action='store',
+            help='sdk host manifest')
+
+        sdk_dgroup = self.parser.add_argument_group('sdk display options')
+        sdk_dgroup.add_argument('--list-sdk-env', action='store_true',
+            default=False, help='sdk list available environment')
+
+        # XXX this option is required but argparse_oe has a bug handling
+        # required options, seems that don't keep track of already parsed
+        # options
+        sdk_rgroup = self.parser.add_argument_group('sdk required options')
+        sdk_rgroup.add_argument('--sdk-dir', required=False, action='store', 
+            help='sdk installed directory')
+
+    @staticmethod
+    def _load_manifest(manifest):
+        pkg_manifest = {}
+        if manifest:
+            with open(manifest) as f:
+                for line in f:
+                    (pkg, arch, version) = line.strip().split()
+                    pkg_manifest[pkg] = (version, arch)
+
+        return pkg_manifest
+
+    def _process_args(self, logger, args):
+        super(OESDKTestContextExecutor, self)._process_args(logger, args)
+
+        self.tc_kwargs['init']['sdk_dir'] = args.sdk_dir
+        self.tc_kwargs['init']['sdk_env'] = self.sdk_env
+        self.tc_kwargs['init']['target_pkg_manifest'] = \
+                OESDKTestContextExecutor._load_manifest(args.target_manifest)
+        self.tc_kwargs['init']['host_pkg_manifest'] = \
+                OESDKTestContextExecutor._load_manifest(args.host_manifest)
+
+    @staticmethod
+    def _get_sdk_environs(sdk_dir):
+        sdk_env = {}
+
+        environ_pattern = sdk_dir + '/environment-setup-*'
+        full_sdk_env = glob.glob(sdk_dir + '/environment-setup-*')
+        for env in full_sdk_env:
+            m = re.search('environment-setup-(.*)', env)
+            if m:
+                sdk_env[m.group(1)] = env
+
+        return sdk_env
+
+    def _display_sdk_envs(self, log, args, sdk_envs):
+        log("Available SDK environments at directory %s:" \
+                % args.sdk_dir)
+        log("")
+        for env in sdk_envs:
+            log(env)
+
+    def run(self, logger, args):
+        if not args.sdk_dir:
+            raise argparse_oe.ArgumentUsageError("No SDK directory "\
+                   "specified please do, --sdk-dir SDK_DIR", self.name)
+
+        sdk_envs = OESDKTestContextExecutor._get_sdk_environs(args.sdk_dir)
+        if not sdk_envs:
+            raise argparse_oe.ArgumentUsageError("No available SDK "\
+                   "enviroments found at %s" % args.sdk_dir, self.name)
+
+        if args.list_sdk_env:
+            self._display_sdk_envs(logger.info, args, sdk_envs)
+            sys.exit(0)
+
+        if not args.sdk_env in sdk_envs:
+            self._display_sdk_envs(logger.error, args, sdk_envs)
+            raise argparse_oe.ArgumentUsageError("No valid SDK "\
+                   "environment (%s) specified" % args.sdk_env, self.name)
+
+        self.sdk_env = sdk_envs[args.sdk_env]
+        super(OESDKTestContextExecutor, self).run(logger, args)
+
+_executor_class = OESDKTestContextExecutor
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/runtime/files/testsdkmakefile b/import-layers/yocto-poky/meta/lib/oeqa/sdk/files/testsdkmakefile
similarity index 100%
rename from import-layers/yocto-poky/meta/lib/oeqa/runtime/files/testsdkmakefile
rename to import-layers/yocto-poky/meta/lib/oeqa/sdk/files/testsdkmakefile
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/gcc.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/gcc.py
deleted file mode 100644
index 8395b9b..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/gcc.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import unittest
-import os
-import shutil
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    machine = oeSDKTest.tc.d.getVar("MACHINE", True)
-    if not oeSDKTest.hasHostPackage("packagegroup-cross-canadian-" + machine):
-        skipModule("SDK doesn't contain a cross-canadian toolchain")
-
-
-class GccCompileTest(oeSDKTest):
-
-    @classmethod
-    def setUpClass(self):
-        for f in ['test.c', 'test.cpp', 'testsdkmakefile']:
-            shutil.copyfile(os.path.join(self.tc.filesdir, f), self.tc.sdktestdir + f)
-
-    def test_gcc_compile(self):
-        self._run('$CC %s/test.c -o %s/test -lm' % (self.tc.sdktestdir, self.tc.sdktestdir))
-
-    def test_gpp_compile(self):
-        self._run('$CXX %s/test.c -o %s/test -lm' % (self.tc.sdktestdir, self.tc.sdktestdir))
-
-    def test_gpp2_compile(self):
-        self._run('$CXX %s/test.cpp -o %s/test -lm' % (self.tc.sdktestdir, self.tc.sdktestdir))
-
-    def test_make(self):
-        self._run('cd %s; make -f testsdkmakefile' % self.tc.sdktestdir)
-
-    @classmethod
-    def tearDownClass(self):
-        files = [self.tc.sdktestdir + f for f in ['test.c', 'test.cpp', 'test.o', 'test', 'testsdkmakefile']]
-        for f in files:
-            bb.utils.remove(f)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/perl.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/perl.py
deleted file mode 100644
index 45f422e..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/perl.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import unittest
-import os
-import shutil
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeSDKTest.hasHostPackage("nativesdk-perl"):
-        skipModule("No perl package in the SDK")
-
-
-class PerlTest(oeSDKTest):
-
-    @classmethod
-    def setUpClass(self):
-        for f in ['test.pl']:
-            shutil.copyfile(os.path.join(self.tc.filesdir, f), self.tc.sdktestdir + f)
-        self.testfile = self.tc.sdktestdir + "test.pl"
-
-    def test_perl_exists(self):
-        self._run('which perl')
-
-    def test_perl_works(self):
-        self._run('perl %s/test.pl' % self.tc.sdktestdir)
-
-    @classmethod
-    def tearDownClass(self):
-        bb.utils.remove("%s/test.pl" % self.tc.sdktestdir)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/python.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/python.py
deleted file mode 100644
index 896fab4..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdk/python.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import unittest
-import os
-import shutil
-from oeqa.oetest import oeSDKTest, skipModule
-from oeqa.utils.decorators import *
-
-def setUpModule():
-    if not oeSDKTest.hasHostPackage("nativesdk-python"):
-        skipModule("No python package in the SDK")
-
-
-class PythonTest(oeSDKTest):
-
-    @classmethod
-    def setUpClass(self):
-        for f in ['test.py']:
-            shutil.copyfile(os.path.join(self.tc.filesdir, f), self.tc.sdktestdir + f)
-
-    def test_python_exists(self):
-        self._run('which python')
-
-    def test_python_stdout(self):
-        output = self._run('python %s/test.py' % self.tc.sdktestdir)
-        self.assertEqual(output.strip(), "the value of a is 0.01", msg="Incorrect output: %s" % output)
-
-    def test_python_testfile(self):
-        self._run('ls /tmp/testfile.python')
-
-    @classmethod
-    def tearDownClass(self):
-        bb.utils.remove("%s/test.py" % self.tc.sdktestdir)
-        bb.utils.remove("/tmp/testfile.python")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/sdkbuildproject.py b/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/sdkbuildproject.py
new file mode 100644
index 0000000..4e25114
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdk/utils/sdkbuildproject.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import subprocess
+
+from oeqa.utils.buildproject import BuildProject
+
+class SDKBuildProject(BuildProject):
+    def __init__(self, testpath, sdkenv, uri, testlogdir, builddatetime,
+            foldername=None, dl_dir=None):
+        self.sdkenv = sdkenv
+        self.testdir = testpath
+        self.targetdir = testpath
+        os.makedirs(testpath, exist_ok=True)
+        self.datetime = builddatetime
+        self.testlogdir = testlogdir
+        os.makedirs(self.testlogdir, exist_ok=True)
+        self.logfile = os.path.join(self.testlogdir, "sdk_target_log.%s" % self.datetime)
+        BuildProject.__init__(self, uri, foldername, tmpdir=testpath, dl_dir=dl_dir)
+
+    def download_archive(self):
+
+        self._download_archive()
+
+        cmd = 'tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir)
+        subprocess.check_output(cmd, shell=True)
+
+        #Change targetdir to project folder
+        self.targetdir = os.path.join(self.targetdir, self.fname)
+
+    def run_configure(self, configure_args='', extra_cmds=''):
+        return super(SDKBuildProject, self).run_configure(configure_args=(configure_args or '$CONFIGURE_FLAGS'), extra_cmds=extra_cmds)
+
+    def run_install(self, install_args=''):
+        return super(SDKBuildProject, self).run_install(install_args=(install_args or "DESTDIR=%s/../install" % self.targetdir))
+
+    def log(self, msg):
+        if self.logfile:
+            with open(self.logfile, "a") as f:
+               f.write("%s\n" % msg)
+
+    def _run(self, cmd):
+        self.log("Running . %s; " % self.sdkenv + cmd)
+        return subprocess.call(". %s; " % self.sdkenv + cmd, shell=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/__init__.py
index 4cf3fa7..e69de29 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/__init__.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/__init__.py
@@ -1,3 +0,0 @@
-# Enable other layers to have tests in the same named directory
-from pkgutil import extend_path
-__path__ = extend_path(__path__, __name__)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/case.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/case.py
new file mode 100644
index 0000000..21b7188
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/case.py
@@ -0,0 +1,21 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+import subprocess
+
+from oeqa.utils import avoid_paths_in_environ
+from oeqa.sdk.case import OESDKTestCase
+
+class OESDKExtTestCase(OESDKTestCase):
+    def _run(self, cmd):
+        # extensible sdk shows a warning if found bitbake in the path
+        # because can cause contamination, i.e. use devtool from
+        # poky/scripts instead of eSDK one.
+        env = os.environ.copy()
+        paths_to_avoid = ['bitbake/bin', 'poky/scripts']
+        env['PATH'] = avoid_paths_in_environ(paths_to_avoid)
+
+        return subprocess.check_output(". %s > /dev/null;"\
+            " %s;" % (self.tc.sdk_env, cmd), stderr=subprocess.STDOUT,
+            shell=True, env=env, universal_newlines=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/devtool.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/devtool.py
new file mode 100644
index 0000000..a01bc0b
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/devtool.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import shutil
+import subprocess
+
+from oeqa.sdkext.case import OESDKExtTestCase
+from oeqa.core.decorator.depends import OETestDepends
+from oeqa.core.decorator.oeid import OETestID
+
+class DevtoolTest(OESDKExtTestCase):
+    @classmethod
+    def setUpClass(cls):
+        myapp_src = os.path.join(cls.tc.esdk_files_dir, "myapp")
+        cls.myapp_dst = os.path.join(cls.tc.sdk_dir, "myapp")
+        shutil.copytree(myapp_src, cls.myapp_dst)
+
+        myapp_cmake_src = os.path.join(cls.tc.esdk_files_dir, "myapp_cmake")
+        cls.myapp_cmake_dst = os.path.join(cls.tc.sdk_dir, "myapp_cmake")
+        shutil.copytree(myapp_cmake_src, cls.myapp_cmake_dst)
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.myapp_dst)
+        shutil.rmtree(cls.myapp_cmake_dst)
+
+    def _test_devtool_build(self, directory):
+        self._run('devtool add myapp %s' % directory)
+        try:
+            self._run('devtool build myapp')
+        finally:
+            self._run('devtool reset myapp')
+
+    def _test_devtool_build_package(self, directory):
+        self._run('devtool add myapp %s' % directory)
+        try:
+            self._run('devtool package myapp')
+        finally:
+            self._run('devtool reset myapp')
+
+    def test_devtool_location(self):
+        output = self._run('which devtool')
+        self.assertEqual(output.startswith(self.tc.sdk_dir), True, \
+            msg="Seems that devtool isn't the eSDK one: %s" % output)
+
+    @OETestDepends(['test_devtool_location'])
+    def test_devtool_add_reset(self):
+        self._run('devtool add myapp %s' % self.myapp_dst)
+        self._run('devtool reset myapp')
+
+    @OETestID(1605)
+    @OETestDepends(['test_devtool_location'])
+    def test_devtool_build_make(self):
+        self._test_devtool_build(self.myapp_dst)
+
+    @OETestID(1606)
+    @OETestDepends(['test_devtool_location'])
+    def test_devtool_build_esdk_package(self):
+        self._test_devtool_build_package(self.myapp_dst)
+
+    @OETestID(1607)
+    @OETestDepends(['test_devtool_location'])
+    def test_devtool_build_cmake(self):
+        self._test_devtool_build(self.myapp_cmake_dst)
+
+    @OETestID(1608)
+    @OETestDepends(['test_devtool_location'])
+    def test_extend_autotools_recipe_creation(self):
+        req = 'https://github.com/rdfa/librdfa'
+        recipe = "librdfa"
+        self._run('devtool sdk-install libxml2')
+        self._run('devtool add %s %s' % (recipe, req) )
+        try:
+            self._run('devtool build %s' % recipe)
+        finally:
+            self._run('devtool reset %s' % recipe)
+
+    @OETestID(1609)
+    @OETestDepends(['test_devtool_location'])
+    def test_devtool_kernelmodule(self):
+        docfile = 'https://github.com/umlaeute/v4l2loopback.git'
+        recipe = 'v4l2loopback-driver'
+        self._run('devtool add %s %s' % (recipe, docfile) )
+        try:
+            self._run('devtool build %s' % recipe)
+        finally:
+            self._run('devtool reset %s' % recipe)
+
+    @OETestID(1610)
+    @OETestDepends(['test_devtool_location'])
+    def test_recipes_for_nodejs(self):
+        package_nodejs = "npm://registry.npmjs.org;name=winston;version=2.2.0"
+        self._run('devtool add %s ' % package_nodejs)
+        try:
+            self._run('devtool build %s ' % package_nodejs)
+        finally:
+            self._run('devtool reset %s '% package_nodejs)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/sdk_update.py
similarity index 63%
rename from import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py
rename to import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/sdk_update.py
index 2ade839..2f8598b 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/sdk_update.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/cases/sdk_update.py
@@ -1,23 +1,26 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
 import os
 import shutil
 import subprocess
 
-from oeqa.oetest import oeSDKExtTest
+from oeqa.sdkext.case import OESDKExtTestCase
 from oeqa.utils.httpserver import HTTPService
 
-class SdkUpdateTest(oeSDKExtTest):
-
+class SdkUpdateTest(OESDKExtTestCase):
     @classmethod
     def setUpClass(self):
-        self.publish_dir = os.path.join(self.tc.sdktestdir, 'esdk_publish')
+        self.publish_dir = os.path.join(self.tc.sdk_dir, 'esdk_publish')
         if os.path.exists(self.publish_dir):
             shutil.rmtree(self.publish_dir)
         os.mkdir(self.publish_dir)
 
-        tcname_new = self.tc.d.expand(
-            "${SDK_DEPLOY}/${TOOLCHAINEXT_OUTPUTNAME}-new.sh")
+        base_tcname = "%s/%s" % (self.td.get("SDK_DEPLOY", ''),
+            self.td.get("TOOLCHAINEXT_OUTPUTNAME", ''))
+        tcname_new = "%s-new.sh" % base_tcname
         if not os.path.exists(tcname_new):
-            tcname_new = self.tc.tcname
+            tcname_new = "%s.sh" % base_tcname
 
         cmd = 'oe-publish-sdk %s %s' % (tcname_new, self.publish_dir)
         subprocess.check_output(cmd, shell=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/context.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/context.py
new file mode 100644
index 0000000..65da4c6
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/context.py
@@ -0,0 +1,29 @@
+# Copyright (C) 2016 Intel Corporation
+# Released under the MIT license (see COPYING.MIT)
+
+import os
+from oeqa.sdk.context import OESDKTestContext, OESDKTestContextExecutor
+
+class OESDKExtTestContext(OESDKTestContext):
+    esdk_files_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files")
+
+    # FIXME - We really need to do better mapping of names here, this at
+    # least allows some tests to run
+    def hasHostPackage(self, pkg):
+        # We force a toolchain to be installed into the eSDK even if its minimal
+        if pkg.startswith("packagegroup-cross-canadian-"):
+            return True
+        return self._hasPackage(self.host_pkg_manifest, pkg)
+
+class OESDKExtTestContextExecutor(OESDKTestContextExecutor):
+    _context_class = OESDKExtTestContext
+
+    name = 'esdk'
+    help = 'esdk test component'
+    description = 'executes esdk tests'
+
+    default_cases = OESDKTestContextExecutor.default_cases + \
+            [os.path.join(os.path.abspath(os.path.dirname(__file__)), 'cases')]
+    default_test_data = None
+
+_executor_class = OESDKExtTestContextExecutor
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py b/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py
deleted file mode 100644
index 65f41f6..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/sdkext/devtool.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import shutil
-import subprocess
-import urllib.request
-from oeqa.oetest import oeSDKExtTest
-from oeqa.utils.decorators import *
-
-class DevtoolTest(oeSDKExtTest):
-    @classmethod
-    def setUpClass(self):
-        self.myapp_src = os.path.join(self.tc.sdkextfilesdir, "myapp")
-        self.myapp_dst = os.path.join(self.tc.sdktestdir, "myapp")
-        shutil.copytree(self.myapp_src, self.myapp_dst)
-
-        self.myapp_cmake_src = os.path.join(self.tc.sdkextfilesdir, "myapp_cmake")
-        self.myapp_cmake_dst = os.path.join(self.tc.sdktestdir, "myapp_cmake")
-        shutil.copytree(self.myapp_cmake_src, self.myapp_cmake_dst)
-
-    def _test_devtool_build(self, directory):
-        self._run('devtool add myapp %s' % directory)
-        try:
-            self._run('devtool build myapp')
-        except Exception as e:
-            print(e.output)
-            self._run('devtool reset myapp')
-            raise e
-        self._run('devtool reset myapp')
-
-    def _test_devtool_build_package(self, directory):
-        self._run('devtool add myapp %s' % directory)
-        try:
-            self._run('devtool package myapp')
-        except Exception as e:
-            print(e.output)
-            self._run('devtool reset myapp')
-            raise e
-        self._run('devtool reset myapp')
-
-    def test_devtool_location(self):
-        output = self._run('which devtool')
-        self.assertEqual(output.startswith(self.tc.sdktestdir), True, \
-            msg="Seems that devtool isn't the eSDK one: %s" % output)
-    
-    @skipUnlessPassed('test_devtool_location')
-    def test_devtool_add_reset(self):
-        self._run('devtool add myapp %s' % self.myapp_dst)
-        self._run('devtool reset myapp')
-    
-    @testcase(1473)
-    @skipUnlessPassed('test_devtool_location')
-    def test_devtool_build_make(self):
-        self._test_devtool_build(self.myapp_dst)
-    
-    @testcase(1474)
-    @skipUnlessPassed('test_devtool_location')
-    def test_devtool_build_esdk_package(self):
-        self._test_devtool_build_package(self.myapp_dst)
-
-    @testcase(1479)
-    @skipUnlessPassed('test_devtool_location')
-    def test_devtool_build_cmake(self):
-        self._test_devtool_build(self.myapp_cmake_dst)
-    
-    @testcase(1482)
-    @skipUnlessPassed('test_devtool_location')
-    def test_extend_autotools_recipe_creation(self):
-        req = 'https://github.com/rdfa/librdfa'
-        recipe = "bbexample"
-        self._run('devtool add %s %s' % (recipe, req) )
-        try:
-            self._run('devtool build %s' % recipe)
-        except Exception as e:
-            print(e.output)
-            self._run('devtool reset %s' % recipe)
-            raise e
-        self._run('devtool reset %s' % recipe)
-
-    @testcase(1484)
-    @skipUnlessPassed('test_devtool_location')
-    def test_devtool_kernelmodule(self):
-        docfile = 'https://github.com/umlaeute/v4l2loopback.git'
-        recipe = 'v4l2loopback-driver'
-        self._run('devtool add %s %s' % (recipe, docfile) )
-        try:
-            self._run('devtool build %s' % recipe)
-        except Exception as e:
-            print(e.output)
-            self._run('devtool reset %s' % recipe)
-            raise e
-        self._run('devtool reset %s' % recipe)
-
-    @testcase(1478)
-    @skipUnlessPassed('test_devtool_location')
-    def test_recipes_for_nodejs(self):
-        package_nodejs = "npm://registry.npmjs.org;name=winston;version=2.2.0"
-        self._run('devtool add %s ' % package_nodejs)
-        try:
-            self._run('devtool build %s ' % package_nodejs)
-        except Exception as e:
-            print(e.output)
-            self._run('devtool reset %s' % package_nodejs)
-            raise e
-        self._run('devtool reset %s '% package_nodejs)
-
-
-    @classmethod
-    def tearDownClass(self):
-        shutil.rmtree(self.myapp_dst)
-        shutil.rmtree(self.myapp_cmake_dst)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py
deleted file mode 100644
index 15ea9df..0000000
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/_toaster.py
+++ /dev/null
@@ -1,320 +0,0 @@
-import unittest
-import os
-import sys
-import shlex, subprocess
-import urllib.request, urllib.parse, urllib.error, subprocess, time, getpass, re, json, shlex
-
-import oeqa.utils.ftools as ftools
-from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd
-
-sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../', 'bitbake/lib/toaster')))
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "toastermain.settings")
-
-import toastermain.settings
-from django.db.models import Q
-from orm.models import *
-from oeqa.utils.decorators import testcase
-
-class ToasterSetup(oeSelfTest):
-
-    def recipe_parse(self, file_path, var):
-        for line in open(file_path,'r'):
-            if line.find(var) > -1:
-                val = line.split(" = ")[1].replace("\"", "").strip()
-                return val
-
-    def fix_file_path(self, file_path):
-        if ":" in file_path:
-            file_path=file_path.split(":")[2]
-        return file_path
-
-class Toaster_DB_Tests(ToasterSetup):
-
-    # Check if build name is unique - tc_id=795
-    @testcase(795)
-    def test_Build_Unique_Name(self):
-        all_builds = Build.objects.all().count()
-        distinct_builds = Build.objects.values('id').distinct().count()
-        self.assertEqual(distinct_builds, all_builds, msg = 'Build name is not unique')
-
-    # Check if build coocker log path is unique - tc_id=819
-    @testcase(819)
-    def test_Build_Unique_Cooker_Log_Path(self):
-        distinct_path = Build.objects.values('cooker_log_path').distinct().count()
-        total_builds = Build.objects.values('id').count()
-        self.assertEqual(distinct_path, total_builds, msg = 'Build coocker log path is not unique')
-
-    # Check if task order is unique for one build - tc=824
-    @testcase(824)
-    def test_Task_Unique_Order(self):
-        builds = Build.objects.values('id')
-        cnt_err = []
-        for build in builds:
-            total_task_order = Task.objects.filter(build = build['id']).values('order').count()
-            distinct_task_order = Task.objects.filter(build = build['id']).values('order').distinct().count()
-            if (total_task_order != distinct_task_order):
-                cnt_err.append(build['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for build id: %s' % cnt_err)
-
-    # Check task order sequence for one build - tc=825
-    @testcase(825)
-    def test_Task_Order_Sequence(self):
-        builds = builds = Build.objects.values('id')
-        cnt_err = []
-        for build in builds:
-            tasks = Task.objects.filter(Q(build = build['id']), ~Q(order = None), ~Q(task_name__contains = '_setscene')).values('id', 'order').order_by("order")
-            cnt_tasks = 0
-            for task in tasks:
-                cnt_tasks += 1
-                if (task['order'] != cnt_tasks):
-                    cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if disk_io matches the difference between EndTimeIO and StartTimeIO in build stats - tc=828
-    ### this needs to be updated ###
-    #def test_Task_Disk_IO_TC828(self):
-
-    # Check if outcome = 2 (SSTATE) then sstate_result must be 3 (RESTORED) - tc=832
-    @testcase(832)
-    def test_Task_If_Outcome_2_Sstate_Result_Must_Be_3(self):
-        tasks = Task.objects.filter(outcome = 2).values('id', 'sstate_result')
-        cnt_err = []
-        for task in tasks:
-            if (row['sstate_result'] != 3):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if outcome = 1 (COVERED) or 3 (EXISTING) then sstate_result must be 0 (SSTATE_NA) - tc=833
-    @testcase(833)
-    def test_Task_If_Outcome_1_3_Sstate_Result_Must_Be_0(self):
-        tasks = Task.objects.filter(outcome__in = (1, 3)).values('id', 'sstate_result')
-        cnt_err = []
-        for task in tasks:
-            if (task['sstate_result'] != 0):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if outcome is 0 (SUCCESS) or 4 (FAILED) then sstate_result must be 0 (NA), 1 (MISS) or 2 (FAILED) - tc=834
-    @testcase(834)
-    def test_Task_If_Outcome_0_4_Sstate_Result_Must_Be_0_1_2(self):
-        tasks = Task.objects.filter(outcome__in = (0, 4)).values('id', 'sstate_result')
-        cnt_err = []
-        for task in tasks:
-            if (task['sstate_result'] not in [0, 1, 2]):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if task_executed = TRUE (1), script_type must be 0 (CODING_NA), 2 (CODING_PYTHON), 3 (CODING_SHELL) - tc=891
-    @testcase(891)
-    def test_Task_If_Task_Executed_True_Script_Type_0_2_3(self):
-        tasks = Task.objects.filter(task_executed = 1).values('id', 'script_type')
-        cnt_err = []
-        for task in tasks:
-            if (task['script_type'] not in [0, 2, 3]):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if task_executed = TRUE (1), outcome must be 0 (SUCCESS) or 4 (FAILED) - tc=836
-    @testcase(836)
-    def test_Task_If_Task_Executed_True_Outcome_0_4(self):
-        tasks = Task.objects.filter(task_executed = 1).values('id', 'outcome')
-        cnt_err = []
-        for task in tasks:
-            if (task['outcome'] not in [0, 4]):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if task_executed = FALSE (0), script_type must be 0 - tc=890
-    @testcase(890)
-    def test_Task_If_Task_Executed_False_Script_Type_0(self):
-        tasks = Task.objects.filter(task_executed = 0).values('id', 'script_type')
-        cnt_err = []
-        for task in tasks:
-            if (task['script_type'] != 0):
-                cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Check if task_executed = FALSE (0) and build outcome = SUCCEEDED (0), task outcome must be 1 (COVERED), 2 (CACHED), 3 (PREBUILT), 5 (EMPTY) - tc=837
-    @testcase(837)
-    def test_Task_If_Task_Executed_False_Outcome_1_2_3_5(self):
-        builds = Build.objects.filter(outcome = 0).values('id')
-        cnt_err = []
-        for build in builds:
-            tasks = Task.objects.filter(build = build['id'], task_executed = 0).values('id', 'outcome')
-            for task in tasks:
-                if (task['outcome'] not in [1, 2, 3, 5]):
-                    cnt_err.append(task['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task id: %s' % cnt_err)
-
-    # Key verification - tc=888
-    @testcase(888)
-    def test_Target_Installed_Package(self):
-        rows = Target_Installed_Package.objects.values('id', 'target_id', 'package_id')
-        cnt_err = []
-        for row in rows:
-            target = Target.objects.filter(id = row['target_id']).values('id')
-            package = Package.objects.filter(id = row['package_id']).values('id')
-            if (not target or not package):
-                cnt_err.append(row['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for target installed package id: %s' % cnt_err)
-
-    # Key verification - tc=889
-    @testcase(889)
-    def test_Task_Dependency(self):
-        rows = Task_Dependency.objects.values('id', 'task_id', 'depends_on_id')
-        cnt_err = []
-        for row in rows:
-            task_id = Task.objects.filter(id = row['task_id']).values('id')
-            depends_on_id = Task.objects.filter(id = row['depends_on_id']).values('id')
-            if (not task_id or not depends_on_id):
-                cnt_err.append(row['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for task dependency id: %s' % cnt_err)
-
-    # Check if build target file_name is populated only if is_image=true AND orm_build.outcome=0 then if the file exists and its size matches the file_size value
-    ### Need to add the tc in the test run
-    @testcase(1037)
-    def test_Target_File_Name_Populated(self):
-        builds = Build.objects.filter(outcome = 0).values('id')
-        for build in builds:
-            targets = Target.objects.filter(build_id = build['id'], is_image = 1).values('id')
-            for target in targets:
-                target_files = Target_Image_File.objects.filter(target_id = target['id']).values('id', 'file_name', 'file_size')
-                cnt_err = []
-                for file_info in target_files:
-                    target_id = file_info['id']
-                    target_file_name = file_info['file_name']
-                    target_file_size = file_info['file_size']
-                    if (not target_file_name or not target_file_size):
-                        cnt_err.append(target_id)
-                    else:
-                        if (not os.path.exists(target_file_name)):
-                            cnt_err.append(target_id)
-                        else:
-                            if (os.path.getsize(target_file_name) != target_file_size):
-                                cnt_err.append(target_id)
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for target image file id: %s' % cnt_err)
-
-    # Key verification - tc=884
-    @testcase(884)
-    def test_Package_Dependency(self):
-        cnt_err = []
-        deps = Package_Dependency.objects.values('id', 'package_id', 'depends_on_id')
-        for dep in deps:
-            if (dep['package_id'] == dep['depends_on_id']):
-                cnt_err.append(dep['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package dependency id: %s' % cnt_err)
-
-    # Recipe key verification, recipe name does not depends on a recipe having the same name - tc=883
-    @testcase(883)
-    def test_Recipe_Dependency(self):
-        deps = Recipe_Dependency.objects.values('id', 'recipe_id', 'depends_on_id')
-        cnt_err = []
-        for dep in deps:
-            if (not dep['recipe_id'] or not dep['depends_on_id']):
-                cnt_err.append(dep['id'])
-            else:
-                name = Recipe.objects.filter(id = dep['recipe_id']).values('name')
-                dep_name = Recipe.objects.filter(id = dep['depends_on_id']).values('name')
-                if (name == dep_name):
-                    cnt_err.append(dep['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for recipe dependency id: %s' % cnt_err)
-
-    # Check if package name does not start with a number (0-9) - tc=846
-    @testcase(846)
-    def test_Package_Name_For_Number(self):
-        packages = Package.objects.filter(~Q(size = -1)).values('id', 'name')
-        cnt_err = []
-        for package in packages:
-            if (package['name'][0].isdigit() is True):
-                cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check if package version starts with a number (0-9) - tc=847
-    @testcase(847)
-    def test_Package_Version_Starts_With_Number(self):
-        packages = Package.objects.filter(~Q(size = -1)).values('id', 'version')
-        cnt_err = []
-        for package in packages:
-            if (package['version'][0].isdigit() is False):
-                cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check if package revision starts with 'r' - tc=848
-    @testcase(848)
-    def test_Package_Revision_Starts_With_r(self):
-        packages = Package.objects.filter(~Q(size = -1)).values('id', 'revision')
-        cnt_err = []
-        for package in packages:
-            if (package['revision'][0].startswith("r") is False):
-                cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check the validity of the package build_id
-    ### TC must be added in test run
-    @testcase(1038)
-    def test_Package_Build_Id(self):
-        packages = Package.objects.filter(~Q(size = -1)).values('id', 'build_id')
-        cnt_err = []
-        for package in packages:
-            build_id = Build.objects.filter(id = package['build_id']).values('id')
-            if (not build_id):
-                cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check the validity of package recipe_id
-    ### TC must be added in test run
-    @testcase(1039)
-    def test_Package_Recipe_Id(self):
-        packages = Package.objects.filter(~Q(size = -1)).values('id', 'recipe_id')
-        cnt_err = []
-        for package in packages:
-            recipe_id = Recipe.objects.filter(id = package['recipe_id']).values('id')
-            if (not recipe_id):
-                cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check if package installed_size field is not null
-    ### TC must be aded in test run
-    @testcase(1040)
-    def test_Package_Installed_Size_Not_NULL(self):
-        packages = Package.objects.filter(installed_size__isnull = True).values('id')
-        cnt_err = []
-        for package in packages:
-            cnt_err.append(package['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for package id: %s' % cnt_err)
-
-    # Check if all layers requests return exit code is 200 - tc=843
-    @testcase(843)
-    def test_Layers_Requests_Exit_Code(self):
-        layers = Layer.objects.values('id', 'layer_index_url')
-        cnt_err = []
-        for layer in layers:
-            resp = urllib.request.urlopen(layer['layer_index_url'])
-            if (resp.getcode() != 200):
-                cnt_err.append(layer['id'])
-        self.assertEqual(len(cnt_err), 0, msg = 'Errors for layer id: %s' % cnt_err)
-
-    # Check if django server starts regardless of the timezone set on the machine - tc=905
-    @testcase(905)
-    def test_Start_Django_Timezone(self):
-        current_path = os.getcwd()
-        zonefilelist = []
-        ZONEINFOPATH = '/usr/share/zoneinfo/'
-        os.chdir("../bitbake/lib/toaster/")
-        cnt_err = 0
-        for filename in os.listdir(ZONEINFOPATH):
-            if os.path.isfile(os.path.join(ZONEINFOPATH, filename)):
-                zonefilelist.append(filename)
-        for k in range(len(zonefilelist)):
-            if k <= 5:
-                files = zonefilelist[k]
-                os.system("export TZ="+str(files)+"; python manage.py runserver > /dev/null 2>&1 &")
-                time.sleep(3)
-                pid = subprocess.check_output("ps aux | grep '[/u]sr/bin/python manage.py runserver' | awk '{print $2}'", shell = True)
-                if pid:
-                    os.system("kill -9 "+str(pid))
-                else:
-                    cnt_err.append(zonefilelist[k])
-        self.assertEqual(cnt_err, 0, msg = 'Errors django server does not start with timezone: %s' % cnt_err)
-        os.chdir(current_path)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/archiver.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/archiver.py
index f2030c4..7f01c36 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/archiver.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/archiver.py
@@ -1,5 +1,5 @@
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import bitbake, get_bb_var
+from oeqa.utils.commands import bitbake, get_bb_vars
 from oeqa.utils.decorators import testcase
 import glob
 import os
@@ -26,25 +26,94 @@
         features += 'ARCHIVER_MODE[src] = "original"\n'
         features += 'COPYLEFT_PN_INCLUDE = "%s"\n' % include_recipe
         features += 'COPYLEFT_PN_EXCLUDE = "%s"\n' % exclude_recipe
-
-        # Update local.conf
         self.write_config(features)
 
-        tmp_dir = get_bb_var('TMPDIR')
-        deploy_dir_src = get_bb_var('DEPLOY_DIR_SRC')
-        target_sys = get_bb_var('TARGET_SYS')
-        src_path = os.path.join(deploy_dir_src, target_sys)
+        bitbake('-c clean %s %s' % (include_recipe, exclude_recipe))
+        bitbake("-c deploy_archives %s %s" % (include_recipe, exclude_recipe))
 
-        # Delete tmp directory
-        shutil.rmtree(tmp_dir)
-
-        # Build core-image-minimal
-        bitbake('core-image-minimal')
+        bb_vars = get_bb_vars(['DEPLOY_DIR_SRC', 'TARGET_SYS'])
+        src_path = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['TARGET_SYS'])
 
         # Check that include_recipe was included
-        is_included = len(glob.glob(src_path + '/%s*' % include_recipe))
-        self.assertEqual(1, is_included, 'Recipe %s was not included.' % include_recipe)
+        included_present = len(glob.glob(src_path + '/%s-*' % include_recipe))
+        self.assertTrue(included_present, 'Recipe %s was not included.' % include_recipe)
 
         # Check that exclude_recipe was excluded
-        is_excluded = len(glob.glob(src_path + '/%s*' % exclude_recipe))
-        self.assertEqual(0, is_excluded, 'Recipe %s was not excluded.' % exclude_recipe)
+        excluded_present = len(glob.glob(src_path + '/%s-*' % exclude_recipe))
+        self.assertFalse(excluded_present, 'Recipe %s was not excluded.' % exclude_recipe)
+
+
+    def test_archiver_filters_by_type(self):
+        """
+        Summary:     The archiver is documented to filter on the recipe type.
+        Expected:    1. included recipe type (target) should be included
+                     2. other types should be excluded
+        Product:     oe-core
+        Author:      André Draszik <adraszik@tycoint.com>
+        """
+
+        target_recipe = 'initscripts'
+        native_recipe = 'zlib-native'
+
+        features = 'INHERIT += "archiver"\n'
+        features += 'ARCHIVER_MODE[src] = "original"\n'
+        features += 'COPYLEFT_RECIPE_TYPES = "target"\n'
+        self.write_config(features)
+
+        bitbake('-c clean %s %s' % (target_recipe, native_recipe))
+        bitbake("%s -c deploy_archives %s" % (target_recipe, native_recipe))
+
+        bb_vars = get_bb_vars(['DEPLOY_DIR_SRC', 'TARGET_SYS', 'BUILD_SYS'])
+        src_path_target = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['TARGET_SYS'])
+        src_path_native = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['BUILD_SYS'])
+
+        # Check that target_recipe was included
+        included_present = len(glob.glob(src_path_target + '/%s-*' % target_recipe))
+        self.assertTrue(included_present, 'Recipe %s was not included.' % target_recipe)
+
+        # Check that native_recipe was excluded
+        excluded_present = len(glob.glob(src_path_native + '/%s-*' % native_recipe))
+        self.assertFalse(excluded_present, 'Recipe %s was not excluded.' % native_recipe)
+
+    def test_archiver_filters_by_type_and_name(self):
+        """
+        Summary:     Test that the archiver archives by recipe type, taking the
+                     recipe name into account.
+        Expected:    1. included recipe type (target) should be included
+                     2. other types should be excluded
+                     3. recipe by name should be included / excluded,
+                        overriding previous decision by type
+        Product:     oe-core
+        Author:      André Draszik <adraszik@tycoint.com>
+        """
+
+        target_recipes = [ 'initscripts', 'zlib' ]
+        native_recipes = [ 'update-rc.d-native', 'zlib-native' ]
+
+        features = 'INHERIT += "archiver"\n'
+        features += 'ARCHIVER_MODE[src] = "original"\n'
+        features += 'COPYLEFT_RECIPE_TYPES = "target"\n'
+        features += 'COPYLEFT_PN_INCLUDE = "%s"\n' % native_recipes[1]
+        features += 'COPYLEFT_PN_EXCLUDE = "%s"\n' % target_recipes[1]
+        self.write_config(features)
+
+        bitbake('-c clean %s %s' % (' '.join(target_recipes), ' '.join(native_recipes)))
+        bitbake('-c deploy_archives %s %s' % (' '.join(target_recipes), ' '.join(native_recipes)))
+
+        bb_vars = get_bb_vars(['DEPLOY_DIR_SRC', 'TARGET_SYS', 'BUILD_SYS'])
+        src_path_target = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['TARGET_SYS'])
+        src_path_native = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['BUILD_SYS'])
+
+        # Check that target_recipe[0] and native_recipes[1] were included
+        included_present = len(glob.glob(src_path_target + '/%s-*' % target_recipes[0]))
+        self.assertTrue(included_present, 'Recipe %s was not included.' % target_recipes[0])
+
+        included_present = len(glob.glob(src_path_native + '/%s-*' % native_recipes[1]))
+        self.assertTrue(included_present, 'Recipe %s was not included.' % native_recipes[1])
+
+        # Check that native_recipes[0] and target_recipes[1] were excluded
+        excluded_present = len(glob.glob(src_path_native + '/%s-*' % native_recipes[0]))
+        self.assertFalse(excluded_present, 'Recipe %s was not excluded.' % native_recipes[0])
+
+        excluded_present = len(glob.glob(src_path_target + '/%s-*' % target_recipes[1]))
+        self.assertFalse(excluded_present, 'Recipe %s was not excluded.' % target_recipes[1])
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
index 26c93f9..47a8ea8 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/base.py
@@ -163,7 +163,7 @@
 
     # remove data from <builddir>/conf/selftest.inc
     def remove_config(self, data):
-        self.log.debug("Removing from: %s\n\%s\n" % (self.testinc_path, data))
+        self.log.debug("Removing from: %s\n%s\n" % (self.testinc_path, data))
         ftools.remove_from_file(self.testinc_path, data)
 
     # write to meta-sefltest/recipes-test/<recipe>/test_recipe.inc
@@ -206,7 +206,7 @@
 
     # remove data from <builddir>/conf/bblayers.inc
     def remove_bblayers_config(self, data):
-        self.log.debug("Removing from: %s\n\%s\n" % (self.testinc_bblayers_path, data))
+        self.log.debug("Removing from: %s\n%s\n" % (self.testinc_bblayers_path, data))
         ftools.remove_from_file(self.testinc_bblayers_path, data)
 
     # write to <builddir>/conf/machine.inc
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bblayers.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bblayers.py
index d23675e..cd658c5 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bblayers.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bblayers.py
@@ -71,17 +71,12 @@
         result = runCmd('bitbake-layers show-recipes')
         self.assertIn('aspell:', result.output)
         self.assertIn('mtd-utils:', result.output)
-        self.assertIn('linux-yocto:', result.output)
         self.assertIn('core-image-minimal:', result.output)
         result = runCmd('bitbake-layers show-recipes mtd-utils')
         self.assertIn('mtd-utils:', result.output)
         self.assertNotIn('aspell:', result.output)
-        result = runCmd('bitbake-layers show-recipes -i kernel')
-        self.assertIn('linux-yocto:', result.output)
-        self.assertNotIn('mtd-utils:', result.output)
         result = runCmd('bitbake-layers show-recipes -i image')
         self.assertIn('core-image-minimal', result.output)
-        self.assertNotIn('linux-yocto:', result.output)
         self.assertNotIn('mtd-utils:', result.output)
         result = runCmd('bitbake-layers show-recipes -i cmake,pkgconfig')
         self.assertIn('libproxy:', result.output)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
index 4ce935f..46e09f5 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/bbtests.py
@@ -3,7 +3,7 @@
 
 import oeqa.utils.ftools as ftools
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
 from oeqa.utils.decorators import testcase
 
 class BitbakeTests(oeSelfTest):
@@ -78,9 +78,10 @@
         # test 1 from bug 5875
         test_recipe = 'zlib'
         test_data = "Microsoft Made No Profit From Anyone's Zunes Yo"
-        image_dir = get_bb_var('D', test_recipe)
-        pkgsplit_dir = get_bb_var('PKGDEST', test_recipe)
-        man_dir = get_bb_var('mandir', test_recipe)
+        bb_vars = get_bb_vars(['D', 'PKGDEST', 'mandir'], test_recipe)
+        image_dir = bb_vars['D']
+        pkgsplit_dir = bb_vars['PKGDEST']
+        man_dir = bb_vars['mandir']
 
         bitbake('-c clean %s' % test_recipe)
         bitbake('-c package -f %s' % test_recipe)
@@ -112,17 +113,18 @@
 
     @testcase(167)
     def test_bitbake_g(self):
-        result = bitbake('-g core-image-full-cmdline')
-        for f in ['pn-buildlist', 'pn-depends.dot', 'package-depends.dot', 'task-depends.dot']:
+        result = bitbake('-g core-image-minimal')
+        for f in ['pn-buildlist', 'recipe-depends.dot', 'task-depends.dot']:
             self.addCleanup(os.remove, f)
-        self.assertTrue('NOTE: PN build list saved to \'pn-buildlist\'' in result.output, msg = "No dependency \"pn-buildlist\" file was generated for the given task target. bitbake output: %s" % result.output)
-        self.assertTrue('openssh' in ftools.read_file(os.path.join(self.builddir, 'pn-buildlist')), msg = "No \"openssh\" dependency found in pn-buildlist file.")
+        self.assertTrue('Task dependencies saved to \'task-depends.dot\'' in result.output, msg = "No task dependency \"task-depends.dot\" file was generated for the given task target. bitbake output: %s" % result.output)
+        self.assertTrue('busybox' in ftools.read_file(os.path.join(self.builddir, 'task-depends.dot')), msg = "No \"busybox\" dependency found in task-depends.dot file.")
 
     @testcase(899)
     def test_image_manifest(self):
         bitbake('core-image-minimal')
-        deploydir = get_bb_var("DEPLOY_DIR_IMAGE", target="core-image-minimal")
-        imagename = get_bb_var("IMAGE_LINK_NAME", target="core-image-minimal")
+        bb_vars = get_bb_vars(["DEPLOY_DIR_IMAGE", "IMAGE_LINK_NAME"], "core-image-minimal")
+        deploydir = bb_vars["DEPLOY_DIR_IMAGE"]
+        imagename = bb_vars["IMAGE_LINK_NAME"]
         manifest = os.path.join(deploydir, imagename + ".manifest")
         self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image. It should have been created in %s" % manifest)
 
@@ -149,19 +151,21 @@
 
     @testcase(171)
     def test_rename_downloaded_file(self):
+        # TODO unique dldir instead of using cleanall
+        # TODO: need to set sstatedir?
         self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
 SSTATE_DIR = \"${TOPDIR}/download-selftest\"
 """)
         self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
 
-        data = 'SRC_URI_append = ";downloadfilename=test-aspell.tar.gz"'
+        data = 'SRC_URI = "${GNU_MIRROR}/aspell/aspell-${PV}.tar.gz;downloadfilename=test-aspell.tar.gz"'
         self.write_recipeinc('aspell', data)
-        bitbake('-ccleanall aspell')
-        result = bitbake('-c fetch aspell', ignore_status=True)
+        result = bitbake('-f -c fetch aspell', ignore_status=True)
         self.delete_recipeinc('aspell')
         self.assertEqual(result.status, 0, msg = "Couldn't fetch aspell. %s" % result.output)
-        self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz')), msg = "File rename failed. No corresponding test-aspell.tar.gz file found under %s" % str(get_bb_var("DL_DIR")))
-        self.assertTrue(os.path.isfile(os.path.join(get_bb_var("DL_DIR"), 'test-aspell.tar.gz.done')), "File rename failed. No corresponding test-aspell.tar.gz.done file found under %s" % str(get_bb_var("DL_DIR")))
+        dl_dir = get_bb_var("DL_DIR")
+        self.assertTrue(os.path.isfile(os.path.join(dl_dir, 'test-aspell.tar.gz')), msg = "File rename failed. No corresponding test-aspell.tar.gz file found under %s" % dl_dir)
+        self.assertTrue(os.path.isfile(os.path.join(dl_dir, 'test-aspell.tar.gz.done')), "File rename failed. No corresponding test-aspell.tar.gz.done file found under %s" % dl_dir)
 
     @testcase(1028)
     def test_environment(self):
@@ -227,14 +231,12 @@
 
     @testcase(1119)
     def test_non_gplv3(self):
-        data = 'INCOMPATIBLE_LICENSE = "GPLv3"'
-        conf = os.path.join(self.builddir, 'conf/local.conf')
-        ftools.append_file(conf ,data)
-        self.addCleanup(ftools.remove_from_file, conf ,data)
-        result = bitbake('readline', ignore_status=True)
+        self.write_config('INCOMPATIBLE_LICENSE = "GPLv3"')
+        result = bitbake('selftest-ed', ignore_status=True)
         self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
-        self.assertFalse(os.path.isfile(os.path.join(self.builddir, 'tmp/deploy/licenses/readline/generic_GPLv3')))
-        self.assertTrue(os.path.isfile(os.path.join(self.builddir, 'tmp/deploy/licenses/readline/generic_GPLv2')))
+        lic_dir = get_bb_var('LICENSE_DIRECTORY')
+        self.assertFalse(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv3')))
+        self.assertTrue(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv2')))
 
     @testcase(1422)
     def test_setscene_only(self):
@@ -255,8 +257,9 @@
     def test_bbappend_order(self):
         """ Bitbake should bbappend to recipe in a predictable order """
         test_recipe = 'ed'
-        test_recipe_summary_before = get_bb_var('SUMMARY', test_recipe)
-        test_recipe_pv = get_bb_var('PV', test_recipe)
+        bb_vars = get_bb_vars(['SUMMARY', 'PV'], test_recipe)
+        test_recipe_summary_before = bb_vars['SUMMARY']
+        test_recipe_pv = bb_vars['PV']
         recipe_append_file = test_recipe + '_' + test_recipe_pv + '.bbappend'
         expected_recipe_summary = test_recipe_summary_before
 
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildhistory.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildhistory.py
index 674da62..008c39c 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildhistory.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildhistory.py
@@ -3,14 +3,15 @@
 import datetime
 
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import bitbake, get_bb_var
+from oeqa.utils.commands import bitbake, get_bb_vars
 from oeqa.utils.decorators import testcase
 
 
 class BuildhistoryBase(oeSelfTest):
 
     def config_buildhistory(self, tmp_bh_location=False):
-        if (not 'buildhistory' in get_bb_var('USER_CLASSES')) and (not 'buildhistory' in get_bb_var('INHERIT')):
+        bb_vars = get_bb_vars(['USER_CLASSES', 'INHERIT'])
+        if (not 'buildhistory' in bb_vars['USER_CLASSES']) and (not 'buildhistory' in bb_vars['INHERIT']):
             add_buildhistory_config = 'INHERIT += "buildhistory"\nBUILDHISTORY_COMMIT = "1"'
             self.append_config(add_buildhistory_config)
 
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
index 4754955..a6e0203 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/buildoptions.py
@@ -5,7 +5,7 @@
 import tempfile
 from oeqa.selftest.base import oeSelfTest
 from oeqa.selftest.buildhistory import BuildhistoryBase
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
 import oeqa.utils.ftools as ftools
 from oeqa.utils.decorators import testcase
 
@@ -16,32 +16,38 @@
         image_pkgtype = get_bb_var("IMAGE_PKGTYPE")
         if image_pkgtype != 'rpm':
             self.skipTest('Not using RPM as main package format')
-        bitbake("-c cleanall core-image-minimal")
+        bitbake("-c clean core-image-minimal")
         self.write_config('INC_RPM_IMAGE_GEN = "1"')
         self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
         bitbake("core-image-minimal")
         log_data_file = os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")
         log_data_created = ftools.read_file(log_data_file)
-        incremental_created = re.search("NOTE: load old install solution for incremental install\nNOTE: old install solution not exist\nNOTE: creating new install solution for incremental install(\n.*)*NOTE: Installing the following packages:.*packagegroup-core-ssh-openssh", log_data_created)
+        incremental_created = re.search("Installing  : packagegroup-core-ssh-openssh", log_data_created)
         self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
         self.assertTrue(incremental_created, msg = "Match failed in:\n%s" % log_data_created)
         bitbake("core-image-minimal")
         log_data_removed = ftools.read_file(log_data_file)
-        incremental_removed = re.search("NOTE: load old install solution for incremental install\nNOTE: creating new install solution for incremental install(\n.*)*NOTE: incremental removed:.*openssh-sshd-.*", log_data_removed)
+        incremental_removed = re.search("Erasing     : packagegroup-core-ssh-openssh", log_data_removed)
         self.assertTrue(incremental_removed, msg = "Match failed in:\n%s" % log_data_removed)
 
     @testcase(286)
     def test_ccache_tool(self):
         bitbake("ccache-native")
-        self.assertTrue(os.path.isfile(os.path.join(get_bb_var('STAGING_BINDIR_NATIVE', 'ccache-native'), "ccache")), msg = "No ccache found under %s" % str(get_bb_var('STAGING_BINDIR_NATIVE', 'ccache-native')))
+        bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'ccache-native')
+        p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "ccache"
+        self.assertTrue(os.path.isfile(p), msg = "No ccache found (%s)" % p)
         self.write_config('INHERIT += "ccache"')
         self.add_command_to_tearDown('bitbake -c clean m4')
         bitbake("m4 -f -c compile")
-        res = runCmd("grep ccache %s" % (os.path.join(get_bb_var("WORKDIR","m4"),"temp/log.do_compile")), ignore_status=True)
-        self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile. For further details: %s" % os.path.join(get_bb_var("WORKDIR","m4"),"temp/log.do_compile"))
+        log_compile = os.path.join(get_bb_var("WORKDIR","m4"), "temp/log.do_compile")
+        res = runCmd("grep ccache %s" % log_compile, ignore_status=True)
+        self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile. For further details: %s" % log_compile)
 
     @testcase(1435)
     def test_read_only_image(self):
+        distro_features = get_bb_var('DISTRO_FEATURES')
+        if not ('x11' in distro_features and 'opengl' in distro_features):
+            self.skipTest('core-image-sato requires x11 and opengl in distro features')
         self.write_config('IMAGE_FEATURES += "read-only-rootfs"')
         bitbake("core-image-sato")
         # do_image will fail if there are any pending postinsts
@@ -157,7 +163,6 @@
 
     @testcase(294)
     def test_buildhistory_buildtime_pr_backwards(self):
-        self.add_command_to_tearDown('cleanup-workdir')
         target = 'xcursor-transparent-theme'
         error = "ERROR:.*QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1.* to .*-r0.*)" % target
         self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
@@ -169,11 +174,11 @@
         """
         Test for archiving the work directory and exporting the source files.
         """
-        self.add_command_to_tearDown('cleanup-workdir')
         self.write_config("INHERIT += \"archiver\"\nARCHIVER_MODE[src] = \"original\"\nARCHIVER_MODE[srpm] = \"1\"")
         res = bitbake("xcursor-transparent-theme", ignore_status=True)
         self.assertEqual(res.status, 0, "\nCouldn't build xcursortransparenttheme.\nbitbake output %s" % res.output)
-        pkgs_path = g.glob(str(self.builddir) + "/tmp/deploy/sources/allarch*/xcurs*")
+        deploy_dir_src = get_bb_var('DEPLOY_DIR_SRC')
+        pkgs_path = g.glob(str(deploy_dir_src) + "/allarch*/xcurs*")
         src_file_glob = str(pkgs_path[0]) + "/xcursor*.src.rpm"
         tar_file_glob = str(pkgs_path[0]) + "/xcursor*.tar.gz"
-        self.assertTrue((g.glob(src_file_glob) and g.glob(tar_file_glob)), "Couldn't find .src.rpm and .tar.gz files under tmp/deploy/sources/allarch*/xcursor*")
+        self.assertTrue((g.glob(src_file_glob) and g.glob(tar_file_glob)), "Couldn't find .src.rpm and .tar.gz files under %s/allarch*/xcursor*" % deploy_dir_src)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/containerimage.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/containerimage.py
new file mode 100644
index 0000000..def481f
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/containerimage.py
@@ -0,0 +1,83 @@
+import os
+
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import bitbake, get_bb_vars, runCmd
+
+# This test builds an image with using the "container" IMAGE_FSTYPE, and
+# ensures that then files in the image are only the ones expected.
+#
+# The only package added to the image is container_image_testpkg, which
+# contains one file. However, due to some other things not cleaning up during
+# rootfs creation, there is some cruft. Ideally bugs will be filed and the
+# cruft removed, but for now we whitelist some known set.
+#
+# Also for performance reasons we're only checking the cruft when using ipk.
+# When using deb, and rpm it is a bit different and we could test all
+# of them, but this test is more to catch if other packages get added by
+# default other than what is in ROOTFS_BOOTSTRAP_INSTALL.
+#
+class ContainerImageTests(oeSelfTest):
+
+    # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
+    # the conversion type bar gets added as a dep as well
+    def test_expected_files(self):
+
+        def get_each_path_part(path):
+            if path:
+                part = [ '.' + path + '/' ]
+                result = get_each_path_part(path.rsplit('/', 1)[0])
+                if result:
+                    return part + result
+                else:
+                    return part
+            else:
+                return None
+
+        self.write_config("""PREFERRED_PROVIDER_virtual/kernel = "linux-dummy"
+IMAGE_FSTYPES = "container"
+PACKAGE_CLASSES = "package_ipk"
+IMAGE_FEATURES = ""
+""")
+
+        bbvars = get_bb_vars(['bindir', 'sysconfdir', 'localstatedir',
+                              'DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'],
+                              target='container-test-image')
+        expected_files = [
+                    './',
+                    '.{bindir}/theapp',
+                    '.{sysconfdir}/default/',
+                    '.{sysconfdir}/default/postinst',
+                    '.{sysconfdir}/ld.so.cache',
+                    '.{sysconfdir}/timestamp',
+                    '.{sysconfdir}/version',
+                    './run/',
+                    '.{localstatedir}/cache/',
+                    '.{localstatedir}/cache/ldconfig/',
+                    '.{localstatedir}/cache/ldconfig/aux-cache',
+                    '.{localstatedir}/cache/opkg/',
+                    '.{localstatedir}/lib/',
+                    '.{localstatedir}/lib/opkg/'
+                ]
+
+        expected_files = [ x.format(bindir=bbvars['bindir'],
+                                    sysconfdir=bbvars['sysconfdir'],
+                                    localstatedir=bbvars['localstatedir'])
+                                    for x in expected_files ]
+
+        # Since tar lists all directories individually, make sure each element
+        # from bindir, sysconfdir, etc is added
+        expected_files += get_each_path_part(bbvars['bindir'])
+        expected_files += get_each_path_part(bbvars['sysconfdir'])
+        expected_files += get_each_path_part(bbvars['localstatedir'])
+
+        expected_files = sorted(expected_files)
+
+        # Build the image of course
+        bitbake('container-test-image')
+
+        image = os.path.join(bbvars['DEPLOY_DIR_IMAGE'],
+                             bbvars['IMAGE_LINK_NAME'] + '.tar.bz2')
+
+        # Ensure the files in the image are what we expect
+        result = runCmd("tar tf {} | sort".format(image), shell=True)
+        self.assertEqual(result.output.split('\n'), expected_files)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
index 302ec5d..5704866 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/devtool.py
@@ -45,9 +45,12 @@
                 if var and var in checkvars:
                     needvalue = checkvars.pop(var)
                     if needvalue is None:
-                        self.fail('Variable %s should not appear in recipe')
+                        self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
                     if isinstance(needvalue, set):
-                        value = set(value.split())
+                        if var == 'LICENSE':
+                            value = set(value.split(' & '))
+                        else:
+                            value = set(value.split())
                     self.assertEqual(value, needvalue, 'values for %s do not match' % var)
 
 
@@ -210,9 +213,10 @@
         bitbake('pv -c cleansstate')
         # Test devtool build
         result = runCmd('devtool build pv')
-        installdir = get_bb_var('D', 'pv')
+        bb_vars = get_bb_vars(['D', 'bindir'], 'pv')
+        installdir = bb_vars['D']
         self.assertTrue(installdir, 'Could not query installdir variable')
-        bindir = get_bb_var('bindir', 'pv')
+        bindir = bb_vars['bindir']
         self.assertTrue(bindir, 'Could not query bindir variable')
         if bindir[0] == '/':
             bindir = bindir[1:]
@@ -260,8 +264,6 @@
 
     @testcase(1162)
     def test_devtool_add_library(self):
-        # We don't have the ability to pick up this dependency automatically yet...
-        bitbake('libusb1')
         # Fetch source
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         self.track_for_cleanup(tempdir)
@@ -290,13 +292,17 @@
         result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
         with open(recipefile, 'a') as f:
             f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n')
+            # We don't have the ability to pick up this dependency automatically yet...
+            f.write('\nDEPENDS += "libusb1"\n')
+            f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
         # Test devtool build
         result = runCmd('devtool build libftdi')
-        staging_libdir = get_bb_var('STAGING_LIBDIR', 'libftdi')
-        self.assertTrue(staging_libdir, 'Could not query STAGING_LIBDIR variable')
+        bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
+        staging_libdir = bb_vars['TESTLIBOUTPUT']
+        self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
         self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), "libftdi binary not found in STAGING_LIBDIR. Output of devtool build libftdi %s" % result.output)
         # Test devtool reset
-        stampprefix = get_bb_var('STAMP', 'libftdi')
+        stampprefix = bb_vars['STAMP']
         result = runCmd('devtool reset libftdi')
         result = runCmd('devtool status')
         self.assertNotIn('libftdi', result.output)
@@ -353,12 +359,11 @@
 
     @testcase(1161)
     def test_devtool_add_fetch_git(self):
-        # Fetch source
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         self.track_for_cleanup(tempdir)
-        url = 'git://git.yoctoproject.org/libmatchbox'
-        checkrev = '462f0652055d89c648ddd54fd7b03f175c2c6973'
-        testrecipe = 'libmatchbox2'
+        url = 'gitsm://git.yoctoproject.org/mraa'
+        checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
+        testrecipe = 'mraa'
         srcdir = os.path.join(tempdir, testrecipe)
         # Test devtool add
         self.track_for_cleanup(self.workspacedir)
@@ -366,7 +371,7 @@
         self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
         result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
         self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created: %s' % result.output)
-        self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure.ac in source directory')
+        self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
         # Test devtool status
         result = runCmd('devtool status')
         self.assertIn(testrecipe, result.output)
@@ -376,7 +381,7 @@
         self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
         checkvars = {}
         checkvars['S'] = '${WORKDIR}/git'
-        checkvars['PV'] = '1.12+git${SRCPV}'
+        checkvars['PV'] = '1.0+git${SRCPV}'
         checkvars['SRC_URI'] = url
         checkvars['SRCREV'] = '${AUTOREV}'
         self._test_recipe_contents(recipefile, checkvars, [])
@@ -385,7 +390,7 @@
         shutil.rmtree(srcdir)
         url_rev = '%s;rev=%s' % (url, checkrev)
         result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
-        self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure.ac in source directory')
+        self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
         # Test devtool status
         result = runCmd('devtool status')
         self.assertIn(testrecipe, result.output)
@@ -430,9 +435,8 @@
 
     @testcase(1164)
     def test_devtool_modify(self):
-        # Clean up anything in the workdir/sysroot/sstate cache
-        bitbake('mdadm -c cleansstate')
-        # Try modifying a recipe
+        import oe.path
+
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         self.track_for_cleanup(tempdir)
         self.track_for_cleanup(self.workspacedir)
@@ -443,35 +447,95 @@
         self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
         matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
         self.assertTrue(matches, 'bbappend not created %s' % result.output)
+
         # Test devtool status
         result = runCmd('devtool status')
         self.assertIn('mdadm', result.output)
         self.assertIn(tempdir, result.output)
-        # Check git repo
         self._check_src_repo(tempdir)
-        # Try building
-        bitbake('mdadm')
-        # Try making (minor) modifications to the source
-        result = runCmd("sed -i 's!^\.TH.*!.TH MDADM 8 \"\" v9.999-custom!' %s" % os.path.join(tempdir, 'mdadm.8.in'))
-        bitbake('mdadm -c package')
-        pkgd = get_bb_var('PKGD', 'mdadm')
+
+        bitbake('mdadm -C unpack')
+
+        def check_line(checkfile, expected, message, present=True):
+            # Check for $expected, on a line on its own, in checkfile.
+            with open(checkfile, 'r') as f:
+                if present:
+                    self.assertIn(expected + '\n', f, message)
+                else:
+                    self.assertNotIn(expected + '\n', f, message)
+
+        modfile = os.path.join(tempdir, 'mdadm.8.in')
+        bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
+        pkgd = bb_vars['PKGD']
         self.assertTrue(pkgd, 'Could not query PKGD variable')
-        mandir = get_bb_var('mandir', 'mdadm')
+        mandir = bb_vars['mandir']
         self.assertTrue(mandir, 'Could not query mandir variable')
-        if mandir[0] == '/':
-            mandir = mandir[1:]
-        with open(os.path.join(pkgd, mandir, 'man8', 'mdadm.8'), 'r') as f:
-            for line in f:
-                if line.startswith('.TH'):
-                    self.assertEqual(line.rstrip(), '.TH MDADM 8 "" v9.999-custom', 'man file not modified. man searched file path: %s' % os.path.join(pkgd, mandir, 'man8', 'mdadm.8'))
-        # Test devtool reset
-        stampprefix = get_bb_var('STAMP', 'mdadm')
+        manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
+
+        check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
+        check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
+
+        result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
+        check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
+
+        bitbake('mdadm -c package')
+        check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
+
+        result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
+        check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
+
+        bitbake('mdadm -c package')
+        check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
+
         result = runCmd('devtool reset mdadm')
         result = runCmd('devtool status')
         self.assertNotIn('mdadm', result.output)
-        self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe mdadm')
-        matches = glob.glob(stampprefix + '*')
-        self.assertFalse(matches, 'Stamp files exist for recipe mdadm that should have been cleaned')
+
+    def test_devtool_buildclean(self):
+        def assertFile(path, *paths):
+            f = os.path.join(path, *paths)
+            self.assertTrue(os.path.exists(f), "%r does not exist" % f)
+        def assertNoFile(path, *paths):
+            f = os.path.join(path, *paths)
+            self.assertFalse(os.path.exists(os.path.join(f)), "%r exists" % f)
+
+        # Clean up anything in the workdir/sysroot/sstate cache
+        bitbake('mdadm m4 -c cleansstate')
+        # Try modifying a recipe
+        tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
+        tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
+        builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(tempdir_mdadm)
+        self.track_for_cleanup(tempdir_m4)
+        self.track_for_cleanup(builddir_m4)
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        self.add_command_to_tearDown('bitbake -c clean mdadm m4')
+        self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
+        try:
+            runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
+            runCmd('devtool modify m4 -x %s' % tempdir_m4)
+            assertNoFile(tempdir_mdadm, 'mdadm')
+            assertNoFile(builddir_m4, 'src/m4')
+            result = bitbake('m4 -e')
+            result = bitbake('mdadm m4 -c compile')
+            self.assertEqual(result.status, 0)
+            assertFile(tempdir_mdadm, 'mdadm')
+            assertFile(builddir_m4, 'src/m4')
+            # Check that buildclean task exists and does call make clean
+            bitbake('mdadm m4 -c buildclean')
+            assertNoFile(tempdir_mdadm, 'mdadm')
+            assertNoFile(builddir_m4, 'src/m4')
+            bitbake('mdadm m4 -c compile')
+            assertFile(tempdir_mdadm, 'mdadm')
+            assertFile(builddir_m4, 'src/m4')
+            bitbake('mdadm m4 -c clean')
+            # Check that buildclean task is run before clean for B == S
+            assertNoFile(tempdir_mdadm, 'mdadm')
+            # Check that buildclean task is not run before clean for B != S
+            assertFile(builddir_m4, 'src/m4')
+        finally:
+            self.delete_recipeinc('m4')
 
     @testcase(1166)
     def test_devtool_modify_invalid(self):
@@ -594,8 +658,8 @@
     @testcase(1378)
     def test_devtool_modify_virtual(self):
         # Try modifying a virtual recipe
-        virtrecipe = 'virtual/libx11'
-        realrecipe = 'libx11'
+        virtrecipe = 'virtual/make'
+        realrecipe = 'make'
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         self.track_for_cleanup(tempdir)
         self.track_for_cleanup(self.workspacedir)
@@ -618,8 +682,9 @@
     def test_devtool_update_recipe(self):
         # Check preconditions
         testrecipe = 'minicom'
-        recipefile = get_bb_var('FILE', testrecipe)
-        src_uri = get_bb_var('SRC_URI', testrecipe)
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
         self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
         self._check_repo_status(os.path.dirname(recipefile), [])
         # First, modify a recipe
@@ -650,8 +715,9 @@
     def test_devtool_update_recipe_git(self):
         # Check preconditions
         testrecipe = 'mtd-utils'
-        recipefile = get_bb_var('FILE', testrecipe)
-        src_uri = get_bb_var('SRC_URI', testrecipe)
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
         self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
         patches = []
         for entry in src_uri.split():
@@ -670,7 +736,7 @@
         self._check_src_repo(tempdir)
         # Add a couple of commits
         # FIXME: this only tests adding, need to also test update and remove
-        result = runCmd('echo "# Additional line" >> Makefile', cwd=tempdir)
+        result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
         result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
         result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
         result = runCmd('git add devtool-new-file', cwd=tempdir)
@@ -719,8 +785,9 @@
     def test_devtool_update_recipe_append(self):
         # Check preconditions
         testrecipe = 'mdadm'
-        recipefile = get_bb_var('FILE', testrecipe)
-        src_uri = get_bb_var('SRC_URI', testrecipe)
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
         self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
         self._check_repo_status(os.path.dirname(recipefile), [])
         # First, modify a recipe
@@ -787,8 +854,9 @@
     def test_devtool_update_recipe_append_git(self):
         # Check preconditions
         testrecipe = 'mtd-utils'
-        recipefile = get_bb_var('FILE', testrecipe)
-        src_uri = get_bb_var('SRC_URI', testrecipe)
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
         self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
         for entry in src_uri.split():
             if entry.startswith('git://'):
@@ -807,7 +875,7 @@
         # Check git repo
         self._check_src_repo(tempsrcdir)
         # Add a commit
-        result = runCmd('echo "# Additional line" >> Makefile', cwd=tempsrcdir)
+        result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
         result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
         self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
         # Create a temporary layer
@@ -887,6 +955,8 @@
         result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
         # Check git repo
         self._check_src_repo(tempdir)
+        # Try building just to ensure we haven't broken that
+        bitbake("%s" % testrecipe)
         # Edit / commit local source
         runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir)
         runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
@@ -943,6 +1013,78 @@
                            ('??', '.*/0001-Add-new-file.patch$')]
         self._check_repo_status(os.path.dirname(recipefile), expected_status)
 
+    def test_devtool_update_recipe_local_files_3(self):
+        # First, modify the recipe
+        testrecipe = 'devtool-test-localonly'
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
+        tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(tempdir)
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        # (don't bother with cleaning the recipe on teardown, we won't be building it)
+        result = runCmd('devtool modify %s' % testrecipe)
+        # Modify one file
+        runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files'))
+        self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+        result = runCmd('devtool update-recipe %s' % testrecipe)
+        expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
+        self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+    def test_devtool_update_recipe_local_patch_gz(self):
+        # First, modify the recipe
+        testrecipe = 'devtool-test-patch-gz'
+        if get_bb_var('DISTRO') == 'poky-tiny':
+            self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
+        tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(tempdir)
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        # (don't bother with cleaning the recipe on teardown, we won't be building it)
+        result = runCmd('devtool modify %s' % testrecipe)
+        # Modify one file
+        srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
+        runCmd('echo "Another line" >> README', cwd=srctree)
+        runCmd('git commit -a --amend --no-edit', cwd=srctree)
+        self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+        result = runCmd('devtool update-recipe %s' % testrecipe)
+        expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
+        self._check_repo_status(os.path.dirname(recipefile), expected_status)
+        patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
+        result = runCmd('file %s' % patch_gz)
+        if 'gzip compressed data' not in result.output:
+            self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
+
+    def test_devtool_update_recipe_local_files_subdir(self):
+        # Try devtool extract on a recipe that has a file with subdir= set in
+        # SRC_URI such that it overwrites a file that was in an archive that
+        # was also in SRC_URI
+        # First, modify the recipe
+        testrecipe = 'devtool-test-subdir'
+        bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+        recipefile = bb_vars['FILE']
+        src_uri = bb_vars['SRC_URI']
+        tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(tempdir)
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        # (don't bother with cleaning the recipe on teardown, we won't be building it)
+        result = runCmd('devtool modify %s' % testrecipe)
+        testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
+        self.assertTrue(os.path.exists(testfile), 'Extracted source could not be found')
+        with open(testfile, 'r') as f:
+            contents = f.read().rstrip()
+        self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
+        # Test devtool update-recipe without modifying any files
+        self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+        result = runCmd('devtool update-recipe %s' % testrecipe)
+        expected_status = []
+        self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
     @testcase(1163)
     def test_devtool_extract(self):
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
@@ -960,7 +1102,7 @@
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         # Try devtool extract
         self.track_for_cleanup(tempdir)
-        result = runCmd('devtool extract virtual/libx11 %s' % tempdir)
+        result = runCmd('devtool extract virtual/make %s' % tempdir)
         self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
         # devtool extract shouldn't create the workspace
         self.assertFalse(os.path.exists(self.workspacedir))
@@ -1054,9 +1196,10 @@
             result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
             # Check if it deployed all of the files with the right ownership/perms
             # First look on the host - need to do this under pseudo to get the correct ownership/perms
-            installdir = get_bb_var('D', testrecipe)
-            fakerootenv = get_bb_var('FAKEROOTENV', testrecipe)
-            fakerootcmd = get_bb_var('FAKEROOTCMD', testrecipe)
+            bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
+            installdir = bb_vars['D']
+            fakerootenv = bb_vars['FAKEROOTENV']
+            fakerootcmd = bb_vars['FAKEROOTCMD']
             result = runCmd('%s %s find . -type f -exec ls -l {} \;' % (fakerootenv, fakerootcmd), cwd=installdir)
             filelist1 = self._process_ls_output(result.output)
 
@@ -1207,6 +1350,49 @@
         result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
         self.assertEqual(result.output, s[::-1])
 
+    def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
+        dstdir = basedstdir
+        self.assertTrue(os.path.exists(dstdir))
+        for p in paths:
+            dstdir = os.path.join(dstdir, p)
+            if not os.path.exists(dstdir):
+                os.makedirs(dstdir)
+                self.track_for_cleanup(dstdir)
+        dstfile = os.path.join(dstdir, os.path.basename(srcfile))
+        if srcfile != dstfile:
+            shutil.copy(srcfile, dstfile)
+            self.track_for_cleanup(dstfile)
+
+    def test_devtool_load_plugin(self):
+        """Test that devtool loads only the first found plugin in BBPATH."""
+
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+        devtool = runCmd("which devtool")
+        fromname = runCmd("devtool --quiet pluginfile")
+        srcfile = fromname.output
+        bbpath = get_bb_var('BBPATH')
+        searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
+        plugincontent = []
+        with open(srcfile) as fh:
+            plugincontent = fh.readlines()
+        try:
+            self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
+            for path in searchpath:
+                self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
+            result = runCmd("devtool --quiet count")
+            self.assertEqual(result.output, '1')
+            result = runCmd("devtool --quiet multiloaded")
+            self.assertEqual(result.output, "no")
+            for path in searchpath:
+                result = runCmd("devtool --quiet bbdir")
+                self.assertEqual(result.output, path)
+                os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
+        finally:
+            with open(srcfile, 'w') as fh:
+                fh.writelines(plugincontent)
+
     def _setup_test_devtool_finish_upgrade(self):
         # Check preconditions
         self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
@@ -1362,3 +1548,149 @@
         files.remove(foundpatch)
         if files:
             self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
+
+    def test_devtool_rename(self):
+        # Check preconditions
+        self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+        # First run devtool add
+        # We already have this recipe in OE-Core, but that doesn't matter
+        recipename = 'i2c-tools'
+        recipever = '3.1.2'
+        recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
+        url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
+        def add_recipe():
+            result = runCmd('devtool add %s' % url)
+            self.assertTrue(os.path.exists(recipefile), 'Expected recipe file not created')
+            self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory not created')
+            checkvars = {}
+            checkvars['S'] = None
+            checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
+            self._test_recipe_contents(recipefile, checkvars, [])
+        add_recipe()
+        # Now rename it - change both name and version
+        newrecipename = 'mynewrecipe'
+        newrecipever = '456'
+        newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
+        result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
+        self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+        self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
+        newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
+        self.assertTrue(os.path.exists(newsrctree), 'Source directory not renamed')
+        checkvars = {}
+        checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever)
+        checkvars['SRC_URI'] = url
+        self._test_recipe_contents(newrecipefile, checkvars, [])
+        # Try again - change just name this time
+        result = runCmd('devtool reset -n %s' % newrecipename)
+        shutil.rmtree(newsrctree)
+        add_recipe()
+        newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
+        result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
+        self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+        self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
+        self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', newrecipename)), 'Source directory not renamed')
+        checkvars = {}
+        checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename
+        checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
+        self._test_recipe_contents(newrecipefile, checkvars, [])
+        # Try again - change just version this time
+        result = runCmd('devtool reset -n %s' % newrecipename)
+        shutil.rmtree(newsrctree)
+        add_recipe()
+        newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
+        result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
+        self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+        self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory no longer exists')
+        checkvars = {}
+        checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever
+        checkvars['SRC_URI'] = url
+        self._test_recipe_contents(newrecipefile, checkvars, [])
+
+    @testcase(1577)
+    def test_devtool_virtual_kernel_modify(self):
+        """
+        Summary:        The purpose of this test case is to verify that
+                        devtool modify works correctly when building
+                        the kernel.
+        Dependencies:   NA
+        Steps:          1. Build kernel with bitbake.
+                        2. Save the config file generated.
+                        3. Clean the environment.
+                        4. Use `devtool modify virtual/kernel` to validate following:
+                           4.1 The source is checked out correctly.
+                           4.2 The resulting configuration is the same as
+                               what was get on step 2.
+                           4.3 The Kernel can be build correctly.
+                           4.4 Changes made on the source are reflected on the
+                               subsequent builds.
+                           4.5 Changes on the configuration are reflected on the
+                               subsequent builds
+         Expected:       devtool modify is able to checkout the source of the kernel
+                         and modification to the source and configurations are reflected
+                         when building the kernel.
+         """
+        #Set machine to qemxu86 to be able to modify the kernel and
+        #verify the modification.
+        features = 'MACHINE = "qemux86"\n'
+        self.write_config(features)
+        kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel')
+        # Clean up the enviroment
+        bitbake('%s -c clean' % kernel_provider)
+        tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(tempdir)
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
+        #Step 1
+        #Here is just generated the config file instead of all the kernel to optimize the
+        #time of executing this test case.
+        bitbake('%s -c configure' % kernel_provider)
+        bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
+        buildir= get_bb_var('TOPDIR')
+        #Step 2
+        runCmd('cp %s %s' % (bbconfig, buildir))
+        self.assertTrue(os.path.exists(os.path.join(buildir, '.config')),
+                        'Could not copy .config file from kernel')
+
+        tmpconfig = os.path.join(buildir, '.config')
+        #Step 3
+        bitbake('%s -c clean' % kernel_provider)
+        #Step 4.1
+        runCmd('devtool modify virtual/kernel -x %s' % tempdir)
+        self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')),
+                        'Extracted source could not be found')
+        #Step 4.2
+        configfile = os.path.join(tempdir,'.config')
+        diff = runCmd('diff %s %s' % (tmpconfig, configfile))
+        self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool')
+        #Step 4.3
+        #NOTE: virtual/kernel is mapped to kernel_provider
+        result = runCmd('devtool build %s' % kernel_provider)
+        self.assertEqual(0,result.status,'Cannot build kernel using `devtool build`')
+        kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
+        self.assertTrue(os.path.exists(kernelfile),'Kernel was not build correctly')
+
+        #Modify the kernel source, this is specific for qemux86
+        modfile = os.path.join(tempdir,'arch/x86/boot/header.S')
+        modstring = "use a boot loader - Devtool kernel testing"
+        modapplied = runCmd("sed -i 's/boot loader/%s/' %s" % (modstring, modfile))
+        self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile)
+        #Modify the configuration
+        codeconfigfile = os.path.join(tempdir,'.config.new')
+        modconfopt = "CONFIG_SG_POOL=n"
+        modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
+        self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile)
+        #Build again kernel with devtool
+        rebuild = runCmd('devtool build %s' % kernel_provider)
+        self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config')
+        #Step 4.4
+        bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider)
+        bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename)
+        checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
+        self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
+        #Step 4.5
+        checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile))
+        self.assertEqual(0,checkmodconfg.status,'Modification to configuration file failed')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py
index 9d5c680..1596c6e 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/eSDK.py
@@ -6,16 +6,15 @@
 import logging
 import subprocess
 import oeqa.utils.ftools as ftools
-from oeqa.utils.decorators import testcase 
+from oeqa.utils.decorators import testcase
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var
-from oeqa.utils.httpserver import HTTPService
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
 
 class oeSDKExtSelfTest(oeSelfTest):
     """
     # Bugzilla Test Plan: 6033
     # This code is planned to be part of the automation for eSDK containig
-    # Install libraries and headers, image generation binary feeds.
+    # Install libraries and headers, image generation binary feeds, sdk-update.
     """
 
     @staticmethod
@@ -24,7 +23,7 @@
         # what environment load oe-selftest, i586, x86_64
         pattern = os.path.join(tmpdir_eSDKQA, 'environment-setup-*')
         return glob.glob(pattern)[0]
-    
+
     @staticmethod
     def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, postconfig=None, **options):
         if postconfig:
@@ -47,53 +46,66 @@
     def get_eSDK_toolchain(image):
         pn_task = '%s -c populate_sdk_ext' % image
 
-        sdk_deploy = get_bb_var('SDK_DEPLOY', pn_task)
-        toolchain_name = get_bb_var('TOOLCHAINEXT_OUTPUTNAME', pn_task)
+        bb_vars = get_bb_vars(['SDK_DEPLOY', 'TOOLCHAINEXT_OUTPUTNAME'], pn_task)
+        sdk_deploy = bb_vars['SDK_DEPLOY']
+        toolchain_name = bb_vars['TOOLCHAINEXT_OUTPUTNAME']
         return os.path.join(sdk_deploy, toolchain_name + '.sh')
-    
+
+    @staticmethod
+    def update_configuration(cls, image, tmpdir_eSDKQA, env_eSDK, ext_sdk_path):
+        sstate_dir = os.path.join(os.environ['BUILDDIR'], 'sstate-cache')
+
+        oeSDKExtSelfTest.generate_eSDK(cls.image)
+
+        cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
+        runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
+
+        cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
+
+        sstate_config="""
+SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
+SSTATE_MIRRORS =  "file://.* file://%s/PATH"
+CORE_IMAGE_EXTRA_INSTALL = "perl"
+        """ % sstate_dir
+
+        with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
+            f.write(sstate_config)
 
     @classmethod
     def setUpClass(cls):
-        # Start to serve sstate dir
-        sstate_dir = os.path.join(os.environ['BUILDDIR'], 'sstate-cache')
-        cls.http_service = HTTPService(sstate_dir)
-        cls.http_service.start()
-
-        http_url = "127.0.0.1:%d" % cls.http_service.port
- 
-        image = 'core-image-minimal'
-
         cls.tmpdir_eSDKQA = tempfile.mkdtemp(prefix='eSDKQA')
-        oeSDKExtSelfTest.generate_eSDK(image)
+
+        sstate_dir = get_bb_var('SSTATE_DIR')
+
+        cls.image = 'core-image-minimal'
+        oeSDKExtSelfTest.generate_eSDK(cls.image)
 
         # Install eSDK
-        ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(image)
-        runCmd("%s -y -d \"%s\"" % (ext_sdk_path, cls.tmpdir_eSDKQA))
+        cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
+        runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
 
         cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
 
         # Configure eSDK to use sstate mirror from poky
         sstate_config="""
 SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
-SSTATE_MIRRORS =  "file://.* http://%s/PATH"
-        """ % http_url
+SSTATE_MIRRORS =  "file://.* file://%s/PATH"
+            """ % sstate_dir
         with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
             f.write(sstate_config)
 
-      
     @classmethod
     def tearDownClass(cls):
         shutil.rmtree(cls.tmpdir_eSDKQA)
-        cls.http_service.stop()
 
-    @testcase (1471)
+    @testcase (1602)
     def test_install_libraries_headers(self):
         pn_sstate = 'bc'
         bitbake(pn_sstate)
         cmd = "devtool sdk-install %s " % pn_sstate
         oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
-    
-    @testcase(1472)
+
+    @testcase(1603)
     def test_image_generation_binary_feeds(self):
         image = 'core-image-minimal'
         cmd = "devtool build-image %s" % image
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/image_typedep.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/image_typedep.py
new file mode 100644
index 0000000..256142d
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/image_typedep.py
@@ -0,0 +1,51 @@
+import os
+
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import bitbake
+
+class ImageTypeDepTests(oeSelfTest):
+
+    # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
+    # the conversion type bar gets added as a dep as well
+    def test_conversion_typedep_added(self):
+
+        self.write_recipeinc('emptytest', """
+# Try to empty out the default dependency list
+PACKAGE_INSTALL = ""
+DISTRO_EXTRA_RDEPENDS=""
+
+LICENSE = "MIT"
+IMAGE_FSTYPES = "testfstype"
+
+IMAGE_TYPES_MASKED += "testfstype"
+IMAGE_TYPEDEP_testfstype = "tar.bz2"
+
+inherit image
+
+""")
+        # First get the dependency that should exist for bz2, it will look
+        # like CONVERSION_DEPENDS_bz2="somedep"
+        result = bitbake('-e emptytest')
+
+        for line in result.output.split('\n'):
+            if line.startswith('CONVERSION_DEPENDS_bz2'):
+                dep = line.split('=')[1].strip('"')
+                break
+
+        # Now get the dependency task list and check for the expected task
+        # dependency
+        bitbake('-g emptytest')
+
+        taskdependsfile = os.path.join(self.builddir, 'task-depends.dot')
+        dep =  dep + ".do_populate_sysroot"
+        depfound = False
+        expectedline = '"emptytest.do_rootfs" -> "{}"'.format(dep)
+
+        with open(taskdependsfile, "r") as f:
+            for line in f:
+                if line.strip() == expectedline:
+                    depfound = True
+                    break
+
+        if not depfound:
+            raise AssertionError("\"{}\" not found".format(expectedline))
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
index d015c49..76896c7 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/imagefeatures.py
@@ -91,9 +91,9 @@
         AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
         """
 
-        features = 'DISTRO_FEATURES_append = " wayland"\n'
-        features += 'CORE_IMAGE_EXTRA_INSTALL += "wayland weston"'
-        self.write_config(features)
+        distro_features = get_bb_var('DISTRO_FEATURES')
+        if not ('opengl' in distro_features and 'wayland' in distro_features):
+            self.skipTest('neither opengl nor wayland present on DISTRO_FEATURES so core-image-weston cannot be built')
 
         # Build a core-image-weston
         bitbake('core-image-weston')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/layerappend.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/layerappend.py
index 4de5034..37bb32c 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/layerappend.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/layerappend.py
@@ -55,7 +55,7 @@
     @testcase(1196)
     def test_layer_appends(self):
         corebase = get_bb_var("COREBASE")
-        stagingdir = get_bb_var("STAGING_DIR_TARGET")
+
         for l in ["0", "1", "2"]:
             layer = os.path.join(corebase, "meta-layertest" + l)
             self.assertFalse(os.path.exists(layer))
@@ -83,6 +83,7 @@
 
         self.layerappend = "BBLAYERS += \"{0}/meta-layertest0 {0}/meta-layertest1 {0}/meta-layertest2\"".format(corebase)
         ftools.append_file(self.builddir + "/conf/bblayers.conf", self.layerappend)
+        stagingdir = get_bb_var("SYSROOT_DESTDIR", "layerappendtest")
         bitbake("layerappendtest")
         data = ftools.read_file(stagingdir + "/appendtest.txt")
         self.assertEqual(data, "Layer 2 test")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py
index 35131eb..0b0301d 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/liboe.py
@@ -1,11 +1,16 @@
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import get_bb_var, bitbake, runCmd
+from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake, runCmd
 import oe.path
 import glob
 import os
 import os.path
 
 class LibOE(oeSelfTest):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.tmp_dir = get_bb_var('TMPDIR')
+
     def test_copy_tree_special(self):
         """
         Summary:    oe.path.copytree() should copy files with special character
@@ -14,8 +19,7 @@
         Product:    OE-Core
         Author:     Joshua Lock <joshua.g.lock@intel.com>
         """
-        tmp_dir = get_bb_var('TMPDIR')
-        testloc = oe.path.join(tmp_dir, 'liboetests')
+        testloc = oe.path.join(self.tmp_dir, 'liboetests')
         src = oe.path.join(testloc, 'src')
         dst = oe.path.join(testloc, 'dst')
         bb.utils.mkdirhier(testloc)
@@ -40,8 +44,7 @@
         Product:    OE-Core
         Author:     Joshua Lock <joshua.g.lock@intel.com>
         """
-        tmp_dir = get_bb_var('TMPDIR')
-        testloc = oe.path.join(tmp_dir, 'liboetests')
+        testloc = oe.path.join(self.tmp_dir, 'liboetests')
         src = oe.path.join(testloc, 'src')
         dst = oe.path.join(testloc, 'dst')
         bb.utils.mkdirhier(testloc)
@@ -50,7 +53,11 @@
 
         # ensure we have setfattr available
         bitbake("attr-native")
-        bindir = get_bb_var('STAGING_BINDIR_NATIVE')
+
+        bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'attr-native')
+        destdir = bb_vars['SYSROOT_DESTDIR']
+        bindir = bb_vars['bindir']
+        bindir = destdir + bindir
 
         # create a file with xattr and copy it
         open(oe.path.join(src, testfilename), 'w+b').close()
@@ -70,8 +77,7 @@
         Product:    OE-Core
         Author:     Joshua Lock <joshua.g.lock@intel.com>
         """
-        tmp_dir = get_bb_var('TMPDIR')
-        testloc = oe.path.join(tmp_dir, 'liboetests')
+        testloc = oe.path.join(self.tmp_dir, 'liboetests')
         src = oe.path.join(testloc, 'src')
         dst = oe.path.join(testloc, 'dst')
         bb.utils.mkdirhier(testloc)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/manifest.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/manifest.py
index 44d0404..fe6f949 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/manifest.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/manifest.py
@@ -2,7 +2,7 @@
 import os
 
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import get_bb_var, bitbake
+from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake
 from oeqa.utils.decorators import testcase
 
 class ManifestEntry:
@@ -84,9 +84,10 @@
         try:
             mdir = self.get_dir_from_bb_var('SDK_DEPLOY', self.buildtarget)
             for k in d_target.keys():
+                bb_vars = get_bb_vars(['SDK_NAME', 'SDK_VERSION'], self.buildtarget)
                 mfilename[k] = "{}-toolchain-{}.{}.manifest".format(
-                        get_bb_var("SDK_NAME", self.buildtarget),
-                        get_bb_var("SDK_VERSION", self.buildtarget),
+                        bb_vars['SDK_NAME'],
+                        bb_vars['SDK_VERSION'],
                         k)
                 mpath[k] = os.path.join(mdir, mfilename[k])
                 if not os.path.isfile(mpath[k]):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/__init__.py
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/buildhistory.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/buildhistory.py
new file mode 100644
index 0000000..5ed4b02
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/buildhistory.py
@@ -0,0 +1,88 @@
+import os
+import unittest
+import tempfile
+from git import Repo
+from oeqa.utils.commands import get_bb_var
+from oe.buildhistory_analysis import blob_to_dict, compare_dict_blobs
+
+class TestBlobParsing(unittest.TestCase):
+
+    def setUp(self):
+        import time
+        self.repo_path = tempfile.mkdtemp(prefix='selftest-buildhistory',
+            dir=get_bb_var('TOPDIR'))
+
+        self.repo = Repo.init(self.repo_path)
+        self.test_file = "test"
+        self.var_map = {}
+
+    def tearDown(self):
+        import shutil
+        shutil.rmtree(self.repo_path)
+
+    def commit_vars(self, to_add={}, to_remove = [], msg="A commit message"):
+        if len(to_add) == 0 and len(to_remove) == 0:
+            return
+
+        for k in to_remove:
+            self.var_map.pop(x,None)
+        for k in to_add:
+            self.var_map[k] = to_add[k]
+
+        with open(os.path.join(self.repo_path, self.test_file), 'w') as repo_file:
+            for k in self.var_map:
+                repo_file.write("%s = %s\n" % (k, self.var_map[k]))
+
+        self.repo.git.add("--all")
+        self.repo.git.commit(message=msg)
+
+    def test_blob_to_dict(self):
+        """
+        Test convertion of git blobs to dictionary
+        """
+        valuesmap = { "foo" : "1", "bar" : "2" }
+        self.commit_vars(to_add = valuesmap)
+
+        blob = self.repo.head.commit.tree.blobs[0]
+        self.assertEqual(valuesmap, blob_to_dict(blob),
+            "commit was not translated correctly to dictionary")
+
+    def test_compare_dict_blobs(self):
+        """
+        Test comparisson of dictionaries extracted from git blobs
+        """
+        changesmap = { "foo-2" : ("2", "8"), "bar" : ("","4"), "bar-2" : ("","5")}
+
+        self.commit_vars(to_add = { "foo" : "1", "foo-2" : "2", "foo-3" : "3" })
+        blob1 = self.repo.heads.master.commit.tree.blobs[0]
+
+        self.commit_vars(to_add = { "foo-2" : "8", "bar" : "4", "bar-2" : "5" })
+        blob2 = self.repo.heads.master.commit.tree.blobs[0]
+
+        change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
+            blob1, blob2, False, False)
+
+        var_changes = { x.fieldname : (x.oldvalue, x.newvalue) for x in change_records}
+        self.assertEqual(changesmap, var_changes, "Changes not reported correctly")
+
+    def test_compare_dict_blobs_default(self):
+        """
+        Test default values for comparisson of git blob dictionaries
+        """
+        defaultmap = { x : ("default", "1")  for x in ["PKG", "PKGE", "PKGV", "PKGR"]}
+
+        self.commit_vars(to_add = { "foo" : "1" })
+        blob1 = self.repo.heads.master.commit.tree.blobs[0]
+
+        self.commit_vars(to_add = { "PKG" : "1", "PKGE" : "1", "PKGV" : "1", "PKGR" : "1" })
+        blob2 = self.repo.heads.master.commit.tree.blobs[0]
+
+        change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
+            blob1, blob2, False, False)
+
+        var_changes = {}
+        for x in change_records:
+            oldvalue = "default" if ("default" in x.oldvalue) else x.oldvalue
+            var_changes[x.fieldname] = (oldvalue, x.newvalue)
+
+        self.assertEqual(defaultmap, var_changes, "Defaults not set properly")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/elf.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/elf.py
new file mode 100644
index 0000000..1f59037
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/elf.py
@@ -0,0 +1,21 @@
+import unittest
+import oe.qa
+
+class TestElf(unittest.TestCase):
+    def test_machine_name(self):
+        """
+        Test elf_machine_to_string()
+        """
+        self.assertEqual(oe.qa.elf_machine_to_string(0x02), "SPARC")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x03), "x86")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x08), "MIPS")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x14), "PowerPC")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x28), "ARM")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x2A), "SuperH")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x32), "IA-64")
+        self.assertEqual(oe.qa.elf_machine_to_string(0x3E), "x86-64")
+        self.assertEqual(oe.qa.elf_machine_to_string(0xB7), "AArch64")
+
+        self.assertEqual(oe.qa.elf_machine_to_string(0x00), "Unknown (0)")
+        self.assertEqual(oe.qa.elf_machine_to_string(0xDEADBEEF), "Unknown (3735928559)")
+        self.assertEqual(oe.qa.elf_machine_to_string("foobar"), "Unknown ('foobar')")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/license.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/license.py
new file mode 100644
index 0000000..c388886
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/license.py
@@ -0,0 +1,68 @@
+import unittest
+import oe.license
+
+class SeenVisitor(oe.license.LicenseVisitor):
+    def __init__(self):
+        self.seen = []
+        oe.license.LicenseVisitor.__init__(self)
+
+    def visit_Str(self, node):
+        self.seen.append(node.s)
+
+class TestSingleLicense(unittest.TestCase):
+    licenses = [
+        "GPLv2",
+        "LGPL-2.0",
+        "Artistic",
+        "MIT",
+        "GPLv3+",
+        "FOO_BAR",
+    ]
+    invalid_licenses = ["GPL/BSD"]
+
+    @staticmethod
+    def parse(licensestr):
+        visitor = SeenVisitor()
+        visitor.visit_string(licensestr)
+        return visitor.seen
+
+    def test_single_licenses(self):
+        for license in self.licenses:
+            licenses = self.parse(license)
+            self.assertListEqual(licenses, [license])
+
+    def test_invalid_licenses(self):
+        for license in self.invalid_licenses:
+            with self.assertRaises(oe.license.InvalidLicense) as cm:
+                self.parse(license)
+            self.assertEqual(cm.exception.license, license)
+
+class TestSimpleCombinations(unittest.TestCase):
+    tests = {
+        "FOO&BAR": ["FOO", "BAR"],
+        "BAZ & MOO": ["BAZ", "MOO"],
+        "ALPHA|BETA": ["ALPHA"],
+        "BAZ&MOO|FOO": ["FOO"],
+        "FOO&BAR|BAZ": ["FOO", "BAR"],
+    }
+    preferred = ["ALPHA", "FOO", "BAR"]
+
+    def test_tests(self):
+        def choose(a, b):
+            if all(lic in self.preferred for lic in b):
+                return b
+            else:
+                return a
+
+        for license, expected in self.tests.items():
+            licenses = oe.license.flattened_licenses(license, choose)
+            self.assertListEqual(licenses, expected)
+
+class TestComplexCombinations(TestSimpleCombinations):
+    tests = {
+        "FOO & (BAR | BAZ)&MOO": ["FOO", "BAR", "MOO"],
+        "(ALPHA|(BETA&THETA)|OMEGA)&DELTA": ["OMEGA", "DELTA"],
+        "((ALPHA|BETA)&FOO)|BAZ": ["BETA", "FOO"],
+        "(GPL-2.0|Proprietary)&BSD-4-clause&MIT": ["GPL-2.0", "BSD-4-clause", "MIT"],
+    }
+    preferred = ["BAR", "OMEGA", "BETA", "GPL-2.0"]
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/path.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/path.py
new file mode 100644
index 0000000..44d0681
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/path.py
@@ -0,0 +1,89 @@
+import unittest
+import oe, oe.path
+import tempfile
+import os
+import errno
+import shutil
+
+class TestRealPath(unittest.TestCase):
+    DIRS = [ "a", "b", "etc", "sbin", "usr", "usr/bin", "usr/binX", "usr/sbin", "usr/include", "usr/include/gdbm" ]
+    FILES = [ "etc/passwd", "b/file" ]
+    LINKS = [
+        ( "bin",             "/usr/bin",             "/usr/bin" ),
+        ( "binX",            "usr/binX",             "/usr/binX" ),
+        ( "c",               "broken",               "/broken" ),
+        ( "etc/passwd-1",    "passwd",               "/etc/passwd" ),
+        ( "etc/passwd-2",    "passwd-1",             "/etc/passwd" ),
+        ( "etc/passwd-3",    "/etc/passwd-1",        "/etc/passwd" ),
+        ( "etc/shadow-1",    "/etc/shadow",          "/etc/shadow" ),
+        ( "etc/shadow-2",    "/etc/shadow-1",        "/etc/shadow" ),
+        ( "prog-A",          "bin/prog-A",           "/usr/bin/prog-A" ),
+        ( "prog-B",          "/bin/prog-B",          "/usr/bin/prog-B" ),
+        ( "usr/bin/prog-C",  "../../sbin/prog-C",    "/sbin/prog-C" ),
+        ( "usr/bin/prog-D",  "/sbin/prog-D",         "/sbin/prog-D" ),
+        ( "usr/binX/prog-E", "../sbin/prog-E",       None ),
+        ( "usr/bin/prog-F",  "../../../sbin/prog-F", "/sbin/prog-F" ),
+        ( "loop",            "a/loop",               None ),
+        ( "a/loop",          "../loop",              None ),
+        ( "b/test",          "file/foo",             "/b/file/foo" ),
+    ]
+
+    LINKS_PHYS = [
+        ( "./",          "/",                "" ),
+        ( "binX/prog-E", "/usr/sbin/prog-E", "/sbin/prog-E" ),
+    ]
+
+    EXCEPTIONS = [
+        ( "loop",   errno.ELOOP ),
+        ( "b/test", errno.ENOENT ),
+    ]
+
+    def __del__(self):
+        try:
+            #os.system("tree -F %s" % self.tmpdir)
+            shutil.rmtree(self.tmpdir)
+        except:
+            pass
+
+    def setUp(self):
+        self.tmpdir = tempfile.mkdtemp(prefix = "oe-test_path")
+        self.root = os.path.join(self.tmpdir, "R")
+
+        os.mkdir(os.path.join(self.tmpdir, "_real"))
+        os.symlink("_real", self.root)
+
+        for d in self.DIRS:
+            os.mkdir(os.path.join(self.root, d))
+        for f in self.FILES:
+            open(os.path.join(self.root, f), "w")
+        for l in self.LINKS:
+            os.symlink(l[1], os.path.join(self.root, l[0]))
+
+    def __realpath(self, file, use_physdir, assume_dir = True):
+        return oe.path.realpath(os.path.join(self.root, file), self.root,
+                                use_physdir, assume_dir = assume_dir)
+
+    def test_norm(self):
+        for l in self.LINKS:
+            if l[2] == None:
+                continue
+
+            target_p = self.__realpath(l[0], True)
+            target_l = self.__realpath(l[0], False)
+
+            if l[2] != False:
+                self.assertEqual(target_p, target_l)
+                self.assertEqual(l[2], target_p[len(self.root):])
+
+    def test_phys(self):
+        for l in self.LINKS_PHYS:
+            target_p = self.__realpath(l[0], True)
+            target_l = self.__realpath(l[0], False)
+
+            self.assertEqual(l[1], target_p[len(self.root):])
+            self.assertEqual(l[2], target_l[len(self.root):])
+
+    def test_loop(self):
+        for e in self.EXCEPTIONS:
+            self.assertRaisesRegex(OSError, r'\[Errno %u\]' % e[1],
+                                    self.__realpath, e[0], False, False)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/types.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/types.py
new file mode 100644
index 0000000..4fe2746
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/types.py
@@ -0,0 +1,50 @@
+import unittest
+from oe.maketype import create
+
+class TestBooleanType(unittest.TestCase):
+    def test_invalid(self):
+        self.assertRaises(ValueError, create, '', 'boolean')
+        self.assertRaises(ValueError, create, 'foo', 'boolean')
+        self.assertRaises(TypeError, create, object(), 'boolean')
+
+    def test_true(self):
+        self.assertTrue(create('y', 'boolean'))
+        self.assertTrue(create('yes', 'boolean'))
+        self.assertTrue(create('1', 'boolean'))
+        self.assertTrue(create('t', 'boolean'))
+        self.assertTrue(create('true', 'boolean'))
+        self.assertTrue(create('TRUE', 'boolean'))
+        self.assertTrue(create('truE', 'boolean'))
+
+    def test_false(self):
+        self.assertFalse(create('n', 'boolean'))
+        self.assertFalse(create('no', 'boolean'))
+        self.assertFalse(create('0', 'boolean'))
+        self.assertFalse(create('f', 'boolean'))
+        self.assertFalse(create('false', 'boolean'))
+        self.assertFalse(create('FALSE', 'boolean'))
+        self.assertFalse(create('faLse', 'boolean'))
+
+    def test_bool_equality(self):
+        self.assertEqual(create('n', 'boolean'), False)
+        self.assertNotEqual(create('n', 'boolean'), True)
+        self.assertEqual(create('y', 'boolean'), True)
+        self.assertNotEqual(create('y', 'boolean'), False)
+
+class TestList(unittest.TestCase):
+    def assertListEqual(self, value, valid, sep=None):
+        obj = create(value, 'list', separator=sep)
+        self.assertEqual(obj, valid)
+        if sep is not None:
+            self.assertEqual(obj.separator, sep)
+        self.assertEqual(str(obj), obj.separator.join(obj))
+
+    def test_list_nosep(self):
+        testlist = ['alpha', 'beta', 'theta']
+        self.assertListEqual('alpha beta theta', testlist)
+        self.assertListEqual('alpha  beta\ttheta', testlist)
+        self.assertListEqual('alpha', ['alpha'])
+
+    def test_list_usersep(self):
+        self.assertListEqual('foo:bar', ['foo', 'bar'], ':')
+        self.assertListEqual('foo:bar:baz', ['foo', 'bar', 'baz'], ':')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/utils.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/utils.py
new file mode 100644
index 0000000..7deb10f
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oelib/utils.py
@@ -0,0 +1,51 @@
+import unittest
+from oe.utils import packages_filter_out_system, trim_version
+
+class TestPackagesFilterOutSystem(unittest.TestCase):
+    def test_filter(self):
+        """
+        Test that oe.utils.packages_filter_out_system works.
+        """
+        try:
+            import bb
+        except ImportError:
+            self.skipTest("Cannot import bb")
+
+        d = bb.data_smart.DataSmart()
+        d.setVar("PN", "foo")
+
+        d.setVar("PACKAGES", "foo foo-doc foo-dev")
+        pkgs = packages_filter_out_system(d)
+        self.assertEqual(pkgs, [])
+
+        d.setVar("PACKAGES", "foo foo-doc foo-data foo-dev")
+        pkgs = packages_filter_out_system(d)
+        self.assertEqual(pkgs, ["foo-data"])
+
+        d.setVar("PACKAGES", "foo foo-locale-en-gb")
+        pkgs = packages_filter_out_system(d)
+        self.assertEqual(pkgs, [])
+
+        d.setVar("PACKAGES", "foo foo-data foo-locale-en-gb")
+        pkgs = packages_filter_out_system(d)
+        self.assertEqual(pkgs, ["foo-data"])
+
+
+class TestTrimVersion(unittest.TestCase):
+    def test_version_exception(self):
+        with self.assertRaises(TypeError):
+            trim_version(None, 2)
+        with self.assertRaises(TypeError):
+            trim_version((1, 2, 3), 2)
+
+    def test_num_exception(self):
+        with self.assertRaises(ValueError):
+            trim_version("1.2.3", 0)
+        with self.assertRaises(ValueError):
+            trim_version("1.2.3", -1)
+
+    def test_valid(self):
+        self.assertEqual(trim_version("1.2.3", 1), "1")
+        self.assertEqual(trim_version("1.2.3", 2), "1.2")
+        self.assertEqual(trim_version("1.2.3", 3), "1.2.3")
+        self.assertEqual(trim_version("1.2.3", 4), "1.2.3")
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oescripts.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oescripts.py
index 28345dc..29547f5 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/oescripts.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/oescripts.py
@@ -10,38 +10,10 @@
 from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer
 from oeqa.utils.decorators import testcase
 
-class TestScripts(oeSelfTest):
-
-    @testcase(300)
-    def test_cleanup_workdir(self):
-        path = os.path.dirname(get_bb_var('WORKDIR', 'gzip'))
-        old_version_recipe = os.path.join(get_bb_var('COREBASE'), 'meta/recipes-extended/gzip/gzip_1.3.12.bb')
-        old_version = '1.3.12'
-        bitbake("-c clean gzip")
-        bitbake("-c clean -b %s" % old_version_recipe)
-
-        if os.path.exists(path):
-            initial_contents = os.listdir(path)
-        else:
-            initial_contents = []
-
-        bitbake('gzip')
-        intermediary_contents = os.listdir(path)
-        bitbake("-b %s" % old_version_recipe)
-        runCmd('cleanup-workdir')
-        remaining_contents = os.listdir(path)
-
-        expected_contents = [x for x in intermediary_contents if x not in initial_contents]
-        remaining_not_expected = [x for x in remaining_contents if x not in expected_contents]
-        self.assertFalse(remaining_not_expected, msg="Not all necessary content has been deleted from %s: %s" % (path, ', '.join(map(str, remaining_not_expected))))
-        expected_not_remaining = [x for x in expected_contents if x not in remaining_contents]
-        self.assertFalse(expected_not_remaining, msg="The script removed extra contents from %s: %s" % (path, ', '.join(map(str, expected_not_remaining))))
-
 class BuildhistoryDiffTests(BuildhistoryBase):
 
     @testcase(295)
     def test_buildhistory_diff(self):
-        self.add_command_to_tearDown('cleanup-workdir')
         target = 'xcursor-transparent-theme'
         self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
         self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
index 5a63f89..d69c3c8 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/pkgdata.py
@@ -6,7 +6,7 @@
 
 import oeqa.utils.ftools as ftools
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
 from oeqa.utils.decorators import testcase
 
 class OePkgdataUtilTests(oeSelfTest):
@@ -16,21 +16,21 @@
         # Ensure we have the right data in pkgdata
         logger = logging.getLogger("selftest")
         logger.info('Running bitbake to generate pkgdata')
-        bitbake('glibc busybox zlib bash')
+        bitbake('busybox zlib m4')
 
     @testcase(1203)
     def test_lookup_pkg(self):
         # Forward tests
-        result = runCmd('oe-pkgdata-util lookup-pkg "glibc busybox"')
-        self.assertEqual(result.output, 'libc6\nbusybox')
+        result = runCmd('oe-pkgdata-util lookup-pkg "zlib busybox"')
+        self.assertEqual(result.output, 'libz1\nbusybox')
         result = runCmd('oe-pkgdata-util lookup-pkg zlib-dev')
         self.assertEqual(result.output, 'libz-dev')
         result = runCmd('oe-pkgdata-util lookup-pkg nonexistentpkg', ignore_status=True)
         self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
         self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
         # Reverse tests
-        result = runCmd('oe-pkgdata-util lookup-pkg -r "libc6 busybox"')
-        self.assertEqual(result.output, 'glibc\nbusybox')
+        result = runCmd('oe-pkgdata-util lookup-pkg -r "libz1 busybox"')
+        self.assertEqual(result.output, 'zlib\nbusybox')
         result = runCmd('oe-pkgdata-util lookup-pkg -r libz-dev')
         self.assertEqual(result.output, 'zlib-dev')
         result = runCmd('oe-pkgdata-util lookup-pkg -r nonexistentpkg', ignore_status=True)
@@ -41,24 +41,26 @@
     def test_read_value(self):
         result = runCmd('oe-pkgdata-util read-value PN libz1')
         self.assertEqual(result.output, 'zlib')
-        result = runCmd('oe-pkgdata-util read-value PKGSIZE bash')
+        result = runCmd('oe-pkgdata-util read-value PKG libz1')
+        self.assertEqual(result.output, 'libz1')
+        result = runCmd('oe-pkgdata-util read-value PKGSIZE m4')
         pkgsize = int(result.output.strip())
         self.assertGreater(pkgsize, 1, "Size should be greater than 1. %s" % result.output)
 
     @testcase(1198)
     def test_find_path(self):
-        result = runCmd('oe-pkgdata-util find-path /lib/libc.so.6')
-        self.assertEqual(result.output, 'glibc: /lib/libc.so.6')
-        result = runCmd('oe-pkgdata-util find-path /bin/bash')
-        self.assertEqual(result.output, 'bash: /bin/bash')
+        result = runCmd('oe-pkgdata-util find-path /lib/libz.so.1')
+        self.assertEqual(result.output, 'zlib: /lib/libz.so.1')
+        result = runCmd('oe-pkgdata-util find-path /usr/bin/m4')
+        self.assertEqual(result.output, 'm4: /usr/bin/m4')
         result = runCmd('oe-pkgdata-util find-path /not/exist', ignore_status=True)
         self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
         self.assertEqual(result.output, 'ERROR: Unable to find any package producing path /not/exist')
 
     @testcase(1204)
     def test_lookup_recipe(self):
-        result = runCmd('oe-pkgdata-util lookup-recipe "libc6-staticdev busybox"')
-        self.assertEqual(result.output, 'glibc\nbusybox')
+        result = runCmd('oe-pkgdata-util lookup-recipe "libz-staticdev busybox"')
+        self.assertEqual(result.output, 'zlib\nbusybox')
         result = runCmd('oe-pkgdata-util lookup-recipe libz-dbg')
         self.assertEqual(result.output, 'zlib')
         result = runCmd('oe-pkgdata-util lookup-recipe nonexistentpkg', ignore_status=True)
@@ -70,12 +72,11 @@
         # No arguments
         result = runCmd('oe-pkgdata-util list-pkgs')
         pkglist = result.output.split()
-        self.assertIn('glibc-utils', pkglist, "Listed packages: %s" % result.output)
+        self.assertIn('zlib', pkglist, "Listed packages: %s" % result.output)
         self.assertIn('zlib-dev', pkglist, "Listed packages: %s" % result.output)
         # No pkgspec, runtime
         result = runCmd('oe-pkgdata-util list-pkgs -r')
         pkglist = result.output.split()
-        self.assertIn('libc6-utils', pkglist, "Listed packages: %s" % result.output)
         self.assertIn('libz-dev', pkglist, "Listed packages: %s" % result.output)
         # With recipe specified
         result = runCmd('oe-pkgdata-util list-pkgs -p zlib')
@@ -124,10 +125,11 @@
                     curpkg = line.split(':')[0]
                     files[curpkg] = []
             return files
-        base_libdir = get_bb_var('base_libdir')
-        libdir = get_bb_var('libdir')
-        includedir = get_bb_var('includedir')
-        mandir = get_bb_var('mandir')
+        bb_vars = get_bb_vars(['base_libdir', 'libdir', 'includedir', 'mandir'])
+        base_libdir = bb_vars['base_libdir']
+        libdir = bb_vars['libdir']
+        includedir = bb_vars['includedir']
+        mandir = bb_vars['mandir']
         # Test recipe-space package name
         result = runCmd('oe-pkgdata-util list-pkg-files zlib-dev zlib-doc')
         files = splitoutput(result.output)
@@ -205,11 +207,10 @@
         self.track_for_cleanup(tempdir)
         pkglistfile = os.path.join(tempdir, 'pkglist')
         with open(pkglistfile, 'w') as f:
-            f.write('libc6\n')
             f.write('libz1\n')
             f.write('busybox\n')
         result = runCmd('oe-pkgdata-util glob %s "*-dev"' % pkglistfile)
-        desiredresult = ['libc6-dev', 'libz-dev', 'busybox-dev']
+        desiredresult = ['libz-dev', 'busybox-dev']
         self.assertEqual(sorted(result.output.split()), sorted(desiredresult))
         # The following should not error (because when we use this during rootfs construction, sometimes the complementary package won't exist)
         result = runCmd('oe-pkgdata-util glob %s "*-nonexistent"' % pkglistfile)
@@ -222,5 +223,5 @@
 
     @testcase(1206)
     def test_specify_pkgdatadir(self):
-        result = runCmd('oe-pkgdata-util -p %s lookup-pkg glibc' % get_bb_var('PKGDATA_DIR'))
-        self.assertEqual(result.output, 'libc6')
+        result = runCmd('oe-pkgdata-util -p %s lookup-pkg zlib' % get_bb_var('PKGDATA_DIR'))
+        self.assertEqual(result.output, 'libz1')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/prservice.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/prservice.py
index 0b2dfe6..34d4197 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/prservice.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/prservice.py
@@ -12,10 +12,13 @@
 from oeqa.utils.network import get_free_port
 
 class BitbakePrTests(oeSelfTest):
- 
+
+    @classmethod
+    def setUpClass(cls):
+        cls.pkgdata_dir = get_bb_var('PKGDATA_DIR')
+
     def get_pr_version(self, package_name):
-        pkgdata_dir = get_bb_var('PKGDATA_DIR')
-        package_data_file = os.path.join(pkgdata_dir, 'runtime', package_name)
+        package_data_file = os.path.join(self.pkgdata_dir, 'runtime', package_name)
         package_data = ftools.read_file(package_data_file)
         find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data)
         self.assertTrue(find_pr, "No PKG revision found in %s" % package_data_file)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
index 9b66924..dc55a5e 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/recipetool.py
@@ -1,9 +1,11 @@
 import os
 import logging
+import shutil
 import tempfile
 import urllib.parse
 
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import get_bb_vars, create_temp_layer
 from oeqa.utils.decorators import testcase
 from oeqa.selftest import devtool
 
@@ -24,6 +26,7 @@
 
 
 class RecipetoolBase(devtool.DevtoolBase):
+
     def setUpLocal(self):
         self.templayerdir = templayerdir
         self.tempdir = tempfile.mkdtemp(prefix='recipetoolqa')
@@ -64,12 +67,16 @@
 
 
 class RecipetoolTests(RecipetoolBase):
+
     @classmethod
     def setUpClass(cls):
         # Ensure we have the right data in shlibs/pkgdata
         logger = logging.getLogger("selftest")
         logger.info('Running bitbake to generate pkgdata')
         bitbake('-c packagedata base-files coreutils busybox selftest-recipetool-appendfile')
+        bb_vars = get_bb_vars(['COREBASE', 'BBPATH'])
+        cls.corebase = bb_vars['COREBASE']
+        cls.bbpath = bb_vars['BBPATH']
 
     def _try_recipetool_appendfile(self, testrecipe, destfile, newfile, options, expectedlines, expectedfiles):
         cmd = 'recipetool appendfile %s %s %s %s' % (self.templayerdir, destfile, newfile, options)
@@ -103,9 +110,8 @@
         # Now try with a file we know should be an alternative
         # (this is very much a fake example, but one we know is reliably an alternative)
         self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox'])
-        corebase = get_bb_var('COREBASE')
         # Need a test file - should be executable
-        testfile2 = os.path.join(corebase, 'oe-init-build-env')
+        testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
         testfile2name = os.path.basename(testfile2)
         expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
                          '\n',
@@ -134,7 +140,6 @@
 
     @testcase(1173)
     def test_recipetool_appendfile_add(self):
-        corebase = get_bb_var('COREBASE')
         # Try arbitrary file add to a recipe
         expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
                          '\n',
@@ -147,7 +152,7 @@
         self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile'])
         # Try adding another file, this time where the source file is executable
         # (so we're testing that, plus modifying an existing bbappend)
-        testfile2 = os.path.join(corebase, 'oe-init-build-env')
+        testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
         testfile2name = os.path.basename(testfile2)
         expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
                          '\n',
@@ -363,20 +368,22 @@
         # Try adding a recipe
         tempsrc = os.path.join(self.tempdir, 'srctree')
         os.makedirs(tempsrc)
-        recipefile = os.path.join(self.tempdir, 'logrotate_3.8.7.bb')
-        srcuri = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
+        recipefile = os.path.join(self.tempdir, 'logrotate_3.12.3.bb')
+        srcuri = 'https://github.com/logrotate/logrotate/releases/download/3.12.3/logrotate-3.12.3.tar.xz'
         result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
         self.assertTrue(os.path.isfile(recipefile))
         checkvars = {}
         checkvars['LICENSE'] = 'GPLv2'
-        checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=18810669f13b87348459e611d31ab760'
-        checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
-        checkvars['SRC_URI[md5sum]'] = '6b1aa0e0d07eda3c9a2526520850397a'
-        checkvars['SRC_URI[sha256sum]'] = 'dece4bfeb9d8374a0ecafa34be139b5a697db5c926dcc69a9b8715431a22e733'
+        checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
+        checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/releases/download/${PV}/logrotate-${PV}.tar.xz'
+        checkvars['SRC_URI[md5sum]'] = 'a560c57fac87c45b2fc17406cdf79288'
+        checkvars['SRC_URI[sha256sum]'] = '2e6a401cac9024db2288297e3be1a8ab60e7401ba8e91225218aaf4a27e82a07'
         self._test_recipe_contents(recipefile, checkvars, [])
 
     @testcase(1194)
     def test_recipetool_create_git(self):
+        if 'x11' not in get_bb_var('DISTRO_FEATURES'):
+            self.skipTest('Test requires x11 as distro feature')
         # Ensure we have the right data in shlibs/pkgdata
         bitbake('libpng pango libx11 libxext jpeg libcheck')
         # Try adding a recipe
@@ -480,6 +487,46 @@
         inherits = ['pkgconfig', 'autotools']
         self._test_recipe_contents(recipefile, checkvars, inherits)
 
+    def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
+        dstdir = basedstdir
+        self.assertTrue(os.path.exists(dstdir))
+        for p in paths:
+            dstdir = os.path.join(dstdir, p)
+            if not os.path.exists(dstdir):
+                os.makedirs(dstdir)
+                self.track_for_cleanup(dstdir)
+        dstfile = os.path.join(dstdir, os.path.basename(srcfile))
+        if srcfile != dstfile:
+            shutil.copy(srcfile, dstfile)
+            self.track_for_cleanup(dstfile)
+
+    def test_recipetool_load_plugin(self):
+        """Test that recipetool loads only the first found plugin in BBPATH."""
+
+        recipetool = runCmd("which recipetool")
+        fromname = runCmd("recipetool --quiet pluginfile")
+        srcfile = fromname.output
+        searchpath = self.bbpath.split(':') + [os.path.dirname(recipetool.output)]
+        plugincontent = []
+        with open(srcfile) as fh:
+            plugincontent = fh.readlines()
+        try:
+            self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
+            for path in searchpath:
+                self._copy_file_with_cleanup(srcfile, path, 'lib', 'recipetool')
+            result = runCmd("recipetool --quiet count")
+            self.assertEqual(result.output, '1')
+            result = runCmd("recipetool --quiet multiloaded")
+            self.assertEqual(result.output, "no")
+            for path in searchpath:
+                result = runCmd("recipetool --quiet bbdir")
+                self.assertEqual(result.output, path)
+                os.unlink(os.path.join(result.output, 'lib', 'recipetool', 'bbpath.py'))
+        finally:
+            with open(srcfile, 'w') as fh:
+                fh.writelines(plugincontent)
+
+
 class RecipetoolAppendsrcBase(RecipetoolBase):
     def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles):
         cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile)
@@ -555,20 +602,23 @@
 
         self._try_recipetool_appendsrcfiles(testrecipe, newfiles, expectedfiles=expectedfiles, destdir=destdir, options=options)
 
-        src_uri = get_bb_var('SRC_URI', testrecipe).split()
+        bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'FILESEXTRAPATHS'], testrecipe)
+        src_uri = bb_vars['SRC_URI'].split()
         for f in expectedfiles:
             if destdir:
                 self.assertIn('file://%s;subdir=%s' % (f, destdir), src_uri)
             else:
                 self.assertIn('file://%s' % f, src_uri)
 
-        recipefile = get_bb_var('FILE', testrecipe)
+        recipefile = bb_vars['FILE']
         bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
         filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe)
-        filesextrapaths = get_bb_var('FILESEXTRAPATHS', testrecipe).split(':')
+        filesextrapaths = bb_vars['FILESEXTRAPATHS'].split(':')
         self.assertIn(filesdir, filesextrapaths)
 
 
+
+
 class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase):
 
     @testcase(1273)
@@ -594,8 +644,9 @@
     @testcase(1280)
     def test_recipetool_appendsrcfile_srcdir_basic(self):
         testrecipe = 'bash'
-        srcdir = get_bb_var('S', testrecipe)
-        workdir = get_bb_var('WORKDIR', testrecipe)
+        bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
+        srcdir = bb_vars['S']
+        workdir = bb_vars['WORKDIR']
         subdir = os.path.relpath(srcdir, workdir)
         self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir)
 
@@ -620,8 +671,9 @@
     def test_recipetool_appendsrcfile_replace_file_srcdir(self):
         testrecipe = 'bash'
         filepath = 'Makefile.in'
-        srcdir = get_bb_var('S', testrecipe)
-        workdir = get_bb_var('WORKDIR', testrecipe)
+        bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
+        srcdir = bb_vars['S']
+        workdir = bb_vars['WORKDIR']
         subdir = os.path.relpath(srcdir, workdir)
 
         self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/runqemu.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runqemu.py
new file mode 100644
index 0000000..58c6f96
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runqemu.py
@@ -0,0 +1,140 @@
+#
+# Copyright (c) 2017 Wind River Systems, Inc.
+#
+
+import re
+import logging
+
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import bitbake, runqemu, get_bb_var
+from oeqa.utils.decorators import testcase
+
+class RunqemuTests(oeSelfTest):
+    """Runqemu test class"""
+
+    image_is_ready = False
+    deploy_dir_image = ''
+
+    def setUpLocal(self):
+        self.recipe = 'core-image-minimal'
+        self.machine =  'qemux86-64'
+        self.fstypes = "ext4 iso hddimg vmdk qcow2 vdi"
+        self.cmd_common = "runqemu nographic"
+
+        # Avoid emit the same record multiple times.
+        mainlogger = logging.getLogger("BitBake.Main")
+        mainlogger.propagate = False
+
+        self.write_config(
+"""
+MACHINE = "%s"
+IMAGE_FSTYPES = "%s"
+# 10 means 1 second
+SYSLINUX_TIMEOUT = "10"
+"""
+% (self.machine, self.fstypes)
+        )
+
+        if not RunqemuTests.image_is_ready:
+            RunqemuTests.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+            bitbake(self.recipe)
+            RunqemuTests.image_is_ready = True
+
+    @testcase(2001)
+    def test_boot_machine(self):
+        """Test runqemu machine"""
+        cmd = "%s %s" % (self.cmd_common, self.machine)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
+
+    @testcase(2002)
+    def test_boot_machine_ext4(self):
+        """Test runqemu machine ext4"""
+        cmd = "%s %s ext4" % (self.cmd_common, self.machine)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue('rootfs.ext4' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2003)
+    def test_boot_machine_iso(self):
+        """Test runqemu machine iso"""
+        cmd = "%s %s iso" % (self.cmd_common, self.machine)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue(' -cdrom ' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2004)
+    def test_boot_recipe_image(self):
+        """Test runqemu recipe-image"""
+        cmd = "%s %s" % (self.cmd_common, self.recipe)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
+
+    @testcase(2005)
+    def test_boot_recipe_image_vmdk(self):
+        """Test runqemu recipe-image vmdk"""
+        cmd = "%s %s vmdk" % (self.cmd_common, self.recipe)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue('format=vmdk' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2006)
+    def test_boot_recipe_image_vdi(self):
+        """Test runqemu recipe-image vdi"""
+        cmd = "%s %s vdi" % (self.cmd_common, self.recipe)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue('format=vdi' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2007)
+    def test_boot_deploy(self):
+        """Test runqemu deploy_dir_image"""
+        cmd = "%s %s" % (self.cmd_common, self.deploy_dir_image)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
+
+    @testcase(2008)
+    def test_boot_deploy_hddimg(self):
+        """Test runqemu deploy_dir_image hddimg"""
+        cmd = "%s %s hddimg" % (self.cmd_common, self.deploy_dir_image)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s" % cmd)
+
+    @testcase(2009)
+    def test_boot_machine_slirp(self):
+        """Test runqemu machine slirp"""
+        cmd = "%s slirp %s" % (self.cmd_common, self.machine)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue(' -netdev user' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2009)
+    def test_boot_machine_slirp_qcow2(self):
+        """Test runqemu machine slirp qcow2"""
+        cmd = "%s slirp qcow2 %s" % (self.cmd_common, self.machine)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            with open(qemu.qemurunnerlog) as f:
+                self.assertTrue('format=qcow2' in f.read(), "Failed: %s" % cmd)
+
+    @testcase(2010)
+    def test_boot_qemu_boot(self):
+        """Test runqemu /path/to/image.qemuboot.conf"""
+        qemuboot_conf = "%s-%s.qemuboot.conf" % (self.recipe, self.machine)
+        qemuboot_conf = os.path.join(self.deploy_dir_image, qemuboot_conf)
+        if not os.path.exists(qemuboot_conf):
+            self.skipTest("%s not found" % qemuboot_conf)
+        cmd = "%s %s" % (self.cmd_common, qemuboot_conf)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
+
+    @testcase(2011)
+    def test_boot_rootfs(self):
+        """Test runqemu /path/to/rootfs.ext4"""
+        rootfs = "%s-%s.ext4" % (self.recipe, self.machine)
+        rootfs = os.path.join(self.deploy_dir_image, rootfs)
+        if not os.path.exists(rootfs):
+            self.skipTest("%s not found" % rootfs)
+        cmd = "%s %s" % (self.cmd_common, rootfs)
+        with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
+            self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py
index c2d5b45..e498d04 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/runtime-test.py
@@ -1,10 +1,15 @@
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
 from oeqa.utils.decorators import testcase
 import os
+import re
 
 class TestExport(oeSelfTest):
 
+    @classmethod
+    def tearDownClass(cls):
+        runCmd("rm -rf /tmp/sdk")
+
     def test_testexport_basic(self):
         """
         Summary: Check basic testexport functionality with only ping test enabled.
@@ -26,22 +31,23 @@
         bitbake('core-image-minimal')
         bitbake('-c testexport core-image-minimal')
 
-        # Verify if TEST_EXPORT_DIR was created
         testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
+
+        # Verify if TEST_EXPORT_DIR was created
         isdir = os.path.isdir(testexport_dir)
         self.assertEqual(True, isdir, 'Failed to create testexport dir: %s' % testexport_dir)
 
         with runqemu('core-image-minimal') as qemu:
             # Attempt to run runexported.py to perform ping test
-            runexported_path = os.path.join(testexport_dir, "runexported.py")
-            testdata_path = os.path.join(testexport_dir, "testdata.json")
-            cmd = "%s -t %s -s %s %s" % (runexported_path, qemu.ip, qemu.server_ip, testdata_path)
+            test_path = os.path.join(testexport_dir, "oe-test")
+            data_file = os.path.join(testexport_dir, 'data', 'testdata.json')
+            manifest = os.path.join(testexport_dir, 'data', 'manifest')
+            cmd = ("%s runtime --test-data-file %s --packages-manifest %s "
+                   "--target-ip %s --server-ip %s --quiet"
+                  % (test_path, data_file, manifest, qemu.ip, qemu.server_ip))
             result = runCmd(cmd)
-            self.assertEqual(0, result.status, 'runexported.py returned a non 0 status')
-
             # Verify ping test was succesful
-            failure = True if 'FAIL' in result.output else False
-            self.assertNotEqual(True, failure, 'ping test failed')
+            self.assertEqual(0, result.status, 'oe-test runtime returned a non 0 status')
 
     def test_testexport_sdk(self):
         """
@@ -60,7 +66,6 @@
         features += 'TEST_SERVER_IP = "192.168.7.1"\n'
         features += 'TEST_TARGET_IP = "192.168.7.1"\n'
         features += 'TEST_SUITES = "ping"\n'
-        features += 'TEST_SUITES_TAGS = "selftest_sdk"\n'
         features += 'TEST_EXPORT_SDK_ENABLED = "1"\n'
         features += 'TEST_EXPORT_SDK_PACKAGES = "nativesdk-tar"\n'
         self.write_config(features)
@@ -69,19 +74,31 @@
         bitbake('core-image-minimal')
         bitbake('-c testexport core-image-minimal')
 
-        # Check for SDK
-        testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
-        sdk_dir = get_bb_var('TEST_EXPORT_SDK_DIR', 'core-image-minimal')
-        tarball_name = "%s.sh" % get_bb_var('TEST_EXPORT_SDK_NAME', 'core-image-minimal')
-        tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
-        self.assertEqual(os.path.isfile(tarball_path), True, "Couldn't find SDK tarball: %s" % tarball_path)
+        needed_vars = ['TEST_EXPORT_DIR', 'TEST_EXPORT_SDK_DIR', 'TEST_EXPORT_SDK_NAME']
+        bb_vars = get_bb_vars(needed_vars, 'core-image-minimal')
+        testexport_dir = bb_vars['TEST_EXPORT_DIR']
+        sdk_dir = bb_vars['TEST_EXPORT_SDK_DIR']
+        sdk_name = bb_vars['TEST_EXPORT_SDK_NAME']
 
-        # Run runexported.py
-        runexported_path = os.path.join(testexport_dir, "runexported.py")
-        testdata_path = os.path.join(testexport_dir, "testdata.json")
-        cmd = "%s %s" % (runexported_path, testdata_path)
-        result = runCmd(cmd)
-        self.assertEqual(0, result.status, 'runexported.py returned a non 0 status')
+        # Check for SDK
+        tarball_name = "%s.sh" % sdk_name
+        tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
+        msg = "Couldn't find SDK tarball: %s" % tarball_path
+        self.assertEqual(os.path.isfile(tarball_path), True, msg)
+
+        # Extract SDK and run tar from SDK
+        result = runCmd("%s -y -d /tmp/sdk" % tarball_path)
+        self.assertEqual(0, result.status, "Couldn't extract SDK")
+
+        env_script = result.output.split()[-1]
+        result = runCmd(". %s; which tar" % env_script, shell=True)
+        self.assertEqual(0, result.status, "Couldn't setup SDK environment")
+        is_sdk_tar = True if "/tmp/sdk" in result.output else False
+        self.assertTrue(is_sdk_tar, "Couldn't setup SDK environment")
+
+        tar_sdk = result.output
+        result = runCmd("%s --version" % tar_sdk)
+        self.assertEqual(0, result.status, "Couldn't run tar from SDK")
 
 
 class TestImage(oeSelfTest):
@@ -90,16 +107,131 @@
         """
         Summary: Check install packages functionality for testimage/testexport.
         Expected: 1. Import tests from a directory other than meta.
-                  2. Check install/unistall of socat.
+                  2. Check install/uninstall of socat.
+                  3. Check that remote package feeds can be accessed
         Product: oe-core
         Author: Mariano Lopez <mariano.lopez@intel.com>
+        Author: Alexander Kanavin <alexander.kanavin@intel.com>
         """
+        if get_bb_var('DISTRO') == 'poky-tiny':
+            self.skipTest('core-image-full-cmdline not buildable for poky-tiny')
 
         features = 'INHERIT += "testimage"\n'
         features += 'TEST_SUITES = "ping ssh selftest"\n'
-        features += 'TEST_SUITES_TAGS = "selftest_package_install"\n'
+        # We don't yet know what the server ip and port will be - they will be patched
+        # in at the start of the on-image test
+        features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n'
+        features += 'EXTRA_IMAGE_FEATURES += "package-management"\n'
+        features += 'PACKAGE_CLASSES = "package_rpm"'
         self.write_config(features)
 
         # Build core-image-sato and testimage
         bitbake('core-image-full-cmdline socat')
         bitbake('-c testimage core-image-full-cmdline')
+
+class Postinst(oeSelfTest):
+    @testcase(1540)
+    def test_verify_postinst(self):
+        """
+        Summary: The purpose of this test is to verify the execution order of postinst Bugzilla ID: [5319]
+        Expected :
+        1. Compile a minimal image.
+        2. The compiled image will add the created layer with the recipes postinst[ abdpt]
+        3. Run qemux86
+        4. Validate the task execution order
+        Author: Francisco Pedraza <francisco.j.pedraza.gonzalez@intel.com>
+        """
+        features = 'INHERIT += "testimage"\n'
+        features += 'CORE_IMAGE_EXTRA_INSTALL += "postinst-at-rootfs \
+postinst-delayed-a \
+postinst-delayed-b \
+postinst-delayed-d \
+postinst-delayed-p \
+postinst-delayed-t \
+"\n'
+        self.write_config(features)
+
+        bitbake('core-image-minimal -f ')
+
+        postinst_list = ['100-postinst-at-rootfs',
+                         '101-postinst-delayed-a',
+                         '102-postinst-delayed-b',
+                         '103-postinst-delayed-d',
+                         '104-postinst-delayed-p',
+                         '105-postinst-delayed-t']
+        path_workdir = get_bb_var('WORKDIR','core-image-minimal')
+        workspacedir = 'testimage/qemu_boot_log'
+        workspacedir = os.path.join(path_workdir, workspacedir)
+        rexp = re.compile("^Running postinst .*/(?P<postinst>.*)\.\.\.$")
+        with runqemu('core-image-minimal') as qemu:
+            with open(workspacedir) as f:
+                found = False
+                idx = 0
+                for line in f.readlines():
+                    line = line.strip().replace("^M","")
+                    if not line: # To avoid empty lines
+                        continue
+                    m = rexp.search(line)
+                    if m:
+                        self.assertEqual(postinst_list[idx], m.group('postinst'), "Fail")
+                        idx = idx+1
+                        found = True
+                    elif found:
+                        self.assertEqual(idx, len(postinst_list), "Not found all postinsts")
+                        break
+
+    @testcase(1545)
+    def test_postinst_rootfs_and_boot(self):
+        """
+        Summary:        The purpose of this test case is to verify Post-installation
+                        scripts are called when rootfs is created and also test
+                        that script can be delayed to run at first boot.
+        Dependencies:   NA
+        Steps:          1. Add proper configuration to local.conf file
+                        2. Build a "core-image-minimal" image
+                        3. Verify that file created by postinst_rootfs recipe is
+                           present on rootfs dir.
+                        4. Boot the image created on qemu and verify that the file
+                           created by postinst_boot recipe is present on image.
+        Expected:       The files are successfully created during rootfs and boot
+                        time for 3 different package managers: rpm,ipk,deb and
+                        for initialization managers: sysvinit and systemd.
+
+        """
+        file_rootfs_name = "this-was-created-at-rootfstime"
+        fileboot_name = "this-was-created-at-first-boot"
+        rootfs_pkg = 'postinst-at-rootfs'
+        boot_pkg = 'postinst-delayed-a'
+        #Step 1
+        features = 'MACHINE = "qemux86"\n'
+        features += 'CORE_IMAGE_EXTRA_INSTALL += "%s %s "\n'% (rootfs_pkg, boot_pkg)
+        features += 'IMAGE_FEATURES += "ssh-server-openssh"\n'
+        for init_manager in ("sysvinit", "systemd"):
+            #for sysvinit no extra configuration is needed,
+            if (init_manager is "systemd"):
+                features += 'DISTRO_FEATURES_append = " systemd"\n'
+                features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n'
+                features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n'
+                features += 'VIRTUAL-RUNTIME_initscripts = ""\n'
+            for classes in ("package_rpm package_deb package_ipk",
+                            "package_deb package_rpm package_ipk",
+                            "package_ipk package_deb package_rpm"):
+                features += 'PACKAGE_CLASSES = "%s"\n' % classes
+                self.write_config(features)
+
+                #Step 2
+                bitbake('core-image-minimal')
+
+                #Step 3
+                file_rootfs_created = os.path.join(get_bb_var('IMAGE_ROOTFS',"core-image-minimal"),
+                                                   file_rootfs_name)
+                found = os.path.isfile(file_rootfs_created)
+                self.assertTrue(found, "File %s was not created at rootfs time by %s" % \
+                                (file_rootfs_name, rootfs_pkg))
+
+                #Step 4
+                testcommand = 'ls /etc/'+fileboot_name
+                with runqemu('core-image-minimal') as qemu:
+                    sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
+                    result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
+                    self.assertEqual(result.status, 0, 'File %s was not created at firts boot'% fileboot_name)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
index 606bfd3..0ac3d1f 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/signing.py
@@ -1,5 +1,5 @@
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
 import os
 import glob
 import re
@@ -27,15 +27,17 @@
         cls.pub_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.pub")
         cls.secret_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.secret")
 
-        runCmd('gpg --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path))
+        runCmd('gpg --batch --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path))
 
     @testcase(1362)
     def test_signing_packages(self):
         """
         Summary:     Test that packages can be signed in the package feed
         Expected:    Package should be signed with the correct key
+        Expected:    Images can be created from signed packages
         Product:     oe-core
         Author:      Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+        Author:      Alexander Kanavin <alexander.kanavin@intel.com>
         AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
         """
         import oe.packagedata
@@ -49,7 +51,6 @@
         feature = 'INHERIT += "sign_rpm"\n'
         feature += 'RPM_GPG_PASSPHRASE = "test123"\n'
         feature += 'RPM_GPG_NAME = "testuser"\n'
-        feature += 'RPM_GPG_PUBKEY = "%s"\n' % self.pub_key_path
         feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
 
         self.write_config(feature)
@@ -59,30 +60,38 @@
 
         self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
 
-        pkgdatadir = get_bb_var('PKGDATA_DIR', test_recipe)
+        needed_vars = ['PKGDATA_DIR', 'DEPLOY_DIR_RPM', 'PACKAGE_ARCH', 'STAGING_BINDIR_NATIVE']
+        bb_vars = get_bb_vars(needed_vars, test_recipe)
+        pkgdatadir = bb_vars['PKGDATA_DIR']
         pkgdata = oe.packagedata.read_pkgdatafile(pkgdatadir + "/runtime/ed")
         if 'PKGE' in pkgdata:
             pf = pkgdata['PN'] + "-" + pkgdata['PKGE'] + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
         else:
             pf = pkgdata['PN'] + "-" + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
-        deploy_dir_rpm = get_bb_var('DEPLOY_DIR_RPM', test_recipe)
-        package_arch = get_bb_var('PACKAGE_ARCH', test_recipe).replace('-', '_')
-        staging_bindir_native = get_bb_var('STAGING_BINDIR_NATIVE')
+        deploy_dir_rpm = bb_vars['DEPLOY_DIR_RPM']
+        package_arch = bb_vars['PACKAGE_ARCH'].replace('-', '_')
+        staging_bindir_native = bb_vars['STAGING_BINDIR_NATIVE']
 
         pkg_deploy = os.path.join(deploy_dir_rpm, package_arch, '.'.join((pf, package_arch, 'rpm')))
 
         # Use a temporary rpmdb
         rpmdb = tempfile.mkdtemp(prefix='oeqa-rpmdb')
 
-        runCmd('%s/rpm --define "_dbpath %s" --import %s' %
+        runCmd('%s/rpmkeys --define "_dbpath %s" --import %s' %
                (staging_bindir_native, rpmdb, self.pub_key_path))
 
-        ret = runCmd('%s/rpm --define "_dbpath %s" --checksig %s' %
+        ret = runCmd('%s/rpmkeys --define "_dbpath %s" --checksig %s' %
                      (staging_bindir_native, rpmdb, pkg_deploy))
         # tmp/deploy/rpm/i586/ed-1.9-r0.i586.rpm: rsa sha1 md5 OK
-        self.assertIn('rsa sha1 md5 OK', ret.output, 'Package signed incorrectly.')
+        self.assertIn('rsa sha1 (md5) pgp md5 OK', ret.output, 'Package signed incorrectly.')
         shutil.rmtree(rpmdb)
 
+        #Check that an image can be built from signed packages
+        self.add_command_to_tearDown('bitbake -c clean core-image-minimal')
+        bitbake('-c clean core-image-minimal')
+        bitbake('core-image-minimal')
+
+
     @testcase(1382)
     def test_signing_sstate_archive(self):
         """
@@ -101,13 +110,7 @@
         self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
         self.add_command_to_tearDown('rm -rf %s' % sstatedir)
 
-        # Determine the pub key signature
-        ret = runCmd('gpg --homedir %s --list-keys' % self.gpg_dir)
-        pub_key = re.search(r'^pub\s+\S+/(\S+)\s+', ret.output, re.M)
-        self.assertIsNotNone(pub_key, 'Failed to determine the public key signature.')
-        pub_key = pub_key.group(1)
-
-        feature = 'SSTATE_SIG_KEY ?= "%s"\n' % pub_key
+        feature = 'SSTATE_SIG_KEY ?= "testuser"\n'
         feature += 'SSTATE_SIG_PASSPHRASE ?= "test123"\n'
         feature += 'SSTATE_VERIFY_SIG ?= "1"\n'
         feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstate.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstate.py
index 5989724..f54bc41 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstate.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstate.py
@@ -6,16 +6,24 @@
 
 import oeqa.utils.ftools as ftools
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
+from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_test_layer
 
 
 class SStateBase(oeSelfTest):
 
     def setUpLocal(self):
         self.temp_sstate_location = None
-        self.sstate_path = get_bb_var('SSTATE_DIR')
-        self.distro = get_bb_var('NATIVELSBSTRING')
-        self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro)
+        needed_vars = ['SSTATE_DIR', 'NATIVELSBSTRING', 'TCLIBC', 'TUNE_ARCH',
+                       'TOPDIR', 'TARGET_VENDOR', 'TARGET_OS']
+        bb_vars = get_bb_vars(needed_vars)
+        self.sstate_path = bb_vars['SSTATE_DIR']
+        self.hostdistro = bb_vars['NATIVELSBSTRING']
+        self.tclibc = bb_vars['TCLIBC']
+        self.tune_arch = bb_vars['TUNE_ARCH']
+        self.topdir = bb_vars['TOPDIR']
+        self.target_vendor = bb_vars['TARGET_VENDOR']
+        self.target_os = bb_vars['TARGET_OS']
+        self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
 
     # Creates a special sstate configuration with the option to add sstate mirrors
     def config_sstate(self, temp_sstate_location=False, add_local_mirrors=[]):
@@ -26,9 +34,10 @@
             config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path
             self.append_config(config_temp_sstate)
             self.track_for_cleanup(temp_sstate_path)
-        self.sstate_path = get_bb_var('SSTATE_DIR')
-        self.distro = get_bb_var('NATIVELSBSTRING')
-        self.distro_specific_sstate = os.path.join(self.sstate_path, self.distro)
+        bb_vars = get_bb_vars(['SSTATE_DIR', 'NATIVELSBSTRING'])
+        self.sstate_path = bb_vars['SSTATE_DIR']
+        self.hostdistro = bb_vars['NATIVELSBSTRING']
+        self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
 
         if add_local_mirrors:
             config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""'
@@ -42,7 +51,7 @@
     def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True):
         result = []
         for root, dirs, files in os.walk(self.sstate_path):
-            if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.distro, root):
+            if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.hostdistro, root):
                 for f in files:
                     if re.search(filename_regex, f):
                         result.append(f)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
index f99d746..e35ddff 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/sstatetests.py
@@ -41,22 +41,19 @@
 
     @testcase(975)
     def test_sstate_creation_distro_specific_pass(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
+        self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
 
     @testcase(1374)
     def test_sstate_creation_distro_specific_fail(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_sstate_creation(['binutils-cross-'+ targetarch, 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False)
+        self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False)
 
     @testcase(976)
     def test_sstate_creation_distro_nonspecific_pass(self):
-        self.run_test_sstate_creation(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
+        self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
 
     @testcase(1375)
     def test_sstate_creation_distro_nonspecific_fail(self):
-        self.run_test_sstate_creation(['glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
-
+        self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
 
     # Test the sstate files deletion part of the do_cleansstate task
     def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True):
@@ -77,17 +74,19 @@
 
     @testcase(977)
     def test_cleansstate_task_distro_specific_nonspecific(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_cleansstate_task(['binutils-cross-' + targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
+        targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
+        targets.append('linux-libc-headers')
+        self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
 
     @testcase(1376)
     def test_cleansstate_task_distro_nonspecific(self):
-        self.run_test_cleansstate_task(['glibc-initial'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
+        self.run_test_cleansstate_task(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
 
     @testcase(1377)
     def test_cleansstate_task_distro_specific(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_cleansstate_task(['binutils-cross-'+ targetarch, 'binutils-native', 'glibc-initial'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
+        targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
+        targets.append('linux-libc-headers')
+        self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
 
 
     # Test rebuilding of distro-specific sstate files
@@ -124,13 +123,11 @@
 
     @testcase(175)
     def test_rebuild_distro_specific_sstate_cross_native_targets(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch, 'binutils-native'], temp_sstate_location=True)
+        self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch, 'binutils-native'], temp_sstate_location=True)
 
     @testcase(1372)
     def test_rebuild_distro_specific_sstate_cross_target(self):
-        targetarch = get_bb_var('TUNE_ARCH')
-        self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + targetarch], temp_sstate_location=True)
+        self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch], temp_sstate_location=True)
 
     @testcase(1373)
     def test_rebuild_distro_specific_sstate_native_target(self):
@@ -145,10 +142,9 @@
         self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements')
         self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path])
 
-        # If buildhistory is enabled, we need to disable version-going-backwards QA checks for this test. It may report errors otherwise.
-        if ('buildhistory' in get_bb_var('USER_CLASSES')) or ('buildhistory' in get_bb_var('INHERIT')):
-            remove_errors_config = 'ERROR_QA_remove = "version-going-backwards"'
-            self.append_config(remove_errors_config)
+        # If buildhistory is enabled, we need to disable version-going-backwards
+        # QA checks for this test. It may report errors otherwise.
+        self.append_config('ERROR_QA_remove = "version-going-backwards"')
 
         # For not this only checks if random sstate tasks are handled correctly as a group.
         # In the future we should add control over what tasks we check for.
@@ -229,8 +225,6 @@
         manually and check using bitbake -S.
         """
 
-        topdir = get_bb_var('TOPDIR')
-        targetvendor = get_bb_var('TARGET_VENDOR')
         self.write_config("""
 MACHINE = "qemux86"
 TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
@@ -239,7 +233,7 @@
 SDKMACHINE = "x86_64"
 PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
         bitbake("core-image-sato -S none")
         self.write_config("""
 MACHINE = "qemux86"
@@ -249,7 +243,7 @@
 SDKMACHINE = "i686"
 PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
         bitbake("core-image-sato -S none")
 
         def get_files(d):
@@ -262,9 +256,9 @@
                     continue
                 f.extend(os.path.join(root, name) for name in files)
             return f
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/")
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
-        files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash").replace("i686-linux", "x86_64-linux").replace("i686" + targetvendor + "-linux", "x86_64" + targetvendor + "-linux", ) for x in files2]
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
+        files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash").replace("i686-linux", "x86_64-linux").replace("i686" + self.target_vendor + "-linux", "x86_64" + self.target_vendor + "-linux", ) for x in files2]
         self.maxDiff = None
         self.assertCountEqual(files1, files2)
 
@@ -277,18 +271,17 @@
         builds, override the variables manually and check using bitbake -S.
         """
 
-        topdir = get_bb_var('TOPDIR')
         self.write_config("""
 TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
 NATIVELSBSTRING = \"DistroA\"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
         bitbake("core-image-sato -S none")
         self.write_config("""
 TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
 NATIVELSBSTRING = \"DistroB\"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
         bitbake("core-image-sato -S none")
 
         def get_files(d):
@@ -296,8 +289,8 @@
             for root, dirs, files in os.walk(d):
                 f.extend(os.path.join(root, name) for name in files)
             return f
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/")
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
         files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
         self.maxDiff = None
         self.assertCountEqual(files1, files2)
@@ -346,14 +339,11 @@
 
     def sstate_allarch_samesigs(self, configA, configB):
 
-        topdir = get_bb_var('TOPDIR')
-        targetos = get_bb_var('TARGET_OS')
-        targetvendor = get_bb_var('TARGET_VENDOR')
         self.write_config(configA)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
         bitbake("world meta-toolchain -S none")
         self.write_config(configB)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
         bitbake("world meta-toolchain -S none")
 
         def get_files(d):
@@ -367,15 +357,15 @@
                         (_, task, _, shash) = name.rsplit(".", 3)
                         f[os.path.join(os.path.basename(root), task)] = shash
             return f
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/all" + targetvendor + "-" + targetos)
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/all" + targetvendor + "-" + targetos)
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/all" + self.target_vendor + "-" + self.target_os)
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/all" + self.target_vendor + "-" + self.target_os)
         self.maxDiff = None
         self.assertEqual(files1, files2)
 
-        nativesdkdir = os.path.basename(glob.glob(topdir + "/tmp-sstatesamehash/stamps/*-nativesdk*-linux")[0])
+        nativesdkdir = os.path.basename(glob.glob(self.topdir + "/tmp-sstatesamehash/stamps/*-nativesdk*-linux")[0])
 
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/" + nativesdkdir)
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/" + nativesdkdir)
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/" + nativesdkdir)
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/" + nativesdkdir)
         self.maxDiff = None
         self.assertEqual(files1, files2)
 
@@ -387,9 +377,6 @@
         qemux86copy machine to test this. Also include multilibs in the test.
         """
 
-        topdir = get_bb_var('TOPDIR')
-        targetos = get_bb_var('TARGET_OS')
-        targetvendor = get_bb_var('TARGET_VENDOR')
         self.write_config("""
 TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
 MACHINE = \"qemux86\"
@@ -397,7 +384,7 @@
 MULTILIBS = "multilib:lib32"
 DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
         bitbake("world meta-toolchain -S none")
         self.write_config("""
 TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
@@ -406,7 +393,7 @@
 MULTILIBS = "multilib:lib32"
 DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
         bitbake("world meta-toolchain -S none")
 
         def get_files(d):
@@ -420,8 +407,8 @@
                     if "do_build" not in name and "do_populate_sdk" not in name:
                         f.append(os.path.join(root, name))
             return f
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps")
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps")
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps")
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps")
         files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
         self.maxDiff = None
         self.assertCountEqual(files1, files2)
@@ -433,8 +420,6 @@
         classes inherits should be the same.
         """
 
-        topdir = get_bb_var('TOPDIR')
-        targetvendor = get_bb_var('TARGET_VENDOR')
         self.write_config("""
 TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
 BB_NUMBER_THREADS = "1"
@@ -445,8 +430,8 @@
 INHERIT_remove = "buildstats-summary buildhistory uninative"
 http_proxy = ""
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash")
-        self.track_for_cleanup(topdir + "/download1")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+        self.track_for_cleanup(self.topdir + "/download1")
         bitbake("world meta-toolchain -S none")
         self.write_config("""
 TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
@@ -460,8 +445,8 @@
 INHERIT += "buildstats-summary buildhistory"
 http_proxy = "http://example.com/"
 """)
-        self.track_for_cleanup(topdir + "/tmp-sstatesamehash2")
-        self.track_for_cleanup(topdir + "/download2")
+        self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+        self.track_for_cleanup(self.topdir + "/download2")
         bitbake("world meta-toolchain -S none")
 
         def get_files(d):
@@ -473,8 +458,8 @@
                     base = os.sep.join(root.rsplit(os.sep, 2)[-2:] + [name])
                     f[base] = shash
             return f
-        files1 = get_files(topdir + "/tmp-sstatesamehash/stamps/")
-        files2 = get_files(topdir + "/tmp-sstatesamehash2/stamps/")
+        files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+        files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
         # Remove items that are identical in both sets
         for k,v in files1.items() & files2.items():
             del files1[k]
@@ -487,8 +472,8 @@
             if k in files1 and k in files2:
                 print("%s differs:" % k)
                 print(subprocess.check_output(("bitbake-diffsigs",
-                                               topdir + "/tmp-sstatesamehash/stamps/" + k + "." + files1[k],
-                                               topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k])))
+                                               self.topdir + "/tmp-sstatesamehash/stamps/" + k + "." + files1[k],
+                                               self.topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k])))
             elif k in files1 and k not in files2:
                 print("%s in files1" % k)
             elif k not in files1 and k in files2:
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/tinfoil.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/tinfoil.py
new file mode 100644
index 0000000..73a0c3b
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/tinfoil.py
@@ -0,0 +1,190 @@
+import unittest
+import os
+import re
+import bb.tinfoil
+
+from oeqa.selftest.base import oeSelfTest
+from oeqa.utils.commands import runCmd
+from oeqa.utils.decorators import testcase
+
+class TinfoilTests(oeSelfTest):
+    """ Basic tests for the tinfoil API """
+
+    @testcase(1568)
+    def test_getvar(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(True)
+            machine = tinfoil.config_data.getVar('MACHINE')
+            if not machine:
+                self.fail('Unable to get MACHINE value - returned %s' % machine)
+
+    @testcase(1569)
+    def test_expand(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(True)
+            expr = '${@os.getpid()}'
+            pid = tinfoil.config_data.expand(expr)
+            if not pid:
+                self.fail('Unable to expand "%s" - returned %s' % (expr, pid))
+
+    @testcase(1570)
+    def test_getvar_bb_origenv(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(True)
+            origenv = tinfoil.config_data.getVar('BB_ORIGENV', False)
+            if not origenv:
+                self.fail('Unable to get BB_ORIGENV value - returned %s' % origenv)
+            self.assertEqual(origenv.getVar('HOME', False), os.environ['HOME'])
+
+    @testcase(1571)
+    def test_parse_recipe(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            testrecipe = 'mdadm'
+            best = tinfoil.find_best_provider(testrecipe)
+            if not best:
+                self.fail('Unable to find recipe providing %s' % testrecipe)
+            rd = tinfoil.parse_recipe_file(best[3])
+            self.assertEqual(testrecipe, rd.getVar('PN'))
+
+    @testcase(1572)
+    def test_parse_recipe_copy_expand(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            testrecipe = 'mdadm'
+            best = tinfoil.find_best_provider(testrecipe)
+            if not best:
+                self.fail('Unable to find recipe providing %s' % testrecipe)
+            rd = tinfoil.parse_recipe_file(best[3])
+            # Check we can get variable values
+            self.assertEqual(testrecipe, rd.getVar('PN'))
+            # Check that expanding a value that includes a variable reference works
+            self.assertEqual(testrecipe, rd.getVar('BPN'))
+            # Now check that changing the referenced variable's value in a copy gives that
+            # value when expanding
+            localdata = bb.data.createCopy(rd)
+            localdata.setVar('PN', 'hello')
+            self.assertEqual('hello', localdata.getVar('BPN'))
+
+    @testcase(1573)
+    def test_parse_recipe_initial_datastore(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            testrecipe = 'mdadm'
+            best = tinfoil.find_best_provider(testrecipe)
+            if not best:
+                self.fail('Unable to find recipe providing %s' % testrecipe)
+            dcopy = bb.data.createCopy(tinfoil.config_data)
+            dcopy.setVar('MYVARIABLE', 'somevalue')
+            rd = tinfoil.parse_recipe_file(best[3], config_data=dcopy)
+            # Check we can get variable values
+            self.assertEqual('somevalue', rd.getVar('MYVARIABLE'))
+
+    @testcase(1574)
+    def test_list_recipes(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            # Check pkg_pn
+            checkpns = ['tar', 'automake', 'coreutils', 'm4-native', 'nativesdk-gcc']
+            pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
+            for pn in checkpns:
+                self.assertIn(pn, pkg_pn)
+            # Check pkg_fn
+            checkfns = {'nativesdk-gcc': '^virtual:nativesdk:.*', 'coreutils': '.*/coreutils_.*.bb'}
+            for fn, pn in tinfoil.cooker.recipecaches[''].pkg_fn.items():
+                if pn in checkpns:
+                    if pn in checkfns:
+                        self.assertTrue(re.match(checkfns[pn], fn), 'Entry for %s: %s did not match %s' % (pn, fn, checkfns[pn]))
+                    checkpns.remove(pn)
+            if checkpns:
+                self.fail('Unable to find pkg_fn entries for: %s' % ', '.join(checkpns))
+
+    @testcase(1575)
+    def test_wait_event(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=True)
+            # Need to drain events otherwise events that will be masked will still be in the queue
+            while tinfoil.wait_event(0.25):
+                pass
+            tinfoil.set_event_mask(['bb.event.FilesMatchingFound', 'bb.command.CommandCompleted'])
+            pattern = 'conf'
+            res = tinfoil.run_command('findFilesMatchingInDir', pattern, 'conf/machine')
+            self.assertTrue(res)
+
+            eventreceived = False
+            waitcount = 5
+            while waitcount > 0:
+                event = tinfoil.wait_event(1)
+                if event:
+                    if isinstance(event, bb.command.CommandCompleted):
+                        break
+                    elif isinstance(event, bb.event.FilesMatchingFound):
+                        self.assertEqual(pattern, event._pattern)
+                        self.assertIn('qemuarm.conf', event._matches)
+                        eventreceived = True
+                    else:
+                        self.fail('Unexpected event: %s' % event)
+
+                waitcount = waitcount - 1
+
+            self.assertNotEqual(waitcount, 0, 'Timed out waiting for CommandCompleted event from bitbake server')
+            self.assertTrue(eventreceived, 'Did not receive FilesMatchingFound event from bitbake server')
+
+    @testcase(1576)
+    def test_setvariable_clean(self):
+        # First check that setVariable affects the datastore
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=True)
+            tinfoil.run_command('setVariable', 'TESTVAR', 'specialvalue')
+            self.assertEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is not reflected in client-side getVar()')
+
+        # Now check that the setVariable's effects are no longer present
+        # (this may legitimately break in future if we stop reinitialising
+        # the datastore, in which case we'll have to reconsider use of
+        # setVariable entirely)
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=True)
+            self.assertNotEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is still present!')
+
+        # Now check that setVar on the main datastore works (uses setVariable internally)
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=True)
+            tinfoil.config_data.setVar('TESTVAR', 'specialvalue')
+            value = tinfoil.run_command('getVariable', 'TESTVAR')
+            self.assertEqual(value, 'specialvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
+
+    def test_datastore_operations(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=True)
+            # Test setVarFlag() / getVarFlag()
+            tinfoil.config_data.setVarFlag('TESTVAR', 'flagname', 'flagval')
+            value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
+            self.assertEqual(value, 'flagval', 'Value set using config_data.setVarFlag() is not reflected in config_data.getVarFlag()')
+            # Test delVarFlag()
+            tinfoil.config_data.setVarFlag('TESTVAR', 'otherflag', 'othervalue')
+            tinfoil.config_data.delVarFlag('TESTVAR', 'flagname')
+            value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
+            self.assertEqual(value, None, 'Varflag deleted using config_data.delVarFlag() is not reflected in config_data.getVarFlag()')
+            value = tinfoil.config_data.getVarFlag('TESTVAR', 'otherflag')
+            self.assertEqual(value, 'othervalue', 'Varflag deleted using config_data.delVarFlag() caused unrelated flag to be removed')
+            # Test delVar()
+            tinfoil.config_data.setVar('TESTVAR', 'varvalue')
+            value = tinfoil.config_data.getVar('TESTVAR')
+            self.assertEqual(value, 'varvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
+            tinfoil.config_data.delVar('TESTVAR')
+            value = tinfoil.config_data.getVar('TESTVAR')
+            self.assertEqual(value, None, 'Variable deleted using config_data.delVar() appears to still have a value')
+            # Test renameVar()
+            tinfoil.config_data.setVar('TESTVAROLD', 'origvalue')
+            tinfoil.config_data.renameVar('TESTVAROLD', 'TESTVARNEW')
+            value = tinfoil.config_data.getVar('TESTVAROLD')
+            self.assertEqual(value, None, 'Variable renamed using config_data.renameVar() still seems to exist')
+            value = tinfoil.config_data.getVar('TESTVARNEW')
+            self.assertEqual(value, 'origvalue', 'Variable renamed using config_data.renameVar() does not appear with new name')
+            # Test overrides
+            tinfoil.config_data.setVar('TESTVAR', 'original')
+            tinfoil.config_data.setVar('TESTVAR_overrideone', 'one')
+            tinfoil.config_data.setVar('TESTVAR_overridetwo', 'two')
+            tinfoil.config_data.appendVar('OVERRIDES', ':overrideone')
+            value = tinfoil.config_data.getVar('TESTVAR')
+            self.assertEqual(value, 'one', 'Variable overrides not functioning correctly')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py b/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
index e652fad..726af19 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/selftest/wic.py
@@ -24,42 +24,84 @@
 """Test cases for wic."""
 
 import os
+import sys
+import unittest
 
 from glob import glob
 from shutil import rmtree
+from functools import wraps, lru_cache
+from tempfile import NamedTemporaryFile
 
 from oeqa.selftest.base import oeSelfTest
-from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
 from oeqa.utils.decorators import testcase
 
 
+@lru_cache(maxsize=32)
+def get_host_arch(recipe):
+    """A cached call to get_bb_var('HOST_ARCH', <recipe>)"""
+    return get_bb_var('HOST_ARCH', recipe)
+
+
+def only_for_arch(archs, image='core-image-minimal'):
+    """Decorator for wrapping test cases that can be run only for specific target
+    architectures. A list of compatible architectures is passed in `archs`.
+    Current architecture will be determined by parsing bitbake output for
+    `image` recipe.
+    """
+    def wrapper(func):
+        @wraps(func)
+        def wrapped_f(*args, **kwargs):
+            arch = get_host_arch(image)
+            if archs and arch not in archs:
+                raise unittest.SkipTest("Testcase arch dependency not met: %s" % arch)
+            return func(*args, **kwargs)
+        wrapped_f.__name__ = func.__name__
+        return wrapped_f
+    return wrapper
+
+
 class Wic(oeSelfTest):
     """Wic test class."""
 
-    resultdir = "/var/tmp/wic/build/"
+    resultdir = "/var/tmp/wic.oe-selftest/"
     image_is_ready = False
+    native_sysroot = None
+    wicenv_cache = {}
 
     def setUpLocal(self):
         """This code is executed before each test method."""
-        self.write_config('IMAGE_FSTYPES += " hddimg"\n'
-                          'MACHINE_FEATURES_append = " efi"\n'
-                          'WKS_FILE = "wic-image-minimal"\n')
+        if not self.native_sysroot:
+            Wic.native_sysroot = get_bb_var('STAGING_DIR_NATIVE', 'wic-tools')
 
         # Do this here instead of in setUpClass as the base setUp does some
         # clean up which can result in the native tools built earlier in
         # setUpClass being unavailable.
         if not Wic.image_is_ready:
-            bitbake('syslinux syslinux-native parted-native gptfdisk-native '
-                    'dosfstools-native mtools-native bmap-tools-native')
+            if get_bb_var('USE_NLS') == 'yes':
+                bitbake('wic-tools')
+            else:
+                self.skipTest('wic-tools cannot be built due its (intltool|gettext)-native dependency and NLS disable')
+
             bitbake('core-image-minimal')
             Wic.image_is_ready = True
 
         rmtree(self.resultdir, ignore_errors=True)
 
+    def tearDownLocal(self):
+        """Remove resultdir as it may contain images."""
+        rmtree(self.resultdir, ignore_errors=True)
+
+    @testcase(1552)
+    def test_version(self):
+        """Test wic --version"""
+        self.assertEqual(0, runCmd('wic --version').status)
+
     @testcase(1208)
     def test_help(self):
-        """Test wic --help"""
+        """Test wic --help and wic -h"""
         self.assertEqual(0, runCmd('wic --help').status)
+        self.assertEqual(0, runCmd('wic -h').status)
 
     @testcase(1209)
     def test_createhelp(self):
@@ -71,44 +113,15 @@
         """Test wic list --help"""
         self.assertEqual(0, runCmd('wic list --help').status)
 
-    @testcase(1211)
-    def test_build_image_name(self):
-        """Test wic create directdisk --image-name core-image-minimal"""
-        self.assertEqual(0, runCmd("wic create directdisk "
-                                   "--image-name core-image-minimal").status)
-        self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
+    @testcase(1553)
+    def test_help_create(self):
+        """Test wic help create"""
+        self.assertEqual(0, runCmd('wic help create').status)
 
-    @testcase(1212)
-    def test_build_artifacts(self):
-        """Test wic create directdisk providing all artifacts."""
-        bbvars = dict((var.lower(), get_bb_var(var, 'core-image-minimal')) \
-                        for var in ('STAGING_DATADIR', 'DEPLOY_DIR_IMAGE',
-                                    'STAGING_DIR_NATIVE', 'IMAGE_ROOTFS'))
-        status = runCmd("wic create directdisk "
-                        "-b %(staging_datadir)s "
-                        "-k %(deploy_dir_image)s "
-                        "-n %(staging_dir_native)s "
-                        "-r %(image_rootfs)s" % bbvars).status
-        self.assertEqual(0, status)
-        self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
-
-    @testcase(1157)
-    def test_gpt_image(self):
-        """Test creation of core-image-minimal with gpt table and UUID boot"""
-        self.assertEqual(0, runCmd("wic create directdisk-gpt "
-                                   "--image-name core-image-minimal").status)
-        self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
-
-    @testcase(1213)
-    def test_unsupported_subcommand(self):
-        """Test unsupported subcommand"""
-        self.assertEqual(1, runCmd('wic unsupported',
-                                   ignore_status=True).status)
-
-    @testcase(1214)
-    def test_no_command(self):
-        """Test wic without command"""
-        self.assertEqual(1, runCmd('wic', ignore_status=True).status)
+    @testcase(1554)
+    def test_help_list(self):
+        """Test wic help list"""
+        self.assertEqual(0, runCmd('wic help list').status)
 
     @testcase(1215)
     def test_help_overview(self):
@@ -125,94 +138,418 @@
         """Test wic help kickstart"""
         self.assertEqual(0, runCmd('wic help kickstart').status)
 
+    @testcase(1555)
+    def test_list_images(self):
+        """Test wic list images"""
+        self.assertEqual(0, runCmd('wic list images').status)
+
+    @testcase(1556)
+    def test_list_source_plugins(self):
+        """Test wic list source-plugins"""
+        self.assertEqual(0, runCmd('wic list source-plugins').status)
+
+    @testcase(1557)
+    def test_listed_images_help(self):
+        """Test wic listed images help"""
+        output = runCmd('wic list images').output
+        imagelist = [line.split()[0] for line in output.splitlines()]
+        for image in imagelist:
+            self.assertEqual(0, runCmd('wic list %s help' % image).status)
+
+    @testcase(1213)
+    def test_unsupported_subcommand(self):
+        """Test unsupported subcommand"""
+        self.assertEqual(1, runCmd('wic unsupported',
+                                   ignore_status=True).status)
+
+    @testcase(1214)
+    def test_no_command(self):
+        """Test wic without command"""
+        self.assertEqual(1, runCmd('wic', ignore_status=True).status)
+
+    @testcase(1211)
+    def test_build_image_name(self):
+        """Test wic create wictestdisk --image-name=core-image-minimal"""
+        cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    @testcase(1157)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_gpt_image(self):
+        """Test creation of core-image-minimal with gpt table and UUID boot"""
+        cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
+
+    @testcase(1346)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_iso_image(self):
+        """Test creation of hybrid iso image with legacy and EFI boot"""
+        config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
+                 'MACHINE_FEATURES_append = " efi"\n'
+        self.append_config(config)
+        bitbake('core-image-minimal')
+        self.remove_config(config)
+        cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.direct")))
+        self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.iso")))
+
+    @testcase(1348)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_qemux86_directdisk(self):
+        """Test creation of qemux-86-directdisk image"""
+        cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "qemux86-directdisk-*direct")))
+
+    @testcase(1350)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_mkefidisk(self):
+        """Test creation of mkefidisk image"""
+        cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "mkefidisk-*direct")))
+
+    @testcase(1385)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_bootloader_config(self):
+        """Test creation of directdisk-bootloader-config image"""
+        cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "directdisk-bootloader-config-*direct")))
+
+    @testcase(1560)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_systemd_bootdisk(self):
+        """Test creation of systemd-bootdisk image"""
+        config = 'MACHINE_FEATURES_append = " efi"\n'
+        self.append_config(config)
+        bitbake('core-image-minimal')
+        self.remove_config(config)
+        cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "systemd-bootdisk-*direct")))
+
+    @testcase(1561)
+    def test_sdimage_bootpart(self):
+        """Test creation of sdimage-bootpart image"""
+        cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
+        kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
+        self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
+
+    @testcase(1562)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_default_output_dir(self):
+        """Test default output location"""
+        for fname in glob("directdisk-*.direct"):
+            os.remove(fname)
+        cmd = "wic create directdisk -e core-image-minimal"
+        self.assertEqual(0, runCmd(cmd).status)
+        self.assertEqual(1, len(glob("directdisk-*.direct")))
+
+    @testcase(1212)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_build_artifacts(self):
+        """Test wic create directdisk providing all artifacts."""
+        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
+                              'wic-tools')
+        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
+                                   'core-image-minimal'))
+        bbvars = {key.lower(): value for key, value in bb_vars.items()}
+        bbvars['resultdir'] = self.resultdir
+        status = runCmd("wic create directdisk "
+                        "-b %(staging_datadir)s "
+                        "-k %(deploy_dir_image)s "
+                        "-n %(recipe_sysroot_native)s "
+                        "-r %(image_rootfs)s "
+                        "-o %(resultdir)s" % bbvars).status
+        self.assertEqual(0, status)
+        self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
+
     @testcase(1264)
     def test_compress_gzip(self):
         """Test compressing an image with gzip"""
-        self.assertEqual(0, runCmd("wic create directdisk "
+        self.assertEqual(0, runCmd("wic create wictestdisk "
                                    "--image-name core-image-minimal "
-                                   "-c gzip").status)
-        self.assertEqual(1, len(glob(self.resultdir + \
-                                         "directdisk-*.direct.gz")))
+                                   "-c gzip -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.gz")))
 
     @testcase(1265)
     def test_compress_bzip2(self):
         """Test compressing an image with bzip2"""
-        self.assertEqual(0, runCmd("wic create directdisk "
-                                   "--image-name core-image-minimal "
-                                   "-c bzip2").status)
-        self.assertEqual(1, len(glob(self.resultdir + \
-                                         "directdisk-*.direct.bz2")))
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "-c bzip2 -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.bz2")))
 
     @testcase(1266)
     def test_compress_xz(self):
         """Test compressing an image with xz"""
-        self.assertEqual(0, runCmd("wic create directdisk "
-                                   "--image-name core-image-minimal "
-                                   "-c xz").status)
-        self.assertEqual(1, len(glob(self.resultdir + \
-                                         "directdisk-*.direct.xz")))
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "--compress-with=xz -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.xz")))
 
     @testcase(1267)
     def test_wrong_compressor(self):
         """Test how wic breaks if wrong compressor is provided"""
-        self.assertEqual(2, runCmd("wic create directdisk "
-                                   "--image-name core-image-minimal "
-                                   "-c wrong", ignore_status=True).status)
+        self.assertEqual(2, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "-c wrong -o %s" % self.resultdir,
+                                   ignore_status=True).status)
+
+    @testcase(1558)
+    def test_debug_short(self):
+        """Test -D option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "-D -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    def test_debug_long(self):
+        """Test --debug option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "--debug -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    @testcase(1563)
+    def test_skip_build_check_short(self):
+        """Test -s option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "-s -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    def test_skip_build_check_long(self):
+        """Test --skip-build-check option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "--skip-build-check "
+                                   "--outdir %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    @testcase(1564)
+    def test_build_rootfs_short(self):
+        """Test -f option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "-f -o %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+    def test_build_rootfs_long(self):
+        """Test --build-rootfs option"""
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=core-image-minimal "
+                                   "--build-rootfs "
+                                   "--outdir %s" % self.resultdir).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
 
     @testcase(1268)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
     def test_rootfs_indirect_recipes(self):
         """Test usage of rootfs plugin with rootfs recipes"""
-        wks = "directdisk-multi-rootfs"
-        self.assertEqual(0, runCmd("wic create %s "
-                                   "--image-name core-image-minimal "
-                                   "--rootfs rootfs1=core-image-minimal "
-                                   "--rootfs rootfs2=core-image-minimal" \
-                                   % wks).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s*.direct" % wks)))
+        status = runCmd("wic create directdisk-multi-rootfs "
+                        "--image-name=core-image-minimal "
+                        "--rootfs rootfs1=core-image-minimal "
+                        "--rootfs rootfs2=core-image-minimal "
+                        "--outdir %s" % self.resultdir).status
+        self.assertEqual(0, status)
+        self.assertEqual(1, len(glob(self.resultdir + "directdisk-multi-rootfs*.direct")))
 
     @testcase(1269)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
     def test_rootfs_artifacts(self):
         """Test usage of rootfs plugin with rootfs paths"""
-        bbvars = dict((var.lower(), get_bb_var(var, 'core-image-minimal')) \
-                        for var in ('STAGING_DATADIR', 'DEPLOY_DIR_IMAGE',
-                                    'STAGING_DIR_NATIVE', 'IMAGE_ROOTFS'))
+        bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
+                              'wic-tools')
+        bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
+                                   'core-image-minimal'))
+        bbvars = {key.lower(): value for key, value in bb_vars.items()}
         bbvars['wks'] = "directdisk-multi-rootfs"
+        bbvars['resultdir'] = self.resultdir
         status = runCmd("wic create %(wks)s "
-                        "-b %(staging_datadir)s "
-                        "-k %(deploy_dir_image)s "
-                        "-n %(staging_dir_native)s "
+                        "--bootimg-dir=%(staging_datadir)s "
+                        "--kernel-dir=%(deploy_dir_image)s "
+                        "--native-sysroot=%(recipe_sysroot_native)s "
                         "--rootfs-dir rootfs1=%(image_rootfs)s "
-                        "--rootfs-dir rootfs2=%(image_rootfs)s" \
-                        % bbvars).status
+                        "--rootfs-dir rootfs2=%(image_rootfs)s "
+                        "--outdir %(resultdir)s" % bbvars).status
         self.assertEqual(0, status)
-        self.assertEqual(1, len(glob(self.resultdir + \
-                                     "%(wks)s-*.direct" % bbvars)))
+        self.assertEqual(1, len(glob(self.resultdir + "%(wks)s-*.direct" % bbvars)))
 
-    @testcase(1346)
-    def test_iso_image(self):
-        """Test creation of hybrid iso image with legacy and EFI boot"""
-        self.assertEqual(0, runCmd("wic create mkhybridiso "
-                                   "--image-name core-image-minimal").status)
-        self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.direct")))
-        self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.iso")))
+    def test_exclude_path(self):
+        """Test --exclude-path wks option."""
+
+        oldpath = os.environ['PATH']
+        os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
+
+        try:
+            wks_file = 'temp.wks'
+            with open(wks_file, 'w') as wks:
+                rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
+                wks.write("""
+part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
+part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
+part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
+                          % (rootfs_dir, rootfs_dir))
+            self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+                                       % (wks_file, self.resultdir)).status)
+
+            os.remove(wks_file)
+            wicout = glob(self.resultdir + "%s-*direct" % 'temp')
+            self.assertEqual(1, len(wicout))
+
+            wicimg = wicout[0]
+
+            # verify partition size with wic
+            res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg)
+            self.assertEqual(0, res.status)
+
+            # parse parted output which looks like this:
+            # BYT;\n
+            # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
+            # 1:0.00MiB:200MiB:200MiB:ext4::;\n
+            partlns = res.output.splitlines()[2:]
+
+            self.assertEqual(3, len(partlns))
+
+            for part in [1, 2, 3]:
+                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
+                partln = partlns[part-1].split(":")
+                self.assertEqual(7, len(partln))
+                start = int(partln[1].rstrip("B")) / 512
+                length = int(partln[3].rstrip("B")) / 512
+                self.assertEqual(0, runCmd("dd if=%s of=%s skip=%d count=%d" %
+                                           (wicimg, part_file, start, length)).status)
+
+            def extract_files(debugfs_output):
+                """
+                extract file names from the output of debugfs -R 'ls -p',
+                which looks like this:
+
+                 /2/040755/0/0/.//\n
+                 /2/040755/0/0/..//\n
+                 /11/040700/0/0/lost+found^M//\n
+                 /12/040755/1002/1002/run//\n
+                 /13/040755/1002/1002/sys//\n
+                 /14/040755/1002/1002/bin//\n
+                 /80/040755/1002/1002/var//\n
+                 /92/040755/1002/1002/tmp//\n
+                """
+                # NOTE the occasional ^M in file names
+                return [line.split('/')[5].strip() for line in \
+                        debugfs_output.strip().split('/\n')]
+
+            # Test partition 1, should contain the normal root directories, except
+            # /usr.
+            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+                             os.path.join(self.resultdir, "selftest_img.part1"))
+            self.assertEqual(0, res.status)
+            files = extract_files(res.output)
+            self.assertIn("etc", files)
+            self.assertNotIn("usr", files)
+
+            # Partition 2, should contain common directories for /usr, not root
+            # directories.
+            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+                             os.path.join(self.resultdir, "selftest_img.part2"))
+            self.assertEqual(0, res.status)
+            files = extract_files(res.output)
+            self.assertNotIn("etc", files)
+            self.assertNotIn("usr", files)
+            self.assertIn("share", files)
+
+            # Partition 3, should contain the same as partition 2, including the bin
+            # directory, but not the files inside it.
+            res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+                             os.path.join(self.resultdir, "selftest_img.part3"))
+            self.assertEqual(0, res.status)
+            files = extract_files(res.output)
+            self.assertNotIn("etc", files)
+            self.assertNotIn("usr", files)
+            self.assertIn("share", files)
+            self.assertIn("bin", files)
+            res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \
+                             os.path.join(self.resultdir, "selftest_img.part3"))
+            self.assertEqual(0, res.status)
+            files = extract_files(res.output)
+            self.assertIn(".", files)
+            self.assertIn("..", files)
+            self.assertEqual(2, len(files))
+
+            for part in [1, 2, 3]:
+                part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
+                os.remove(part_file)
+
+        finally:
+            os.environ['PATH'] = oldpath
+
+    def test_exclude_path_errors(self):
+        """Test --exclude-path wks option error handling."""
+        wks_file = 'temp.wks'
+
+        # Absolute argument.
+        with open(wks_file, 'w') as wks:
+            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
+        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+                                      % (wks_file, self.resultdir), ignore_status=True).status)
+        os.remove(wks_file)
+
+        # Argument pointing to parent directory.
+        with open(wks_file, 'w') as wks:
+            wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
+        self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+                                      % (wks_file, self.resultdir), ignore_status=True).status)
+        os.remove(wks_file)
+
+    @testcase(1496)
+    def test_bmap_short(self):
+        """Test generation of .bmap file -m option"""
+        cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
+        status = runCmd(cmd).status
+        self.assertEqual(0, status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
+
+    def test_bmap_long(self):
+        """Test generation of .bmap file --bmap option"""
+        cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
+        status = runCmd(cmd).status
+        self.assertEqual(0, status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
+
+    def _get_image_env_path(self, image):
+        """Generate and obtain the path to <image>.env"""
+        if image not in self.wicenv_cache:
+            self.assertEqual(0, bitbake('%s -c do_rootfs_wicenv' % image).status)
+            bb_vars = get_bb_vars(['STAGING_DIR', 'MACHINE'], image)
+            stdir = bb_vars['STAGING_DIR']
+            machine = bb_vars['MACHINE']
+            self.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
+        return self.wicenv_cache[image]
 
     @testcase(1347)
     def test_image_env(self):
         """Test generation of <image>.env files."""
         image = 'core-image-minimal'
-        self.assertEqual(0, bitbake('%s -c do_rootfs_wicenv' % image).status)
-        stdir = get_bb_var('STAGING_DIR_TARGET', image)
-        imgdatadir = os.path.join(stdir, 'imgdata')
+        imgdatadir = self._get_image_env_path(image)
 
-        basename = get_bb_var('IMAGE_BASENAME', image)
+        bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
+        basename = bb_vars['IMAGE_BASENAME']
         self.assertEqual(basename, image)
         path = os.path.join(imgdatadir, basename) + '.env'
         self.assertTrue(os.path.isfile(path))
 
-        wicvars = set(get_bb_var('WICVARS', image).split())
+        wicvars = set(bb_vars['WICVARS'].split())
         # filter out optional variables
-        wicvars = wicvars.difference(('HDDDIR', 'IMAGE_BOOT_FILES',
-                                      'INITRD', 'ISODIR'))
+        wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
+                                      'INITRD', 'INITRD_LIVE', 'ISODIR'))
         with open(path) as envfile:
             content = dict(line.split("=", 1) for line in envfile)
             # test if variables used by wic present in the .env file
@@ -220,13 +557,41 @@
                 self.assertTrue(var in content, "%s is not in .env file" % var)
                 self.assertTrue(content[var])
 
+    @testcase(1559)
+    def test_image_vars_dir_short(self):
+        """Test image vars directory selection -v option"""
+        image = 'core-image-minimal'
+        imgenvdir = self._get_image_env_path(image)
+
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=%s -v %s -o %s"
+                                   % (image, imgenvdir, self.resultdir)).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+
+    def test_image_vars_dir_long(self):
+        """Test image vars directory selection --vars option"""
+        image = 'core-image-minimal'
+        imgenvdir = self._get_image_env_path(image)
+        self.assertEqual(0, runCmd("wic create wictestdisk "
+                                   "--image-name=%s "
+                                   "--vars %s "
+                                   "--outdir %s"
+                                   % (image, imgenvdir, self.resultdir)).status)
+        self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+
     @testcase(1351)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
     def test_wic_image_type(self):
         """Test building wic images by bitbake"""
+        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
+                 'MACHINE_FEATURES_append = " efi"\n'
+        self.append_config(config)
         self.assertEqual(0, bitbake('wic-image-minimal').status)
+        self.remove_config(config)
 
-        deploy_dir = get_bb_var('DEPLOY_DIR_IMAGE')
-        machine = get_bb_var('MACHINE')
+        bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'])
+        deploy_dir = bb_vars['DEPLOY_DIR_IMAGE']
+        machine = bb_vars['MACHINE']
         prefix = os.path.join(deploy_dir, 'wic-image-minimal-%s.' % machine)
         # check if we have result image and manifests symlinks
         # pointing to existing files
@@ -235,68 +600,193 @@
             self.assertTrue(os.path.islink(path))
             self.assertTrue(os.path.isfile(os.path.realpath(path)))
 
-    @testcase(1348)
-    def test_qemux86_directdisk(self):
-        """Test creation of qemux-86-directdisk image"""
-        image = "qemux86-directdisk"
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
-
-    @testcase(1349)
-    def test_mkgummidisk(self):
-        """Test creation of mkgummidisk image"""
-        image = "mkgummidisk"
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
-
-    @testcase(1350)
-    def test_mkefidisk(self):
-        """Test creation of mkefidisk image"""
-        image = "mkefidisk"
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
-
-    @testcase(1385)
-    def test_directdisk_bootloader_config(self):
-        """Test creation of directdisk-bootloader-config image"""
-        image = "directdisk-bootloader-config"
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
-
     @testcase(1422)
+    @only_for_arch(['i586', 'i686', 'x86_64'])
     def test_qemu(self):
         """Test wic-image-minimal under qemu"""
+        config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
+                 'MACHINE_FEATURES_append = " efi"\n'
+        self.append_config(config)
         self.assertEqual(0, bitbake('wic-image-minimal').status)
+        self.remove_config(config)
 
         with runqemu('wic-image-minimal', ssh=False) as qemu:
-            command = "mount |grep '^/dev/' | cut -f1,3 -d ' '"
-            status, output = qemu.run_serial(command)
-            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (command, output))
-            self.assertEqual(output, '/dev/root /\r\n/dev/vda3 /mnt')
+            cmd = "mount |grep '^/dev/' | cut -f1,3 -d ' '"
+            status, output = qemu.run_serial(cmd)
+            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+            self.assertEqual(output, '/dev/root /\r\n/dev/sda3 /mnt')
 
-    def test_bmap(self):
-        """Test generation of .bmap file"""
-        image = "directdisk"
-        status = runCmd("wic create %s -e core-image-minimal --bmap" % image).status
-        self.assertEqual(0, status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct.bmap" % image)))
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_qemu_efi(self):
+        """Test core-image-minimal efi image under qemu"""
+        config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
+        self.append_config(config)
+        self.assertEqual(0, bitbake('core-image-minimal ovmf').status)
+        self.remove_config(config)
 
-    def test_systemd_bootdisk(self):
-        """Test creation of systemd-bootdisk image"""
-        image = "systemd-bootdisk"
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
+        with runqemu('core-image-minimal', ssh=False,
+                     runqemuparams='ovmf', image_fstype='wic') as qemu:
+            cmd = "grep sda. /proc/partitions  |wc -l"
+            status, output = qemu.run_serial(cmd)
+            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+            self.assertEqual(output, '3')
 
-    def test_sdimage_bootpart(self):
-        """Test creation of sdimage-bootpart image"""
-        image = "sdimage-bootpart"
-        self.write_config('IMAGE_BOOT_FILES = "bzImage"\n')
-        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal" \
-                                   % image).status)
-        self.assertEqual(1, len(glob(self.resultdir + "%s-*direct" % image)))
+    @staticmethod
+    def _make_fixed_size_wks(size):
+        """
+        Create a wks of an image with a single partition. Size of the partition is set
+        using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
+        """
+        with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
+            wkspath = tempf.name
+            tempf.write("part " \
+                     "--source rootfs --ondisk hda --align 4 --fixed-size %d "
+                     "--fstype=ext4\n" % size)
+        wksname = os.path.splitext(os.path.basename(wkspath))[0]
+
+        return wkspath, wksname
+
+    def test_fixed_size(self):
+        """
+        Test creation of a simple image with partition size controlled through
+        --fixed-size flag
+        """
+        wkspath, wksname = Wic._make_fixed_size_wks(200)
+
+        self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+                                   % (wkspath, self.resultdir)).status)
+        os.remove(wkspath)
+        wicout = glob(self.resultdir + "%s-*direct" % wksname)
+        self.assertEqual(1, len(wicout))
+
+        wicimg = wicout[0]
+
+        # verify partition size with wic
+        res = runCmd("parted -m %s unit mib p 2>/dev/null" % wicimg,
+                     ignore_status=True,
+                     native_sysroot=self.native_sysroot)
+        self.assertEqual(0, res.status)
+
+        # parse parted output which looks like this:
+        # BYT;\n
+        # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
+        # 1:0.00MiB:200MiB:200MiB:ext4::;\n
+        partlns = res.output.splitlines()[2:]
+
+        self.assertEqual(1, len(partlns))
+        self.assertEqual("1:0.00MiB:200MiB:200MiB:ext4::;", partlns[0])
+
+    def test_fixed_size_error(self):
+        """
+        Test creation of a simple image with partition size controlled through
+        --fixed-size flag. The size of partition is intentionally set to 1MiB
+        in order to trigger an error in wic.
+        """
+        wkspath, wksname = Wic._make_fixed_size_wks(1)
+
+        self.assertEqual(1, runCmd("wic create %s -e core-image-minimal -o %s" \
+                                   % (wkspath, self.resultdir), ignore_status=True).status)
+        os.remove(wkspath)
+        wicout = glob(self.resultdir + "%s-*direct" % wksname)
+        self.assertEqual(0, len(wicout))
+
+    @only_for_arch(['i586', 'i686', 'x86_64'])
+    def test_rawcopy_plugin_qemu(self):
+        """Test rawcopy plugin in qemu"""
+        # build ext4 and wic images
+        for fstype in ("ext4", "wic"):
+            config = 'IMAGE_FSTYPES = "%s"\nWKS_FILE = "test_rawcopy_plugin.wks.in"\n' % fstype
+            self.append_config(config)
+            self.assertEqual(0, bitbake('core-image-minimal').status)
+            self.remove_config(config)
+
+        with runqemu('core-image-minimal', ssh=False, image_fstype='wic') as qemu:
+            cmd = "grep sda. /proc/partitions  |wc -l"
+            status, output = qemu.run_serial(cmd)
+            self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+            self.assertEqual(output, '2')
+
+    def test_rawcopy_plugin(self):
+        """Test rawcopy plugin"""
+        img = 'core-image-minimal'
+        machine = get_bb_var('MACHINE', img)
+        with NamedTemporaryFile("w", suffix=".wks") as wks:
+            wks.writelines(['part /boot --active --source bootimg-pcbios\n',
+                            'part / --source rawcopy --sourceparams="file=%s-%s.ext4" --use-uuid\n'\
+                             % (img, machine),
+                            'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
+            wks.flush()
+            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
+            self.assertEqual(0, runCmd(cmd).status)
+            wksname = os.path.splitext(os.path.basename(wks.name))[0]
+            out = glob(self.resultdir + "%s-*direct" % wksname)
+            self.assertEqual(1, len(out))
+
+    def test_fs_types(self):
+        """Test filesystem types for empty and not empty partitions"""
+        img = 'core-image-minimal'
+        with NamedTemporaryFile("w", suffix=".wks") as wks:
+            wks.writelines(['part ext2   --fstype ext2     --source rootfs\n',
+                            'part btrfs  --fstype btrfs    --source rootfs --size 40M\n',
+                            'part squash --fstype squashfs --source rootfs\n',
+                            'part swap   --fstype swap --size 1M\n',
+                            'part emptyvfat   --fstype vfat   --size 1M\n',
+                            'part emptymsdos  --fstype msdos  --size 1M\n',
+                            'part emptyext2   --fstype ext2   --size 1M\n',
+                            'part emptybtrfs  --fstype btrfs  --size 100M\n'])
+            wks.flush()
+            cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
+            self.assertEqual(0, runCmd(cmd).status)
+            wksname = os.path.splitext(os.path.basename(wks.name))[0]
+            out = glob(self.resultdir + "%s-*direct" % wksname)
+            self.assertEqual(1, len(out))
+
+    def test_kickstart_parser(self):
+        """Test wks parser options"""
+        with NamedTemporaryFile("w", suffix=".wks") as wks:
+            wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
+                            '--overhead-factor 1.2 --size 100k\n'])
+            wks.flush()
+            cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
+            self.assertEqual(0, runCmd(cmd).status)
+            wksname = os.path.splitext(os.path.basename(wks.name))[0]
+            out = glob(self.resultdir + "%s-*direct" % wksname)
+            self.assertEqual(1, len(out))
+
+    def test_image_bootpart_globbed(self):
+        """Test globbed sources with image-bootpart plugin"""
+        img = "core-image-minimal"
+        cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
+        config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
+        self.append_config(config)
+        self.assertEqual(0, runCmd(cmd).status)
+        self.remove_config(config)
+        self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
+
+    def test_sparse_copy(self):
+        """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
+        libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'wic')
+        sys.path.insert(0, libpath)
+        from  filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
+        with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
+            src_name = sparse.name
+            src_size = 1024 * 10
+            sparse.truncate(src_size)
+            # write one byte to the file
+            with open(src_name, 'r+b') as sfile:
+                sfile.seek(1024 * 4)
+                sfile.write(b'\x00')
+            dest = sparse.name + '.out'
+            # copy src file to dest using different filemap APIs
+            for api in (FilemapFiemap, FilemapSeek, None):
+                if os.path.exists(dest):
+                    os.unlink(dest)
+                try:
+                    sparse_copy(sparse.name, dest, api=api)
+                except ErrorNotSupp:
+                    continue # skip unsupported API
+                dest_stat = os.stat(dest)
+                self.assertEqual(dest_stat.st_size, src_size)
+                # 8 blocks is 4K (physical sector size)
+                self.assertEqual(dest_stat.st_blocks, 8)
+            os.unlink(dest)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py b/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
index 24669f4..3255e3a 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/targetcontrol.py
@@ -18,8 +18,10 @@
 from oeqa.controllers.testtargetloader import TestTargetLoader
 from abc import ABCMeta, abstractmethod
 
+logger = logging.getLogger('BitBake.QemuRunner')
+
 def get_target_controller(d):
-    testtarget = d.getVar("TEST_TARGET", True)
+    testtarget = d.getVar("TEST_TARGET")
     # old, simple names
     if testtarget == "qemu":
         return QemuTarget(d)
@@ -33,7 +35,7 @@
         except AttributeError:
             # nope, perhaps a layer defined one
             try:
-                bbpath = d.getVar("BBPATH", True).split(':')
+                bbpath = d.getVar("BBPATH").split(':')
                 testtargetloader = TestTargetLoader()
                 controller = testtargetloader.get_controller_module(testtarget, bbpath)
             except ImportError as e:
@@ -51,9 +53,9 @@
         self.connection = None
         self.ip = None
         self.server_ip = None
-        self.datetime = d.getVar('DATETIME', True)
-        self.testdir = d.getVar("TEST_LOG_DIR", True)
-        self.pn = d.getVar("PN", True)
+        self.datetime = d.getVar('DATETIME')
+        self.testdir = d.getVar("TEST_LOG_DIR")
+        self.pn = d.getVar("PN")
 
     @abstractmethod
     def deploy(self):
@@ -63,7 +65,7 @@
         if os.path.islink(sshloglink):
             os.unlink(sshloglink)
         os.symlink(self.sshlog, sshloglink)
-        bb.note("SSH log file: %s" %  self.sshlog)
+        logger.info("SSH log file: %s" %  self.sshlog)
 
     @abstractmethod
     def start(self, params=None, ssh=True, extra_bootparams=None):
@@ -80,7 +82,7 @@
     @classmethod
     def match_image_fstype(self, d, image_fstypes=None):
         if not image_fstypes:
-            image_fstypes = d.getVar('IMAGE_FSTYPES', True).split(' ')
+            image_fstypes = d.getVar('IMAGE_FSTYPES').split(' ')
         possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes]
         if possible_image_fstypes:
             return possible_image_fstypes[0]
@@ -113,20 +115,26 @@
 
     supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic']
 
-    def __init__(self, d):
+    def __init__(self, d, image_fstype=None):
 
         super(QemuTarget, self).__init__(d)
 
-        self.image_fstype = self.get_image_fstype(d)
+        self.rootfs = ''
+        self.kernel = ''
+        self.image_fstype = ''
+
+        if d.getVar('FIND_ROOTFS') == '1':
+            self.image_fstype = image_fstype or self.get_image_fstype(d)
+            self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"),  d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype)
+            self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
         self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
-        self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True),  d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
-        self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
-        dump_target_cmds = d.getVar("testimage_dump_target", True)
-        dump_host_cmds = d.getVar("testimage_dump_host", True)
-        dump_dir = d.getVar("TESTIMAGE_DUMP_DIR", True)
-        if d.getVar("QEMU_USE_KVM", False) is not None \
-           and d.getVar("QEMU_USE_KVM", False) == "True" \
-           and "x86" in d.getVar("MACHINE", True):
+        dump_target_cmds = d.getVar("testimage_dump_target")
+        dump_host_cmds = d.getVar("testimage_dump_host")
+        dump_dir = d.getVar("TESTIMAGE_DUMP_DIR")
+        qemu_use_kvm = d.getVar("QEMU_USE_KVM")
+        if qemu_use_kvm and \
+           (qemu_use_kvm == "True" and "x86" in d.getVar("MACHINE") or \
+            d.getVar("MACHINE") in qemu_use_kvm.split()):
             use_kvm = True
         else:
             use_kvm = False
@@ -135,32 +143,31 @@
         import oe.path
         bb.utils.mkdirhier(self.testdir)
         self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime)
-        logger = logging.getLogger('BitBake.QemuRunner')
         loggerhandler = logging.FileHandler(self.qemurunnerlog)
         loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
         logger.addHandler(loggerhandler)
         oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True)
 
-        if d.getVar("DISTRO", True) == "poky-tiny":
-            self.runner = QemuTinyRunner(machine=d.getVar("MACHINE", True),
+        if d.getVar("DISTRO") == "poky-tiny":
+            self.runner = QemuTinyRunner(machine=d.getVar("MACHINE"),
                             rootfs=self.rootfs,
-                            tmpdir = d.getVar("TMPDIR", True),
-                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
-                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
+                            tmpdir = d.getVar("TMPDIR"),
+                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
+                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
                             logfile = self.qemulog,
                             kernel = self.kernel,
-                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)))
+                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")))
         else:
-            self.runner = QemuRunner(machine=d.getVar("MACHINE", True),
+            self.runner = QemuRunner(machine=d.getVar("MACHINE"),
                             rootfs=self.rootfs,
-                            tmpdir = d.getVar("TMPDIR", True),
-                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
-                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
+                            tmpdir = d.getVar("TMPDIR"),
+                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
+                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
                             logfile = self.qemulog,
-                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)),
+                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")),
                             use_kvm = use_kvm,
                             dump_dir = dump_dir,
-                            dump_host_cmds = d.getVar("testimage_dump_host", True))
+                            dump_host_cmds = d.getVar("testimage_dump_host"))
 
         self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
 
@@ -172,12 +179,17 @@
             os.unlink(qemuloglink)
         os.symlink(self.qemulog, qemuloglink)
 
-        bb.note("rootfs file: %s" %  self.rootfs)
-        bb.note("Qemu log file: %s" % self.qemulog)
+        logger.info("rootfs file: %s" %  self.rootfs)
+        logger.info("Qemu log file: %s" % self.qemulog)
         super(QemuTarget, self).deploy()
 
-    def start(self, params=None, ssh=True, extra_bootparams=None):
-        if self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams):
+    def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd='', discard_writes=True):
+        if launch_cmd:
+            start = self.runner.launch(get_ip=ssh, launch_cmd=launch_cmd)
+        else:
+            start = self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams, discard_writes=discard_writes)
+
+        if start:
             if ssh:
                 self.ip = self.runner.ip
                 self.server_ip = self.runner.server_ip
@@ -206,28 +218,28 @@
         else:
             raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
 
-    def run_serial(self, command):
-        return self.runner.run_serial(command)
+    def run_serial(self, command, timeout=5):
+        return self.runner.run_serial(command, timeout=timeout)
 
 
 class SimpleRemoteTarget(BaseTarget):
 
     def __init__(self, d):
         super(SimpleRemoteTarget, self).__init__(d)
-        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.')
+        addr = d.getVar("TEST_TARGET_IP") or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
         self.ip = addr.split(":")[0]
         try:
             self.port = addr.split(":")[1]
         except IndexError:
             self.port = None
-        bb.note("Target IP: %s" % self.ip)
-        self.server_ip = d.getVar("TEST_SERVER_IP", True)
+        logger.info("Target IP: %s" % self.ip)
+        self.server_ip = d.getVar("TEST_SERVER_IP")
         if not self.server_ip:
             try:
                 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
             except Exception as e:
                 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)
-        bb.note("Server IP: %s" % self.server_ip)
+        logger.info("Server IP: %s" % self.server_ip)
 
     def deploy(self):
         super(SimpleRemoteTarget, self).deploy()
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/__init__.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/__init__.py
index 8f706f3..485de03 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/__init__.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/__init__.py
@@ -36,3 +36,33 @@
 
     new_path = new_path[:-1]
     return new_path
+
+def make_logger_bitbake_compatible(logger):
+    import logging
+
+    """ 
+        Bitbake logger redifines debug() in order to
+        set a level within debug, this breaks compatibility
+        with vainilla logging, so we neeed to redifine debug()
+        method again also add info() method with INFO + 1 level.
+    """
+    def _bitbake_log_debug(*args, **kwargs):
+        lvl = logging.DEBUG
+
+        if isinstance(args[0], int):
+            lvl = args[0]
+            msg = args[1]
+            args = args[2:]
+        else:
+            msg = args[0]
+            args = args[1:]
+
+        logger.log(lvl, msg, *args, **kwargs)
+    
+    def _bitbake_log_info(msg, *args, **kwargs):
+        logger.log(logging.INFO + 1, msg, *args, **kwargs)
+
+    logger.debug = _bitbake_log_debug
+    logger.info = _bitbake_log_info
+
+    return logger
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/buildproject.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/buildproject.py
new file mode 100644
index 0000000..487f08b
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/buildproject.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2013-2016 Intel Corporation
+#
+# Released under the MIT license (see COPYING.MIT)
+
+# Provides a class for automating build tests for projects
+
+import os
+import re
+import subprocess
+import shutil
+import tempfile
+
+from abc import ABCMeta, abstractmethod
+
+class BuildProject(metaclass=ABCMeta):
+    def __init__(self, uri, foldername=None, tmpdir=None, dl_dir=None):
+        self.uri = uri
+        self.archive = os.path.basename(uri)
+        if not tmpdir:
+            tmpdir = tempfile.mkdtemp(prefix='buildproject')
+        self.localarchive = os.path.join(tmpdir, self.archive)
+        self.dl_dir = dl_dir
+        if foldername:
+            self.fname = foldername
+        else:
+            self.fname = re.sub(r'\.tar\.bz2$|\.tar\.gz$|\.tar\.xz$', '', self.archive)
+
+    # Download self.archive to self.localarchive
+    def _download_archive(self):
+        if self.dl_dir and os.path.exists(os.path.join(self.dl_dir, self.archive)):
+            shutil.copyfile(os.path.join(self.dl_dir, self.archive), self.localarchive)
+            return
+
+        cmd = "wget -O %s %s" % (self.localarchive, self.uri)
+        subprocess.check_output(cmd, shell=True)
+
+    # This method should provide a way to run a command in the desired environment.
+    @abstractmethod
+    def _run(self, cmd):
+        pass
+
+    # The timeout parameter of target.run is set to 0 to make the ssh command
+    # run with no timeout.
+    def run_configure(self, configure_args='', extra_cmds=''):
+        return self._run('cd %s; gnu-configize; %s ./configure %s' % (self.targetdir, extra_cmds, configure_args))
+
+    def run_make(self, make_args=''):
+        return self._run('cd %s; make %s' % (self.targetdir, make_args))
+
+    def run_install(self, install_args=''):
+        return self._run('cd %s; make install %s' % (self.targetdir, install_args))
+
+    def clean(self):
+        self._run('rm -rf %s' % self.targetdir)
+        subprocess.call('rm -f %s' % self.localarchive, shell=True)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
index 5cd0f74..57286fc 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/commands.py
@@ -97,9 +97,17 @@
     pass
 
 
-def runCmd(command, ignore_status=False, timeout=None, assert_error=True, **options):
+def runCmd(command, ignore_status=False, timeout=None, assert_error=True,
+          native_sysroot=None, limit_exc_output=0, **options):
     result = Result()
 
+    if native_sysroot:
+        extra_paths = "%s/sbin:%s/usr/sbin:%s/usr/bin" % \
+                      (native_sysroot, native_sysroot, native_sysroot)
+        nenv = dict(options.get('env', os.environ))
+        nenv['PATH'] = extra_paths + ':' + nenv.get('PATH', '')
+        options['env'] = nenv
+
     cmd = Command(command, timeout=timeout, **options)
     cmd.run()
 
@@ -110,10 +118,16 @@
     result.pid = cmd.process.pid
 
     if result.status and not ignore_status:
+        exc_output = result.output
+        if limit_exc_output > 0:
+            split = result.output.splitlines()
+            if len(split) > limit_exc_output:
+                exc_output = "\n... (last %d lines of output)\n" % limit_exc_output + \
+                             '\n'.join(split[-limit_exc_output:])
         if assert_error:
-            raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, result.output))
+            raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, exc_output))
         else:
-            raise CommandError(result.status, command, result.output)
+            raise CommandError(result.status, command, exc_output)
 
     return result
 
@@ -149,7 +163,9 @@
     """Get values of multiple bitbake variables"""
     bbenv = get_bb_env(target, postconfig=postconfig)
 
-    var_re = re.compile(r'^(export )?(?P<var>\w+)="(?P<value>.*)"$')
+    if variables is not None:
+        variables = variables.copy()
+    var_re = re.compile(r'^(export )?(?P<var>\w+(_.*)?)="(?P<value>.*)"$')
     unset_re = re.compile(r'^unset (?P<var>\w+)$')
     lastline = None
     values = {}
@@ -209,21 +225,30 @@
 
 
 @contextlib.contextmanager
-def runqemu(pn, ssh=True):
+def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None, qemuparams=None, overrides={}, discard_writes=True):
+    """
+    launch_cmd means directly run the command, don't need set rootfs or env vars.
+    """
 
     import bb.tinfoil
     import bb.build
 
     tinfoil = bb.tinfoil.Tinfoil()
-    tinfoil.prepare(False)
+    tinfoil.prepare(config_only=False, quiet=True)
     try:
         tinfoil.logger.setLevel(logging.WARNING)
         import oeqa.targetcontrol
         tinfoil.config_data.setVar("TEST_LOG_DIR", "${WORKDIR}/testimage")
         tinfoil.config_data.setVar("TEST_QEMUBOOT_TIMEOUT", "1000")
-        import oe.recipeutils
-        recipefile = oe.recipeutils.pn_to_recipe(tinfoil.cooker, pn)
-        recipedata = oe.recipeutils.parse_recipe(tinfoil.cooker, recipefile, [])
+        # Tell QemuTarget() whether need find rootfs/kernel or not
+        if launch_cmd:
+            tinfoil.config_data.setVar("FIND_ROOTFS", '0')
+        else:
+            tinfoil.config_data.setVar("FIND_ROOTFS", '1')
+
+        recipedata = tinfoil.parse_recipe(pn)
+        for key, value in overrides.items():
+            recipedata.setVar(key, value)
 
         # The QemuRunner log is saved out, but we need to ensure it is at the right
         # log level (and then ensure that since it's a child of the BitBake logger,
@@ -231,9 +256,9 @@
         logger = logging.getLogger('BitBake.QemuRunner')
         logger.setLevel(logging.DEBUG)
         logger.propagate = False
-        logdir = recipedata.getVar("TEST_LOG_DIR", True)
+        logdir = recipedata.getVar("TEST_LOG_DIR")
 
-        qemu = oeqa.targetcontrol.QemuTarget(recipedata)
+        qemu = oeqa.targetcontrol.QemuTarget(recipedata, image_fstype)
     finally:
         # We need to shut down tinfoil early here in case we actually want
         # to run tinfoil-using utilities with the running QEMU instance.
@@ -253,7 +278,7 @@
     try:
         qemu.deploy()
         try:
-            qemu.start(ssh=ssh)
+            qemu.start(params=qemuparams, ssh=ssh, runqemuparams=runqemuparams, launch_cmd=launch_cmd, discard_writes=discard_writes)
         except bb.build.FuncFailed:
             raise Exception('Failed to start QEMU - see the logs in %s' % logdir)
 
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
index 25f9c54..d876896 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/decorators.py
@@ -172,18 +172,19 @@
 
         #check status of tests and record it
 
+        tcid = self.id()
         for (name, msg) in result.errors:
-            if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
+            if tcid == name.id():
                 local_log.results("Testcase "+str(test_case)+": ERROR")
                 local_log.results("Testcase "+str(test_case)+":\n"+msg)
                 passed = False
         for (name, msg) in result.failures:
-            if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
+            if tcid == name.id():
                 local_log.results("Testcase "+str(test_case)+": FAILED")
                 local_log.results("Testcase "+str(test_case)+":\n"+msg)
                 passed = False
         for (name, msg) in result.skipped:
-            if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
+            if tcid == name.id():
                 local_log.results("Testcase "+str(test_case)+": SKIPPED")
                 passed = False
         if passed:
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
index 71422a9..5a7edc1 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/dump.py
@@ -5,12 +5,6 @@
 import itertools
 from .commands import runCmd
 
-def get_host_dumper(d):
-    cmds = d.getVar("testimage_dump_host", True)
-    parent_dir = d.getVar("TESTIMAGE_DUMP_DIR", True)
-    return HostDumper(cmds, parent_dir)
-
-
 class BaseDumper(object):
     """ Base class to dump commands from host/target """
 
@@ -77,13 +71,12 @@
             result = runCmd(cmd, ignore_status=True)
             self._write_dump(cmd.split()[0], result.output)
 
-
 class TargetDumper(BaseDumper):
     """ Class to get dumps from target, it only works with QemuRunner """
 
-    def __init__(self, cmds, parent_dir, qemurunner):
+    def __init__(self, cmds, parent_dir, runner):
         super(TargetDumper, self).__init__(cmds, parent_dir)
-        self.runner = qemurunner
+        self.runner = runner
 
     def dump_target(self, dump_dir=""):
         if dump_dir:
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py
index ae85d27..e0cb3f0 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/git.py
@@ -16,8 +16,17 @@
 class GitRepo(object):
     """Class representing a Git repository clone"""
     def __init__(self, path, is_topdir=False):
-        self.top_dir = self._run_git_cmd_at(['rev-parse', '--show-toplevel'],
-                                            path)
+        git_dir = self._run_git_cmd_at(['rev-parse', '--git-dir'], path)
+        git_dir = git_dir if os.path.isabs(git_dir) else os.path.join(path, git_dir)
+        self.git_dir = os.path.realpath(git_dir)
+
+        if self._run_git_cmd_at(['rev-parse', '--is-bare-repository'], path) == 'true':
+            self.bare = True
+            self.top_dir = self.git_dir
+        else:
+            self.bare = False
+            self.top_dir = self._run_git_cmd_at(['rev-parse', '--show-toplevel'],
+                                                path)
         realpath = os.path.realpath(path)
         if is_topdir and realpath != self.top_dir:
             raise GitError("{} is not a Git top directory".format(realpath))
@@ -36,9 +45,12 @@
         return ret.output.strip()
 
     @staticmethod
-    def init(path):
+    def init(path, bare=False):
         """Initialize a new Git repository"""
-        GitRepo._run_git_cmd_at('init', cwd=path)
+        cmd = ['init']
+        if bare:
+            cmd.append('--bare')
+        GitRepo._run_git_cmd_at(cmd, cwd=path)
         return GitRepo(path, is_topdir=True)
 
     def run_cmd(self, git_args, env_update=None):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/metadata.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/metadata.py
new file mode 100644
index 0000000..cb81155
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/metadata.py
@@ -0,0 +1,118 @@
+# Copyright (C) 2016 Intel Corporation
+#
+# Released under the MIT license (see COPYING.MIT)
+#
+# Functions to get metadata from the testing host used
+# for analytics of test results.
+
+from collections import OrderedDict
+from collections.abc import MutableMapping
+from xml.dom.minidom import parseString
+from xml.etree.ElementTree import Element, tostring
+
+from oeqa.utils.commands import runCmd, get_bb_vars
+
+def get_os_release():
+    """Get info from /etc/os-release as a dict"""
+    data = OrderedDict()
+    os_release_file = '/etc/os-release'
+    if not os.path.exists(os_release_file):
+        return None
+    with open(os_release_file) as fobj:
+        for line in fobj:
+            key, value = line.split('=', 1)
+            data[key.strip().lower()] = value.strip().strip('"')
+    return data
+
+def metadata_from_bb():
+    """ Returns test's metadata as OrderedDict.
+
+        Data will be gathered using bitbake -e thanks to get_bb_vars.
+    """
+    metadata_config_vars = ('MACHINE', 'BB_NUMBER_THREADS', 'PARALLEL_MAKE')
+
+    info_dict = OrderedDict()
+    hostname = runCmd('hostname')
+    info_dict['hostname'] = hostname.output
+    data_dict = get_bb_vars()
+
+    # Distro information
+    info_dict['distro'] = {'id': data_dict['DISTRO'],
+                           'version_id': data_dict['DISTRO_VERSION'],
+                           'pretty_name': '%s %s' % (data_dict['DISTRO'], data_dict['DISTRO_VERSION'])}
+
+    # Host distro information
+    os_release = get_os_release()
+    if os_release:
+        info_dict['host_distro'] = OrderedDict()
+        for key in ('id', 'version_id', 'pretty_name'):
+            if key in os_release:
+                info_dict['host_distro'][key] = os_release[key]
+
+    info_dict['layers'] = get_layers(data_dict['BBLAYERS'])
+    info_dict['bitbake'] = git_rev_info(os.path.dirname(bb.__file__))
+
+    info_dict['config'] = OrderedDict()
+    for var in sorted(metadata_config_vars):
+        info_dict['config'][var] = data_dict[var]
+    return info_dict
+
+def metadata_from_data_store(d):
+    """ Returns test's metadata as OrderedDict.
+
+        Data will be collected from the provided data store.
+    """
+    # TODO: Getting metadata from the data store would
+    # be useful when running within bitbake.
+    pass
+
+def git_rev_info(path):
+    """Get git revision information as a dict"""
+    from git import Repo, InvalidGitRepositoryError, NoSuchPathError
+
+    info = OrderedDict()
+    try:
+        repo = Repo(path, search_parent_directories=True)
+    except (InvalidGitRepositoryError, NoSuchPathError):
+        return info
+    info['commit'] = repo.head.commit.hexsha
+    info['commit_count'] = repo.head.commit.count()
+    try:
+        info['branch'] = repo.active_branch.name
+    except TypeError:
+        info['branch'] = '(nobranch)'
+    return info
+
+def get_layers(layers):
+    """Returns layer information in dict format"""
+    layer_dict = OrderedDict()
+    for layer in layers.split():
+        layer_name = os.path.basename(layer)
+        layer_dict[layer_name] = git_rev_info(layer)
+    return layer_dict
+
+def write_metadata_file(file_path, metadata):
+    """ Writes metadata to a XML file in directory. """
+
+    xml = dict_to_XML('metadata', metadata)
+    xml_doc = parseString(tostring(xml).decode('UTF-8'))
+    with open(file_path, 'w') as f:
+        f.write(xml_doc.toprettyxml())
+
+def dict_to_XML(tag, dictionary, **kwargs):
+    """ Return XML element converting dicts recursively. """
+
+    elem = Element(tag, **kwargs)
+    for key, val in dictionary.items():
+        if tag == 'layers':
+            child = (dict_to_XML('layer', val, name=key))
+        elif isinstance(val, MutableMapping):
+            child = (dict_to_XML(key, val))
+        else:
+            if tag == 'config':
+                child = Element('variable', name=key)
+            else:
+                child = Element(key)
+            child.text = str(val)
+        elem.append(child)
+    return elem
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py
index 099ecc9..724afb2 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/package_manager.py
@@ -1,29 +1,210 @@
+import os
+import json
+import shutil
+
+from oeqa.core.utils.test import getCaseFile, getCaseMethod
+
 def get_package_manager(d, root_path):
     """
     Returns an OE package manager that can install packages in root_path.
     """
     from oe.package_manager import RpmPM, OpkgPM, DpkgPM
 
-    pkg_class = d.getVar("IMAGE_PKGTYPE", True)
+    pkg_class = d.getVar("IMAGE_PKGTYPE")
     if pkg_class == "rpm":
         pm = RpmPM(d,
                    root_path,
-                   d.getVar('TARGET_VENDOR', True))
+                   d.getVar('TARGET_VENDOR'))
         pm.create_configs()
 
     elif pkg_class == "ipk":
         pm = OpkgPM(d,
                     root_path,
-                    d.getVar("IPKGCONF_TARGET", True),
-                    d.getVar("ALL_MULTILIB_PACKAGE_ARCHS", True))
+                    d.getVar("IPKGCONF_TARGET"),
+                    d.getVar("ALL_MULTILIB_PACKAGE_ARCHS"))
 
     elif pkg_class == "deb":
         pm = DpkgPM(d,
                     root_path,
-                    d.getVar('PACKAGE_ARCHS', True),
-                    d.getVar('DPKG_ARCH', True))
+                    d.getVar('PACKAGE_ARCHS'),
+                    d.getVar('DPKG_ARCH'))
 
     pm.write_index()
     pm.update()
 
     return pm
+
+def find_packages_to_extract(test_suite):
+    """
+    Returns packages to extract required by runtime tests.
+    """
+    from oeqa.core.utils.test import getSuiteCasesFiles
+
+    needed_packages = {}
+    files = getSuiteCasesFiles(test_suite)
+
+    for f in set(files):
+        json_file = _get_json_file(f)
+        if json_file:
+            needed_packages.update(_get_needed_packages(json_file))
+
+    return needed_packages
+
+def _get_json_file(module_path):
+    """
+    Returns the path of the JSON file for a module, empty if doesn't exitst.
+    """
+
+    json_file = '%s.json' % module_path.rsplit('.', 1)[0]
+    if os.path.isfile(module_path) and os.path.isfile(json_file):
+        return json_file
+    else:
+        return ''
+
+def _get_needed_packages(json_file, test=None):
+    """
+    Returns a dict with needed packages based on a JSON file.
+
+    If a test is specified it will return the dict just for that test.
+    """
+    needed_packages = {}
+
+    with open(json_file) as f:
+        test_packages = json.load(f)
+    for key,value in test_packages.items():
+        needed_packages[key] = value
+
+    if test:
+        if test in needed_packages:
+            needed_packages = needed_packages[test]
+        else:
+            needed_packages = {}
+
+    return needed_packages
+
+def extract_packages(d, needed_packages):
+    """
+    Extract packages that will be needed during runtime.
+    """
+
+    import bb
+    import oe.path
+
+    extracted_path = d.getVar('TEST_EXTRACTED_DIR')
+
+    for key,value in needed_packages.items():
+        packages = ()
+        if isinstance(value, dict):
+            packages = (value, )
+        elif isinstance(value, list):
+            packages = value
+        else:
+            bb.fatal('Failed to process needed packages for %s; '
+                     'Value must be a dict or list' % key)
+
+        for package in packages:
+            pkg = package['pkg']
+            rm = package.get('rm', False)
+            extract = package.get('extract', True)
+
+            if extract:
+                #logger.debug(1, 'Extracting %s' % pkg)
+                dst_dir = os.path.join(extracted_path, pkg)
+                # Same package used for more than one test,
+                # don't need to extract again.
+                if os.path.exists(dst_dir):
+                    continue
+
+                # Extract package and copy it to TEST_EXTRACTED_DIR
+                pkg_dir = _extract_in_tmpdir(d, pkg)
+                oe.path.copytree(pkg_dir, dst_dir)
+                shutil.rmtree(pkg_dir)
+
+            else:
+                #logger.debug(1, 'Copying %s' % pkg)
+                _copy_package(d, pkg)
+
+def _extract_in_tmpdir(d, pkg):
+    """"
+    Returns path to a temp directory where the package was
+    extracted without dependencies.
+    """
+
+    from oeqa.utils.package_manager import get_package_manager
+
+    pkg_path = os.path.join(d.getVar('TEST_INSTALL_TMP_DIR'), pkg)
+    pm = get_package_manager(d, pkg_path)
+    extract_dir = pm.extract(pkg)
+    shutil.rmtree(pkg_path)
+
+    return extract_dir
+
+def _copy_package(d, pkg):
+    """
+    Copy the RPM, DEB or IPK package to dst_dir
+    """
+
+    from oeqa.utils.package_manager import get_package_manager
+
+    pkg_path = os.path.join(d.getVar('TEST_INSTALL_TMP_DIR'), pkg)
+    dst_dir = d.getVar('TEST_PACKAGED_DIR')
+    pm = get_package_manager(d, pkg_path)
+    pkg_info = pm.package_info(pkg)
+    file_path = pkg_info[pkg]['filepath']
+    shutil.copy2(file_path, dst_dir)
+    shutil.rmtree(pkg_path)
+
+def install_package(test_case):
+    """
+    Installs package in DUT if required.
+    """
+    needed_packages = test_needs_package(test_case)
+    if needed_packages:
+        _install_uninstall_packages(needed_packages, test_case, True)
+
+def uninstall_package(test_case):
+    """
+    Uninstalls package in DUT if required.
+    """
+    needed_packages = test_needs_package(test_case)
+    if needed_packages:
+        _install_uninstall_packages(needed_packages, test_case, False)
+
+def test_needs_package(test_case):
+    """
+    Checks if a test case requires to install/uninstall packages.
+    """
+    test_file = getCaseFile(test_case)
+    json_file = _get_json_file(test_file)
+
+    if json_file:
+        test_method = getCaseMethod(test_case)
+        needed_packages = _get_needed_packages(json_file, test_method)
+        if needed_packages:
+            return needed_packages
+
+    return None
+
+def _install_uninstall_packages(needed_packages, test_case, install=True):
+    """
+    Install/Uninstall packages in the DUT without using a package manager
+    """
+
+    if isinstance(needed_packages, dict):
+        packages = [needed_packages]
+    elif isinstance(needed_packages, list):
+        packages = needed_packages
+
+    for package in packages:
+        pkg = package['pkg']
+        rm = package.get('rm', False)
+        extract = package.get('extract', True)
+        src_dir = os.path.join(test_case.tc.extract_dir, pkg)
+
+        # Install package
+        if install and extract:
+            test_case.tc.target.copyDirTo(src_dir, '/')
+
+        # Uninstall package
+        elif not install and rm:
+            test_case.tc.target.deleteDirStructure(src_dir, '/')
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
index 8f1b5b9..ba44b96 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemurunner.py
@@ -7,6 +7,7 @@
 
 import subprocess
 import os
+import sys
 import time
 import signal
 import re
@@ -36,10 +37,12 @@
         self.runqemu = None
         # pid of the qemu process that runqemu will start
         self.qemupid = None
-        # target ip - from the command line
+        # target ip - from the command line or runqemu output
         self.ip = None
         # host ip - where qemu is running
         self.server_ip = None
+        # target ip netmask
+        self.netmask = None
 
         self.machine = machine
         self.rootfs = rootfs
@@ -73,7 +76,7 @@
         if self.logfile:
             # It is needed to sanitize the data received from qemu
             # because is possible to have control characters
-            msg = msg.decode("utf-8")
+            msg = msg.decode("utf-8", errors='ignore')
             msg = re_control_char.sub('', msg)
             with codecs.open(self.logfile, "a", encoding="utf-8") as f:
                 f.write("%s" % msg)
@@ -94,7 +97,7 @@
                 self._dump_host()
                 raise SystemExit
 
-    def start(self, qemuparams = None, get_ip = True, extra_bootparams = None):
+    def start(self, qemuparams = None, get_ip = True, extra_bootparams = None, runqemuparams='', launch_cmd=None, discard_writes=True):
         if self.display:
             os.environ["DISPLAY"] = self.display
             # Set this flag so that Qemu doesn't do any grabs as SDL grabs
@@ -114,6 +117,20 @@
         else:
             os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
 
+        if not launch_cmd:
+            launch_cmd = 'runqemu %s %s ' % ('snapshot' if discard_writes else '', runqemuparams)
+            if self.use_kvm:
+                logger.info('Using kvm for runqemu')
+                launch_cmd += ' kvm'
+            else:
+                logger.info('Not using kvm for runqemu')
+            if not self.display:
+                launch_cmd += ' nographic'
+            launch_cmd += ' %s %s' % (self.machine, self.rootfs)
+
+        return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams)
+
+    def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None):
         try:
             threadsock, threadport = self.create_socket()
             self.server_socket, self.serverport = self.create_socket()
@@ -121,27 +138,19 @@
             logger.error("Failed to create listening socket: %s" % msg[1])
             return False
 
-
         bootparams = 'console=tty1 console=ttyS0,115200n8 printk.time=1'
         if extra_bootparams:
             bootparams = bootparams + ' ' + extra_bootparams
 
         self.qemuparams = 'bootparams="{0}" qemuparams="-serial tcp:127.0.0.1:{1}"'.format(bootparams, threadport)
-        if not self.display:
-            self.qemuparams = 'nographic ' + self.qemuparams
         if qemuparams:
             self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
 
+        launch_cmd += ' tcpserial=%s %s' % (self.serverport, self.qemuparams)
+
         self.origchldhandler = signal.getsignal(signal.SIGCHLD)
         signal.signal(signal.SIGCHLD, self.handleSIGCHLD)
 
-        launch_cmd = 'runqemu snapshot '
-        if self.use_kvm:
-            logger.info('Using kvm for runqemu')
-            launch_cmd += 'kvm '
-        else:
-            logger.info('Not using kvm for runqemu')
-        launch_cmd += 'tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams)
         logger.info('launchcmd=%s'%(launch_cmd))
 
         # FIXME: We pass in stdin=subprocess.PIPE here to work around stty
@@ -191,6 +200,8 @@
                     return False
             time.sleep(1)
 
+        out = self.getOutput(output)
+        netconf = False # network configuration is not required by default
         if self.is_alive():
             logger.info("qemu started - qemu procces pid is %s" % self.qemupid)
             if get_ip:
@@ -202,17 +213,27 @@
                     cmdline = re_control_char.sub('', cmdline)
                 try:
                     ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])
-                    if not ips or len(ips) != 3:
-                        raise ValueError
-                    else:
-                        self.ip = ips[0]
-                        self.server_ip = ips[1]
+                    self.ip = ips[0]
+                    self.server_ip = ips[1]
+                    logger.info("qemu cmdline used:\n{}".format(cmdline))
                 except (IndexError, ValueError):
-                    logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, self.getOutput(output)))
-                    self._dump_host()
-                    self.stop()
-                    return False
-                logger.info("qemu cmdline used:\n{}".format(cmdline))
+                    # Try to get network configuration from runqemu output
+                    match = re.match('.*Network configuration: ([0-9.]+)::([0-9.]+):([0-9.]+)$.*',
+                                     out, re.MULTILINE|re.DOTALL)
+                    if match:
+                        self.ip, self.server_ip, self.netmask = match.groups()
+                        # network configuration is required as we couldn't get it
+                        # from the runqemu command line, so qemu doesn't run kernel
+                        # and guest networking is not configured
+                        netconf = True
+                    else:
+                        logger.error("Couldn't get ip from qemu command line and runqemu output! "
+                                     "Here is the qemu command line used:\n%s\n"
+                                     "and output from runqemu:\n%s" % (cmdline, out))
+                        self._dump_host()
+                        self.stop()
+                        return False
+
                 logger.info("Target IP: %s" % self.ip)
                 logger.info("Server IP: %s" % self.server_ip)
 
@@ -221,12 +242,11 @@
             if not self.thread.connection_established.wait(self.boottime):
                 logger.error("Didn't receive a console connection from qemu. "
                              "Here is the qemu command line used:\n%s\nand "
-                             "output from runqemu:\n%s" % (cmdline,
-                                                           self.getOutput(output)))
+                             "output from runqemu:\n%s" % (cmdline, out))
                 self.stop_thread()
                 return False
 
-            logger.info("Output from runqemu:\n%s", self.getOutput(output))
+            logger.info("Output from runqemu:\n%s", out)
             logger.info("Waiting at most %d seconds for login banner" % self.boottime)
             endtime = time.time() + self.boottime
             socklist = [self.server_socket]
@@ -236,7 +256,10 @@
             bootlog = ''
             data = b''
             while time.time() < endtime and not stopread:
-                sread, swrite, serror = select.select(socklist, [], [], 5)
+                try:
+                    sread, swrite, serror = select.select(socklist, [], [], 5)
+                except InterruptedError:
+                    continue
                 for sock in sread:
                     if sock is self.server_socket:
                         qemusock, addr = self.server_socket.accept()
@@ -278,6 +301,14 @@
                 if re.search("root@[a-zA-Z0-9\-]+:~#", output):
                     self.logged = True
                     logger.info("Logged as root in serial console")
+                    if netconf:
+                        # configure guest networking
+                        cmd = "ifconfig eth0 %s netmask %s up\n" % (self.ip, self.netmask)
+                        output = self.run_serial(cmd, raw=True)[1]
+                        if re.search("root@[a-zA-Z0-9\-]+:~#", output):
+                            logger.info("configured ip address %s", self.ip)
+                        else:
+                            logger.info("Couldn't configure guest networking")
                 else:
                     logger.info("Couldn't login into serial console"
                             " as root using blank password")
@@ -295,6 +326,7 @@
 
     def stop(self):
         self.stop_thread()
+        self.stop_qemu_system()
         if hasattr(self, "origchldhandler"):
             signal.signal(signal.SIGCHLD, self.origchldhandler)
         if self.runqemu:
@@ -319,6 +351,14 @@
         self.qemupid = None
         self.ip = None
 
+    def stop_qemu_system(self):
+        if self.qemupid:
+            try:
+                # qemu-system behaves well and a SIGTERM is enough
+                os.kill(self.qemupid, signal.SIGTERM)
+            except ProcessLookupError as e:
+                logger.warn('qemu-system ended unexpectedly')
+
     def stop_thread(self):
         if self.thread and self.thread.is_alive():
             self.thread.stop()
@@ -385,7 +425,7 @@
             if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
                 return [int(p),commands[p]]
 
-    def run_serial(self, command, raw=False):
+    def run_serial(self, command, raw=False, timeout=5):
         # We assume target system have echo to get command status
         if not raw:
             command = "%s; echo $?\n" % command
@@ -393,20 +433,26 @@
         data = ''
         status = 0
         self.server_socket.sendall(command.encode('utf-8'))
-        keepreading = True
-        while keepreading:
-            sread, _, _ = select.select([self.server_socket],[],[],5)
+        start = time.time()
+        end = start + timeout
+        while True:
+            now = time.time()
+            if now >= end:
+                data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout
+                break
+            try:
+                sread, _, _ = select.select([self.server_socket],[],[], end - now)
+            except InterruptedError:
+                continue
             if sread:
                 answer = self.server_socket.recv(1024)
                 if answer:
                     data += answer.decode('utf-8')
                     # Search the prompt to stop
                     if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data):
-                        keepreading = False
+                        break
                 else:
                     raise Exception("No data on serial console socket")
-            else:
-                keepreading = False
 
         if data:
             if raw:
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
index d554f0d..1bf5900 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/qemutinyrunner.py
@@ -60,7 +60,7 @@
             with open(self.logfile, "a") as f:
                 f.write("%s" % msg)
 
-    def start(self, qemuparams = None, ssh=True, extra_bootparams=None):
+    def start(self, qemuparams = None, ssh=True, extra_bootparams=None, runqemuparams='', discard_writes=True):
 
         if self.display:
             os.environ["DISPLAY"] = self.display
@@ -107,14 +107,17 @@
 
         return self.is_alive()
 
-    def run_serial(self, command):
+    def run_serial(self, command, timeout=5):
         self.server_socket.sendall(command+'\n')
         data = ''
         status = 0
         stopread = False
-        endtime = time.time()+5
+        endtime = time.time()+timeout
         while time.time()<endtime and not stopread:
-                sread, _, _ = select.select([self.server_socket],[],[],5)
+                try:
+                        sread, _, _ = select.select([self.server_socket],[],[],1)
+                except InterruptedError:
+                        continue
                 for sock in sread:
                         answer = sock.recv(1024)
                         if answer:
@@ -124,6 +127,8 @@
                                 stopread = True
         if not data:
             status = 1
+        if not stopread:
+            data += "<<< run_serial(): command timed out after %d seconds without output >>>\r\n\r\n" % timeout
         return (status, str(data))
 
     def find_child(self,parent_pid):
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/subprocesstweak.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/subprocesstweak.py
new file mode 100644
index 0000000..1f7d11b
--- /dev/null
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/subprocesstweak.py
@@ -0,0 +1,19 @@
+import subprocess
+
+class OETestCalledProcessError(subprocess.CalledProcessError):
+    def __str__(self):
+        def strify(o):
+            if isinstance(o, bytes):
+                return o.decode("utf-8", errors="replace")
+            else:
+                return o
+
+        s = "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+        if hasattr(self, "output") and self.output:
+            s = s + "\nStandard Output: " + strify(self.output)
+        if hasattr(self, "stderr") and self.stderr:
+            s = s + "\nStandard Error: " + strify(self.stderr)
+        return s
+
+def errors_have_output():
+    subprocess.CalledProcessError = OETestCalledProcessError
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
index 59593f5..9249fa2 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/targetbuild.py
@@ -8,14 +8,19 @@
 import re
 import bb.utils
 import subprocess
+import tempfile
 from abc import ABCMeta, abstractmethod
 
 class BuildProject(metaclass=ABCMeta):
 
-    def __init__(self, d, uri, foldername=None, tmpdir="/tmp/"):
+    def __init__(self, d, uri, foldername=None, tmpdir=None):
         self.d = d
         self.uri = uri
         self.archive = os.path.basename(uri)
+        if not tmpdir:
+            tmpdir = self.d.getVar('WORKDIR')
+            if not tmpdir:
+                tmpdir = tempfile.mkdtemp(prefix='buildproject')
         self.localarchive = os.path.join(tmpdir,self.archive)
         if foldername:
             self.fname = foldername
@@ -24,8 +29,7 @@
 
     # Download self.archive to self.localarchive
     def _download_archive(self):
-
-        dl_dir = self.d.getVar("DL_DIR", True)
+        dl_dir = self.d.getVar("DL_DIR")
         if dl_dir and os.path.exists(os.path.join(dl_dir, self.archive)):
             bb.utils.copyfile(os.path.join(dl_dir, self.archive), self.localarchive)
             return
@@ -40,12 +44,12 @@
 
         cmd = ''
         for var in exportvars:
-            val = self.d.getVar(var, True)
+            val = self.d.getVar(var)
             if val:
                 cmd = 'export ' + var + '=\"%s\"; %s' % (val, cmd)
 
         cmd = cmd + "wget -O %s %s" % (self.localarchive, self.uri)
-        subprocess.check_call(cmd, shell=True)
+        subprocess.check_output(cmd, shell=True)
 
     # This method should provide a way to run a command in the desired environment.
     @abstractmethod
@@ -73,7 +77,7 @@
     def __init__(self, target, d, uri, foldername=None):
         self.target = target
         self.targetdir = "~/"
-        BuildProject.__init__(self, d, uri, foldername, tmpdir="/tmp")
+        BuildProject.__init__(self, d, uri, foldername)
 
     def download_archive(self):
 
@@ -103,8 +107,8 @@
         self.testdir = testpath
         self.targetdir = testpath
         bb.utils.mkdirhier(testpath)
-        self.datetime = d.getVar('DATETIME', True)
-        self.testlogdir = d.getVar("TEST_LOG_DIR", True)
+        self.datetime = d.getVar('DATETIME')
+        self.testlogdir = d.getVar("TEST_LOG_DIR")
         bb.utils.mkdirhier(self.testlogdir)
         self.logfile = os.path.join(self.testlogdir, "sdk_target_log.%s" % self.datetime)
         BuildProject.__init__(self, d, uri, foldername, tmpdir=testpath)
@@ -114,7 +118,7 @@
         self._download_archive()
 
         cmd = 'tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir)
-        subprocess.check_call(cmd, shell=True)
+        subprocess.check_output(cmd, shell=True)
 
         #Change targetdir to project folder
         self.targetdir = os.path.join(self.targetdir, self.fname)
diff --git a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
index 57be2ca..be2a211 100644
--- a/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
+++ b/import-layers/yocto-poky/meta/lib/oeqa/utils/testexport.py
@@ -72,9 +72,9 @@
         return extract_bin_command
 
     if determine_if_poky_env(): # machine with poky environment
-        exportpath = d.getVar("TEST_EXPORT_DIR", True) if export_env else d.getVar("DEPLOY_DIR", True)
-        rpm_deploy_dir = d.getVar("DEPLOY_DIR_RPM", True)
-        arch = get_dest_folder(d.getVar("TUNE_FEATURES", True), os.listdir(rpm_deploy_dir))
+        exportpath = d.getVar("TEST_EXPORT_DIR") if export_env else d.getVar("DEPLOY_DIR")
+        rpm_deploy_dir = d.getVar("DEPLOY_DIR_RPM")
+        arch = get_dest_folder(d.getVar("TUNE_FEATURES"), os.listdir(rpm_deploy_dir))
         arch_rpm_dir = os.path.join(rpm_deploy_dir, arch)
         extracted_bin_dir = os.path.join(exportpath,"binaries", arch, "extracted_binaries")
         packaged_bin_dir = os.path.join(exportpath,"binaries", arch, "packaged_binaries")
@@ -92,7 +92,7 @@
                     return ""
                 for item in native_rpm_file_list:# will copy all versions of package. Used version will be selected on remote machine
                     bb.plain("Copying native package file: %s" % item)
-                    sh.copy(os.path.join(rpm_deploy_dir, native_rpm_dir, item), os.path.join(d.getVar("TEST_EXPORT_DIR", True), "binaries", "native"))
+                    sh.copy(os.path.join(rpm_deploy_dir, native_rpm_dir, item), os.path.join(d.getVar("TEST_EXPORT_DIR"), "binaries", "native"))
             else: # nothing to do here; running tests under bitbake, so we asume native binaries are in sysroots dir.
                 if param_list[1] or param_list[4]:
                     bb.warn("Native binary %s %s%s. Running tests under bitbake environment. Version can't be checked except when the test itself does it"
@@ -148,7 +148,7 @@
         else: # this is for target device
             if param_list[2] == "rpm":
                 return "No need to extract, this is an .rpm file"
-            arch = get_dest_folder(d.getVar("TUNE_FEATURES", True), os.listdir(binaries_path))
+            arch = get_dest_folder(d.getVar("TUNE_FEATURES"), os.listdir(binaries_path))
             extracted_bin_path = os.path.join(binaries_path, arch, "extracted_binaries")
             extracted_bin_list = [item for item in os.listdir(extracted_bin_path)]
             packaged_bin_path = os.path.join(binaries_path, arch, "packaged_binaries")
@@ -206,9 +206,9 @@
     from oeqa.oetest import oeRuntimeTest
     param_list = params
     cleanup_list = list()
-    bins_dir = os.path.join(d.getVar("TEST_EXPORT_DIR", True), "binaries") if determine_if_poky_env() \
+    bins_dir = os.path.join(d.getVar("TEST_EXPORT_DIR"), "binaries") if determine_if_poky_env() \
                     else os.getenv("bin_dir")
-    arch = get_dest_folder(d.getVar("TUNE_FEATURES", True), os.listdir(bins_dir))
+    arch = get_dest_folder(d.getVar("TUNE_FEATURES"), os.listdir(bins_dir))
     arch_rpms_dir = os.path.join(bins_dir, arch, "packaged_binaries")
     extracted_bin_dir = os.path.join(bins_dir, arch, "extracted_binaries", param_list[0])