[bitbake-devel] [[RFC][PATCH] 1/8] toaster/tests: Reorder/move current test code.

Aníbal Limón anibal.limon at linux.intel.com
Tue Feb 23 00:51:51 UTC 2016


In order to provide a better structure of the toaster test
suite:

bin/toaster-test: Main script for run toaster tests.
lib/toaster/tests: Contains ui test code and configuration.

Signed-off-by: Aníbal Limón <anibal.limon at linux.intel.com>
---
 bin/toaster-test                                   |  155 ++
 .../contrib/tts/toasteruitest/run_toastertests.py  |  155 --
 .../tts/toasteruitest/toaster_automation_test.py   | 2376 --------------------
 .../contrib/tts/toasteruitest/toaster_test.cfg     |   25 -
 lib/toaster/tests/toaster_test.cfg                 |   25 +
 lib/toaster/tests/ui.py                            | 2376 ++++++++++++++++++++
 6 files changed, 2556 insertions(+), 2556 deletions(-)
 create mode 100755 bin/toaster-test
 delete mode 100755 lib/toaster/contrib/tts/toasteruitest/run_toastertests.py
 delete mode 100755 lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py
 delete mode 100644 lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg
 create mode 100644 lib/toaster/tests/toaster_test.cfg
 create mode 100644 lib/toaster/tests/ui.py

diff --git a/bin/toaster-test b/bin/toaster-test
new file mode 100755
index 0000000..2b312cb
--- /dev/null
+++ b/bin/toaster-test
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+
+# Copyright
+
+# DESCRIPTION
+# This is script for running all selected toaster cases on
+# selected web browsers manifested in toaster_test.cfg.
+
+# 1. How to start toaster in yocto:
+# $ source poky/oe-init-build-env
+# $ source toaster start
+# $ bitbake core-image-minimal
+
+# 2. How to install selenium on Ubuntu:
+# $ sudo apt-get install scrot python-pip
+# $ sudo pip install selenium
+
+# 3. How to install selenium addon in firefox:
+# Download the lastest firefox addon from http://release.seleniumhq.org/selenium-ide/
+# Then install it. You can also install firebug and firepath addon
+
+# 4. How to start writing a new case:
+# All you need to do is to implement the function test_xxx() and  pile it on.
+
+# 5. How to test with Chrome browser
+# Download/install chrome on host
+# Download chromedriver from https://code.google.com/p/chromedriver/downloads/list  according to your host type
+# put chromedriver in PATH, (e.g. /usr/bin/, bear in mind to chmod)
+# For windows host, you may put chromedriver.exe in the same directory as chrome.exe
+
+import unittest, sys, os, platform
+import ConfigParser
+import argparse
+from toaster_automation_test import toaster_cases
+
+
+def get_args_parser():
+    description = "Script that runs toaster auto tests."
+    parser = argparse.ArgumentParser(description=description)
+    parser.add_argument('--run-all-tests', required=False, action="store_true", dest="run_all_tests", default=False,
+                       help='Run all tests.')
+    parser.add_argument('--run-suite', required=False, dest='run_suite', default=False,
+                       help='run suite (defined in cfg file)')
+
+    return parser
+
+
+def get_tests():
+    testslist = []
+
+    prefix = 'toaster_automation_test.toaster_cases'
+
+    for t in dir(toaster_cases):
+        if t.startswith('test_'):
+            testslist.append('.'.join((prefix, t)))
+
+    return testslist
+
+
+def get_tests_from_cfg(suite=None):
+
+    testslist = []
+    config = ConfigParser.SafeConfigParser()
+    config.read('toaster_test.cfg')
+
+    if suite is not None:
+        target_suite = suite.lower()
+
+        # TODO: if suite is valid suite
+
+    else:
+        target_suite = platform.system().lower()
+
+    try:
+        tests_from_cfg = eval(config.get('toaster_test_' + target_suite, 'test_cases'))
+    except:
+        print 'Failed to get test cases from cfg file. Make sure the format is correct.'
+        return None
+
+    prefix = 'toaster_automation_test.toaster_cases.test_'
+    for t in tests_from_cfg:
+        testslist.append(prefix + str(t))
+
+    return testslist
+
+def main():
+
+    # In case this script is called from other directory
+    os.chdir(os.path.abspath(sys.path[0]))
+
+    parser = get_args_parser()
+    args = parser.parse_args()
+
+    if args.run_all_tests:
+        testslist = get_tests()
+    elif args.run_suite:
+        testslist = get_tests_from_cfg(args.run_suite)
+        os.environ['TOASTER_SUITE'] = args.run_suite
+    else:
+        testslist = get_tests_from_cfg()
+
+    if not testslist:
+        print 'Failed to get test cases.'
+        exit(1)
+
+    suite = unittest.TestSuite()
+    loader = unittest.TestLoader()
+    loader.sortTestMethodsUsing = None
+    runner = unittest.TextTestRunner(verbosity=2, resultclass=buildResultClass(args))
+
+    for test in testslist:
+        try:
+            suite.addTests(loader.loadTestsFromName(test))
+        except:
+            return 1
+
+    result = runner.run(suite)
+
+    if result.wasSuccessful():
+        return 0
+    else:
+        return 1
+
+
+def buildResultClass(args):
+    """Build a Result Class to use in the testcase execution"""
+
+    class StampedResult(unittest.TextTestResult):
+        """
+        Custom TestResult that prints the time when a test starts.  As toaster-auto
+        can take a long time (ie a few hours) to run, timestamps help us understand
+        what tests are taking a long time to execute.
+        """
+        def startTest(self, test):
+            import time
+            self.stream.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " - ")
+            super(StampedResult, self).startTest(test)
+
+    return StampedResult
+
+
+if __name__ == "__main__":
+
+    try:
+        ret = main()
+    except:
+        ret = 1
+        import traceback
+        traceback.print_exc(5)
+    finally:
+        if os.getenv('TOASTER_SUITE'):
+            del os.environ['TOASTER_SUITE']
+    sys.exit(ret)
+
+
diff --git a/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py b/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py
deleted file mode 100755
index 2b312cb..0000000
--- a/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/python
-
-# Copyright
-
-# DESCRIPTION
-# This is script for running all selected toaster cases on
-# selected web browsers manifested in toaster_test.cfg.
-
-# 1. How to start toaster in yocto:
-# $ source poky/oe-init-build-env
-# $ source toaster start
-# $ bitbake core-image-minimal
-
-# 2. How to install selenium on Ubuntu:
-# $ sudo apt-get install scrot python-pip
-# $ sudo pip install selenium
-
-# 3. How to install selenium addon in firefox:
-# Download the lastest firefox addon from http://release.seleniumhq.org/selenium-ide/
-# Then install it. You can also install firebug and firepath addon
-
-# 4. How to start writing a new case:
-# All you need to do is to implement the function test_xxx() and  pile it on.
-
-# 5. How to test with Chrome browser
-# Download/install chrome on host
-# Download chromedriver from https://code.google.com/p/chromedriver/downloads/list  according to your host type
-# put chromedriver in PATH, (e.g. /usr/bin/, bear in mind to chmod)
-# For windows host, you may put chromedriver.exe in the same directory as chrome.exe
-
-import unittest, sys, os, platform
-import ConfigParser
-import argparse
-from toaster_automation_test import toaster_cases
-
-
-def get_args_parser():
-    description = "Script that runs toaster auto tests."
-    parser = argparse.ArgumentParser(description=description)
-    parser.add_argument('--run-all-tests', required=False, action="store_true", dest="run_all_tests", default=False,
-                       help='Run all tests.')
-    parser.add_argument('--run-suite', required=False, dest='run_suite', default=False,
-                       help='run suite (defined in cfg file)')
-
-    return parser
-
-
-def get_tests():
-    testslist = []
-
-    prefix = 'toaster_automation_test.toaster_cases'
-
-    for t in dir(toaster_cases):
-        if t.startswith('test_'):
-            testslist.append('.'.join((prefix, t)))
-
-    return testslist
-
-
-def get_tests_from_cfg(suite=None):
-
-    testslist = []
-    config = ConfigParser.SafeConfigParser()
-    config.read('toaster_test.cfg')
-
-    if suite is not None:
-        target_suite = suite.lower()
-
-        # TODO: if suite is valid suite
-
-    else:
-        target_suite = platform.system().lower()
-
-    try:
-        tests_from_cfg = eval(config.get('toaster_test_' + target_suite, 'test_cases'))
-    except:
-        print 'Failed to get test cases from cfg file. Make sure the format is correct.'
-        return None
-
-    prefix = 'toaster_automation_test.toaster_cases.test_'
-    for t in tests_from_cfg:
-        testslist.append(prefix + str(t))
-
-    return testslist
-
-def main():
-
-    # In case this script is called from other directory
-    os.chdir(os.path.abspath(sys.path[0]))
-
-    parser = get_args_parser()
-    args = parser.parse_args()
-
-    if args.run_all_tests:
-        testslist = get_tests()
-    elif args.run_suite:
-        testslist = get_tests_from_cfg(args.run_suite)
-        os.environ['TOASTER_SUITE'] = args.run_suite
-    else:
-        testslist = get_tests_from_cfg()
-
-    if not testslist:
-        print 'Failed to get test cases.'
-        exit(1)
-
-    suite = unittest.TestSuite()
-    loader = unittest.TestLoader()
-    loader.sortTestMethodsUsing = None
-    runner = unittest.TextTestRunner(verbosity=2, resultclass=buildResultClass(args))
-
-    for test in testslist:
-        try:
-            suite.addTests(loader.loadTestsFromName(test))
-        except:
-            return 1
-
-    result = runner.run(suite)
-
-    if result.wasSuccessful():
-        return 0
-    else:
-        return 1
-
-
-def buildResultClass(args):
-    """Build a Result Class to use in the testcase execution"""
-
-    class StampedResult(unittest.TextTestResult):
-        """
-        Custom TestResult that prints the time when a test starts.  As toaster-auto
-        can take a long time (ie a few hours) to run, timestamps help us understand
-        what tests are taking a long time to execute.
-        """
-        def startTest(self, test):
-            import time
-            self.stream.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " - ")
-            super(StampedResult, self).startTest(test)
-
-    return StampedResult
-
-
-if __name__ == "__main__":
-
-    try:
-        ret = main()
-    except:
-        ret = 1
-        import traceback
-        traceback.print_exc(5)
-    finally:
-        if os.getenv('TOASTER_SUITE'):
-            del os.environ['TOASTER_SUITE']
-    sys.exit(ret)
-
-
diff --git a/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py b/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py
deleted file mode 100755
index d8f838a..0000000
--- a/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py
+++ /dev/null
@@ -1,2376 +0,0 @@
-#!/usr/bin/python
-# Copyright
-
-# DESCRIPTION
-# This is toaster automation base class and test cases file
-
-# History:
-# 2015.03.09  inital version
-# 2015.03.23  adding toaster_test.cfg, run_toastertest.py so we can run case by case from outside
-
-# Briefs:
-# This file is comprised of 3 parts:
-# I:   common utils like sorting, getting attribute.. etc
-# II:  base class part, which complies with unittest frame work and
-#      contains class selenium-based functions
-# III: test cases
-#      to add new case: just implement new test_xxx() function in class toaster_cases
-
-# NOTES for cases:
-# case 946:
-# step 6 - 8 needs to be observed using screenshots
-# case 956:
-# step 2 - 3 needs to be run manually
-
-import unittest, time, re, sys, getopt, os, logging, string, errno, exceptions
-import shutil, argparse, ConfigParser, platform, json
-from selenium import webdriver
-from selenium.common.exceptions import NoSuchElementException
-from selenium import selenium
-from selenium.webdriver.common.by import By
-from selenium.webdriver.common.keys import Keys
-from selenium.webdriver.support.ui import Select
-import sqlite3 as sqlite
-
-
-###########################################
-#                                         #
-# PART I: utils stuff                     #
-#                                         #
-###########################################
-
-class Listattr(object):
-    """
-    Set of list attribute. This is used to determine what the list content is.
-    Later on we may add more attributes here.
-    """
-    NULL = "null"
-    NUMBERS = "numbers"
-    STRINGS = "strings"
-    PERCENT = "percentage"
-    SIZE = "size"
-    UNKNOWN = "unknown"
-
-
-def get_log_root_dir():
-    max_depth = 5
-    parent_dir = '../'
-    for number in range(0, max_depth):
-        if os.path.isdir(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log'):
-            log_root_dir = os.path.abspath(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log')
-            break
-
-    if number == (max_depth - 1):
-        print 'No log dir found. Please check'
-        raise Exception
-
-    return log_root_dir
-
-
-def mkdir_p(dir):
-    try:
-        os.makedirs(dir)
-    except OSError as exc:
-        if exc.errno == errno.EEXIST and os.path.isdir(dir):
-            pass
-        else:
-            raise
-
-
-def get_list_attr(testlist):
-    """
-    To determine the list content
-    """
-    if not testlist:
-        return Listattr.NULL
-    listtest = testlist[:]
-    try:
-        listtest.remove('')
-    except ValueError:
-        pass
-    pattern_percent = re.compile(r"^([0-9])+(\.)?([0-9])*%$")
-    pattern_size = re.compile(r"^([0-9])+(\.)?([0-9])*( )*(K)*(M)*(G)*B$")
-    pattern_number = re.compile(r"^([0-9])+(\.)?([0-9])*$")
-    def get_patterned_number(pattern, tlist):
-        count = 0
-        for item in tlist:
-            if re.search(pattern, item):
-                count += 1
-        return count
-    if get_patterned_number(pattern_percent, listtest) == len(listtest):
-        return Listattr.PERCENT
-    elif get_patterned_number(pattern_size, listtest) == len(listtest):
-        return Listattr.SIZE
-    elif get_patterned_number(pattern_number, listtest) == len(listtest):
-        return Listattr.NUMBERS
-    else:
-        return Listattr.STRINGS
-
-
-def is_list_sequenced(testlist):
-    """
-    Function to tell if list is sequenced
-    Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
-    Each has respective way to determine if it's sequenced.
-    """
-    test_list = testlist[:]
-    try:
-        test_list.remove('')
-    except ValueError:
-        pass
-
-    if get_list_attr(testlist) == Listattr.NULL :
-        return True
-
-    elif get_list_attr(testlist) == Listattr.STRINGS :
-        return (sorted(test_list) == test_list)
-
-    elif get_list_attr(testlist) == Listattr.NUMBERS :
-        list_number = []
-        for item in test_list:
-            list_number.append(eval(item))
-        return (sorted(list_number) == list_number)
-
-    elif get_list_attr(testlist) == Listattr.PERCENT :
-        list_number = []
-        for item in test_list:
-            list_number.append(eval(item.strip('%')))
-        return (sorted(list_number) == list_number)
-
-    elif get_list_attr(testlist) == Listattr.SIZE :
-        list_number = []
-        # currently SIZE is splitted by space
-        for item in test_list:
-            if item.split()[1].upper() == "KB":
-                list_number.append(1024 * eval(item.split()[0]))
-            elif item.split()[1].upper() == "MB":
-                list_number.append(1024 * 1024 * eval(item.split()[0]))
-            elif item.split()[1].upper() == "GB":
-                list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
-            else:
-                list_number.append(eval(item.split()[0]))
-        return (sorted(list_number) == list_number)
-
-    else:
-        print 'Unrecognized list type, please check'
-        return False
-
-
-def is_list_inverted(testlist):
-    """
-    Function to tell if list is inverted
-    Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
-    Each has respective way to determine if it's inverted.
-    """
-    test_list = testlist[:]
-    try:
-        test_list.remove('')
-    except ValueError:
-        pass
-
-    if get_list_attr(testlist) == Listattr.NULL :
-        return True
-
-    elif get_list_attr(testlist) == Listattr.STRINGS :
-        return (sorted(test_list, reverse = True) == test_list)
-
-    elif get_list_attr(testlist) == Listattr.NUMBERS :
-        list_number = []
-        for item in test_list:
-            list_number.append(eval(item))
-        return (sorted(list_number, reverse = True) == list_number)
-
-    elif get_list_attr(testlist) == Listattr.PERCENT :
-        list_number = []
-        for item in test_list:
-            list_number.append(eval(item.strip('%')))
-        return (sorted(list_number, reverse = True) == list_number)
-
-    elif get_list_attr(testlist) == Listattr.SIZE :
-        list_number = []
-        # currently SIZE is splitted by space. such as 0 B; 1 KB; 2 MB
-        for item in test_list:
-            if item.split()[1].upper() == "KB":
-                list_number.append(1024 * eval(item.split()[0]))
-            elif item.split()[1].upper() == "MB":
-                list_number.append(1024 * 1024 * eval(item.split()[0]))
-            elif item.split()[1].upper() == "GB":
-                list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
-            else:
-                list_number.append(eval(item.split()[0]))
-        return (sorted(list_number, reverse = True) == list_number)
-
-    else:
-        print 'Unrecognized list type, please check'
-        return False
-
-def replace_file_content(filename, item, option):
-    f = open(filename)
-    lines = f.readlines()
-    f.close()
-    output = open(filename, 'w')
-    for line in lines:
-        if line.startswith(item):
-            output.write(item + " = '" + option + "'\n")
-        else:
-            output.write(line)
-    output.close()
-
-def extract_number_from_string(s):
-    """
-    extract the numbers in a string. return type is 'list'
-    """
-    return re.findall(r'([0-9]+)', s)
-
-# Below is decorator derived from toaster backend test code
-class NoParsingFilter(logging.Filter):
-    def filter(self, record):
-        return record.levelno == 100
-
-def LogResults(original_class):
-    orig_method = original_class.run
-
-    from time import strftime, gmtime
-    caller = 'toaster'
-    timestamp = strftime('%Y%m%d%H%M%S',gmtime())
-    logfile = os.path.join(os.getcwd(),'results-'+caller+'.'+timestamp+'.log')
-    linkfile = os.path.join(os.getcwd(),'results-'+caller+'.log')
-
-    #rewrite the run method of unittest.TestCase to add testcase logging
-    def run(self, result, *args, **kws):
-        orig_method(self, result, *args, **kws)
-        passed = True
-        testMethod = getattr(self, self._testMethodName)
-        #if test case is decorated then use it's number, else use it's name
-        try:
-            test_case = testMethod.test_case
-        except AttributeError:
-            test_case = self._testMethodName
-
-        class_name = str(testMethod.im_class).split("'")[1]
-
-        #create custom logging level for filtering.
-        custom_log_level = 100
-        logging.addLevelName(custom_log_level, 'RESULTS')
-
-        def results(self, message, *args, **kws):
-            if self.isEnabledFor(custom_log_level):
-                self.log(custom_log_level, message, *args, **kws)
-        logging.Logger.results = results
-
-        logging.basicConfig(filename=logfile,
-                            filemode='w',
-                            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
-                            datefmt='%H:%M:%S',
-                            level=custom_log_level)
-        for handler in logging.root.handlers:
-            handler.addFilter(NoParsingFilter())
-        local_log = logging.getLogger(caller)
-
-        #check status of tests and record it
-
-        for (name, msg) in result.errors:
-            if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
-                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]):
-                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]):
-                local_log.results("Testcase "+str(test_case)+": SKIPPED")
-                passed = False
-        if passed:
-            local_log.results("Testcase "+str(test_case)+": PASSED")
-
-        # Create symlink to the current log
-        if os.path.exists(linkfile):
-            os.remove(linkfile)
-        os.symlink(logfile, linkfile)
-
-    original_class.run = run
-
-    return original_class
-
-
-###########################################
-#                                         #
-# PART II: base class                     #
-#                                         #
-###########################################
-
- at LogResults
-class toaster_cases_base(unittest.TestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.log = cls.logger_create()
-
-    def setUp(self):
-        self.screenshot_sequence = 1
-        self.verificationErrors = []
-        self.accept_next_alert = True
-        self.host_os = platform.system().lower()
-        if os.getenv('TOASTER_SUITE'):
-            self.target_suite = os.getenv('TOASTER_SUITE')
-        else:
-            self.target_suite = self.host_os
-
-        self.parser = ConfigParser.SafeConfigParser()
-        self.parser.read('toaster_test.cfg')
-        self.base_url = eval(self.parser.get('toaster_test_' + self.target_suite, 'toaster_url'))
-
-        # create log dir . Currently , we put log files in log/tmp. After all
-        # test cases are done, move them to log/$datetime dir
-        self.log_tmp_dir = os.path.abspath(sys.path[0]) + os.sep + 'log' + os.sep + 'tmp'
-        try:
-            mkdir_p(self.log_tmp_dir)
-        except OSError :
-            logging.error("%(asctime)s Cannot create tmp dir under log, please check your privilege")
-        # self.log = self.logger_create()
-        # driver setup
-        self.setup_browser()
-
-    @staticmethod
-    def logger_create():
-        log_file = "toaster-auto-" + time.strftime("%Y%m%d%H%M%S") + ".log"
-        if os.path.exists("toaster-auto.log"): os.remove("toaster-auto.log")
-        os.symlink(log_file, "toaster-auto.log")
-
-        log = logging.getLogger("toaster")
-        log.setLevel(logging.DEBUG)
-
-        fh = logging.FileHandler(filename=log_file, mode='w')
-        fh.setLevel(logging.DEBUG)
-
-        ch = logging.StreamHandler(sys.stdout)
-        ch.setLevel(logging.INFO)
-
-        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-        fh.setFormatter(formatter)
-        ch.setFormatter(formatter)
-
-        log.addHandler(fh)
-        log.addHandler(ch)
-
-        return log
-
-
-    def setup_browser(self, *browser_path):
-        self.browser = eval(self.parser.get('toaster_test_' + self.target_suite, 'test_browser'))
-        print self.browser
-        if self.browser == "firefox":
-            driver = webdriver.Firefox()
-        elif self.browser == "chrome":
-            driver = webdriver.Chrome()
-        elif self.browser == "ie":
-            driver = webdriver.Ie()
-        else:
-            driver = None
-            print "unrecognized browser type, please check"
-        self.driver = driver
-        self.driver.implicitly_wait(30)
-        return self.driver
-
-
-    def save_screenshot(self,  **log_args):
-        """
-        This function is used to save screen either by os interface or selenium interface.
-        How to use:
-        self.save_screenshot(screenshot_type = 'native'/'selenium', log_sub_dir = 'xxx',
-                             append_name = 'stepx')
-        where native means screenshot func provided by OS,
-        selenium means screenshot func provided by selenium webdriver
-        """
-        types = [log_args.get('screenshot_type')]
-        # when no screenshot_type is specified
-        if types == [None]:
-            types = ['native', 'selenium']
-        # normally append_name is used to specify which step..
-        add_name = log_args.get('append_name')
-        if not add_name:
-            add_name = '-'
-        # normally there's no need to specify sub_dir
-        sub_dir = log_args.get('log_sub_dir')
-        if not sub_dir:
-            # use casexxx as sub_dir name
-            sub_dir = 'case' + str(self.case_no)
-        for item in types:
-            log_dir = self.log_tmp_dir + os.sep + sub_dir
-            mkdir_p(log_dir)
-            log_path = log_dir + os.sep +  self.browser + '-' +\
-                    item + '-' + add_name + '-' + str(self.screenshot_sequence) + '.png'
-            if item == 'native':
-                if self.host_os == "linux":
-                    os.system("scrot " + log_path)
-                elif self.host_os=="darwin":
-                    os.system("screencapture -x " + log_path)
-            elif item == 'selenium':
-                self.driver.get_screenshot_as_file(log_path)
-            self.screenshot_sequence += 1
-
-    def browser_delay(self):
-        """
-        currently this is a workaround for some chrome test.
-        Sometimes we need a delay to accomplish some operation.
-        But for firefox, mostly we don't need this.
-        To be discussed
-        """
-        if self.browser == "chrome":
-            time.sleep(1)
-        return
-
-
-# these functions are not contained in WebDriver class..
-    def find_element_by_text(self, string):
-        return self.driver.find_element_by_xpath("//*[text()='" + string + "']")
-
-
-    def find_elements_by_text(self, string):
-        return self.driver.find_elements_by_xpath("//*[text()='" + string + "']")
-
-
-    def find_element_by_text_in_table(self, table_id, text_string):
-        """
-        This is used to search some certain 'text' in certain table
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element = table_element.find_element_by_xpath("//*[text()='" + text_string + "']")
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-        return element
-
-
-    def find_element_by_link_text_in_table(self, table_id, link_text):
-        """
-        Assume there're multiple suitable "find_element_by_link_text".
-        In this circumstance we need to specify "table".
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element = table_element.find_element_by_link_text(link_text)
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-        return element
-
-
-    def find_elements_by_link_text_in_table(self, table_id, link_text):
-        """
-        Search link-text in certain table. This helps to narrow down search area.
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element_list = table_element.find_elements_by_link_text(link_text)
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-        return element_list
-
-
-    def find_element_by_partial_link_text_in_table(self, table_id, link_text):
-        """
-        Search element by partial link text in certain table.
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element = table_element.find_element_by_partial_link_text(link_text)
-            return element
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-
-
-    def find_elements_by_partial_link_text_in_table(self, table_id, link_text):
-        """
-        Assume there're multiple suitable "find_partial_element_by_link_text".
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element_list = table_element.find_elements_by_partial_link_text(link_text)
-            return element_list
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-
-
-    def find_element_by_xpath_in_table(self, table_id, xpath):
-        """
-        This helps to narrow down search area. Especially useful when dealing with pop-up form.
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element = table_element.find_element_by_xpath(xpath)
-        except NoSuchElementException, e:
-            print 'no element found'
-            raise
-        return element
-
-
-    def find_elements_by_xpath_in_table(self, table_id, xpath):
-        """
-        This helps to narrow down search area. Especially useful when dealing with pop-up form.
-        """
-        try:
-            table_element = self.get_table_element(table_id)
-            element_list = table_element.find_elements_by_xpath(xpath)
-        except NoSuchElementException, e:
-            print 'no elements found'
-            raise
-        return element_list
-
-
-    def shortest_xpath(self, pname, pvalue):
-        return "//*[@" + pname + "='" + pvalue + "']"
-
-
-#usually elements in the same column are with same class name. for instance: class="outcome" .TBD
-    def get_table_column_text(self, attr_name, attr_value):
-        c_xpath = self.shortest_xpath(attr_name, attr_value)
-        elements = self.driver.find_elements_by_xpath(c_xpath)
-        c_list = []
-        for element in elements:
-            c_list.append(element.text)
-        return c_list
-
-
-    def get_table_column_text_by_column_number(self, table_id, column_number):
-        c_xpath = "//*[@id='" + table_id + "']//td[" + str(column_number) + "]"
-        elements = self.driver.find_elements_by_xpath(c_xpath)
-        c_list = []
-        for element in elements:
-            c_list.append(element.text)
-        return c_list
-
-
-    def get_table_head_text(self, *table_id):
-#now table_id is a tuple...
-        if table_id:
-            thead_xpath = "//*[@id='" + table_id[0] + "']//thead//th[text()]"
-            elements = self.driver.find_elements_by_xpath(thead_xpath)
-            c_list = []
-            for element in elements:
-                if element.text:
-                    c_list.append(element.text)
-            return c_list
-#default table on page
-        else:
-            return self.driver.find_element_by_xpath("//*/table/thead").text
-
-
-
-    def get_table_element(self, table_id, *coordinate):
-        if len(coordinate) == 0:
-#return whole-table element
-            element_xpath = "//*[@id='" + table_id + "']"
-            try:
-                element = self.driver.find_element_by_xpath(element_xpath)
-            except NoSuchElementException, e:
-                raise
-            return element
-        row = coordinate[0]
-
-        if len(coordinate) == 1:
-#return whole-row element
-            element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]"
-            try:
-                element = self.driver.find_element_by_xpath(element_xpath)
-            except NoSuchElementException, e:
-                return False
-            return element
-#now we are looking for an element with specified X and Y
-        column = coordinate[1]
-
-        element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]/td[" + str(column) + "]"
-        try:
-            element = self.driver.find_element_by_xpath(element_xpath)
-        except NoSuchElementException, e:
-            return False
-        return element
-
-
-    def get_table_data(self, table_id, row_count, column_count):
-        row = 1
-        Lists = []
-        while row <= row_count:
-            column = 1
-            row_content=[]
-            while column <= column_count:
-                s= "//*[@id='" + table_id + "']/tbody/tr[" + str(row) +"]/td[" + str(column) + "]"
-                v = self.driver.find_element_by_xpath(s).text
-                row_content.append(v)
-                column = column + 1
-                print("row_content=",row_content)
-            Lists.extend(row_content)
-            print Lists[row-1][0]
-            row = row + 1
-        return Lists
-
-    # The is_xxx_present functions only returns True/False
-    # All the log work is done in test procedure, so we can easily trace back
-    # using logging
-    def is_text_present (self, patterns):
-        for pattern in patterns:
-            if str(pattern) not in self.driver.page_source:
-                print 'Text "'+pattern+'" is missing'
-                return False
-        return True
-
-
-    def is_element_present(self, how, what):
-        try:
-            self.driver.find_element(how, what)
-        except NoSuchElementException, e:
-            print 'Could not find element '+str(what)+' by ' + str(how)
-            return False
-        return True
-
-
-    def is_alert_present(self):
-        try: self.driver.switch_to_alert()
-        except NoAlertPresentException, e: return False
-        return True
-
-
-    def close_alert_and_get_its_text(self):
-        try:
-            alert = self.driver.switch_to_alert()
-            alert_text = alert.text
-            if self.accept_next_alert:
-                alert.accept()
-            else:
-                alert.dismiss()
-            return alert_text
-        finally: self.accept_next_alert = True
-
-
-    def get_case_number(self):
-        """
-        what case are we running now
-        """
-        funcname = sys._getframe(1).f_code.co_name
-        caseno_str = funcname.strip('test_')
-        try:
-            caseno = int(caseno_str)
-        except ValueError:
-            print "get case number error! please check if func name is test_xxx"
-            return False
-        return caseno
-
-
-    def tearDown(self):
-        self.log.info(' END: CASE %s log \n\n' % str(self.case_no))
-        self.driver.quit()
-        self.assertEqual([], self.verificationErrors)
-
-
-###################################################################
-#                                                                 #
-# PART III: test cases                                            #
-# please refer to                                                 #
-# https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=xxx  #
-#                                                                 #
-###################################################################
-
-# Note: to comply with the unittest framework, we call these test_xxx functions
-# from run_toastercases.py to avoid calling setUp() and tearDown() multiple times
-
-
-class toaster_cases(toaster_cases_base):
-        ##############
-        #  CASE 901  #
-        ##############
-    def test_901(self):
-        # the reason why get_case_number is not in setUp function is that
-        # otherwise it returns "setUp" instead of "test_xxx"
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # open all columns
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # adding explicitly wait for chromedriver..-_-
-        self.browser_delay()
-        self.driver.find_element_by_id("started_on").click()
-        self.browser_delay()
-        self.driver.find_element_by_id("time").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # dict: {lint text name : actual class name}
-        table_head_dict = {'Outcome':'outcome', 'Recipe':'target', 'Machine':'machine', 'Started on':'started_on', 'Completed on':'completed_on', \
-                'Errors':'errors_no', 'Warnings':'warnings_no', 'Time':'time'}
-        for key in table_head_dict:
-            try:
-                self.driver.find_element_by_link_text(key).click()
-            except Exception, e:
-                self.log.error("%s cannot be found on page" % key)
-                raise
-            column_list = self.get_table_column_text("class", table_head_dict[key])
-            # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
-            # the point is, after another click, it should be another order
-            if is_list_inverted(column_list):
-                self.driver.find_element_by_link_text(key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
-            else:
-                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not sequenced" % key))
-                self.driver.find_element_by_link_text(key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
-        self.log.info("case passed")
-
-
-        ##############
-        #  CASE 902  #
-        ##############
-    def test_902(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # Could add more test patterns here in the future. Also, could search some items other than target column in future..
-        patterns = ["minimal", "sato"]
-        for pattern in patterns:
-            ori_target_column_texts = self.get_table_column_text("class", "target")
-            print ori_target_column_texts
-            self.driver.find_element_by_id("search").clear()
-            self.driver.find_element_by_id("search").send_keys(pattern)
-            self.driver.find_element_by_id("search-button").click()
-            new_target_column_texts = self.get_table_column_text("class", "target")
-            # if nothing found, we still count it as "pass"
-            if new_target_column_texts:
-                for text in new_target_column_texts:
-                    self.assertTrue(text.find(pattern), msg=("%s item doesn't exist " % pattern))
-            self.driver.find_element_by_css_selector("i.icon-remove").click()
-            target_column_texts = self.get_table_column_text("class", "target")
-            self.assertTrue(ori_target_column_texts == target_column_texts, msg=("builds changed after operations"))
-
-
-        ##############
-        #  CASE 903  #
-        ##############
-    def test_903(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # when opening a new page, "started_on" is not displayed by default
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # currently all the delay are for chrome driver -_-
-        self.browser_delay()
-        self.driver.find_element_by_id("started_on").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # step 4
-        items = ["Outcome", "Completed on", "Started on"]
-        for item in items:
-            try:
-                temp_element = self.find_element_by_text_in_table('otable', item)
-                # this is how we find "filter icon" in the same level as temp_element(where "a" means clickable, "i" means icon)
-                self.assertTrue(temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']"))
-            except Exception,e:
-                self.assertFalse(True, msg=(" %s cannot be found! %s" % (item, e)))
-                raise
-        # step 5-6
-        temp_element = self.find_element_by_link_text_in_table('otable', 'Outcome')
-        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
-        self.browser_delay()
-        # the 2nd option, whatever it is
-        self.driver.find_element_by_xpath("(//input[@name='filter'])[2]").click()
-        # click "Apply" button
-        self.driver.find_element_by_xpath("//*[@id='filter_outcome']//*[text()='Apply']").click()
-        # save screen here
-        time.sleep(1)
-        self.save_screenshot(screenshot_type='selenium', append_name='step5')
-        temp_element = self.find_element_by_link_text_in_table('otable', 'Completed on')
-        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
-        self.browser_delay()
-        self.driver.find_element_by_xpath("//*[@id='filter_completed_on']//*[text()='Apply']").click()
-        # save screen here to compare to previous one
-        # please note that for chrome driver, need a little break before saving
-        # screen here -_-
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step6')
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("core-image")
-        self.driver.find_element_by_id("search-button").click()
-
-
-        ##############
-        #  CASE 904  #
-        ##############
-    def test_904(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_partial_link_text("core-image").click()
-        self.driver.find_element_by_link_text("Tasks").click()
-        self.table_name = 'otable'
-        # This is how we find the "default" rows-number!
-        rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
-        print rows_displayed
-        self.assertTrue(self.get_table_element(self.table_name, rows_displayed), msg=("not enough rows displayed"))
-        self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1), \
-                         msg=("more rows displayed than expected"))
-        # Search text box background text is "Search tasks"
-        self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search tasks']"),\
-                        msg=("background text doesn't exist"))
-
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("busybox")
-        self.driver.find_element_by_id("search-button").click()
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step5')
-        self.driver.find_element_by_css_selector("i.icon-remove").click()
-        # Save screen here
-        self.save_screenshot(screenshot_type='selenium', append_name='step5_2')
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("cpu_used").click()
-        self.driver.find_element_by_id("disk_io").click()
-        self.driver.find_element_by_id("recipe_version").click()
-        self.driver.find_element_by_id("time_taken").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # The operation is the same as case901
-        # dict: {lint text name : actual class name}
-        table_head_dict = {'Order':'order', 'Recipe':'recipe_name', 'Task':'task_name', 'Executed':'executed', \
-                           'Outcome':'outcome', 'Cache attempt':'cache_attempt', 'Time (secs)':'time_taken', 'CPU usage':'cpu_used', \
-                           'Disk I/O (ms)':'disk_io'}
-        for key in table_head_dict:
-        # This is tricky here: we are doing so because there may be more than 1
-        # same-name link_text in one page. So we only find element inside the table
-            self.find_element_by_link_text_in_table(self.table_name, key).click()
-            column_list = self.get_table_column_text("class", table_head_dict[key])
-        # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
-        # the point is, after another click, it should be another order
-        # the first case is special:this means every item in column_list is the same, so
-        # after one click, either sequenced or inverted will be fine
-            if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
-                or (not column_list) :
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list), \
-                                msg=("%s column not in any order" % key))
-            elif is_list_inverted(column_list):
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
-            else:
-                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
-        # step 8-10
-        # filter dict: {link text name : filter table name in xpath}
-        filter_dict = {'Executed':'filter_executed', 'Outcome':'filter_outcome', 'Cache attempt':'filter_cache_attempt'}
-        for key in filter_dict:
-            temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
-            # find the filter icon besides it.
-            # And here we must have break (1 sec) to get the popup stuff
-            temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
-            self.browser_delay()
-            avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
-            for number in range(0, len(avail_options)):
-                avail_options[number].click()
-                self.browser_delay()
-                # click "Apply"
-                self.driver.find_element_by_xpath("//*[@id='" + filter_dict[key]  + "']//*[@type='submit']").click()
-                # insert screen capture here
-                self.browser_delay()
-                self.save_screenshot(screenshot_type='selenium', append_name='step8')
-                # after the last option was clicked, we don't need operation below anymore
-                if number < len(avail_options)-1:
-                     try:
-                        temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
-                        temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
-                        avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
-                     except:
-                        print "in exception"
-                        self.find_element_by_text("Show all tasks").click()
-#                        self.driver.find_element_by_xpath("//*[@id='searchform']/button[2]").click()
-                        temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
-                        temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
-                        avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
-                     self.browser_delay()
-        # step 11
-        for item in ['order', 'task_name', 'executed', 'outcome', 'recipe_name', 'recipe_version']:
-            try:
-                self.find_element_by_xpath_in_table(self.table_name, "./tbody/tr[1]/*[@class='" + item + "']/a").click()
-            except NoSuchElementException, e:
-            # let it go...
-                print 'no item in the colum' + item
-            # insert screen shot here
-            self.save_screenshot(screenshot_type='selenium', append_name='step11')
-            self.driver.back()
-        # step 12-14
-        # about test_dict: please refer to testcase 904 requirement step 12-14
-        test_dict = {
-            'Time':{
-                'class':'time_taken',
-                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Time (secs)'],
-                'check_column_list':['cpu_used', 'cache_attempt', 'disk_io', 'order', 'recipe_version']
-            },
-            'CPU usage':{
-                'class':'cpu_used',
-                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'CPU usage'],
-                'check_column_list':['cache_attempt', 'disk_io', 'order', 'recipe_version', 'time_taken']
-            },
-            'Disk I/O':{
-                'class':'disk_io',
-                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Disk I/O (ms)'],
-                'check_column_list':['cpu_used', 'cache_attempt', 'order', 'recipe_version', 'time_taken']
-            }
-        }
-        for key in test_dict:
-            self.find_element_by_partial_link_text_in_table('nav', 'core-image').click()
-            self.find_element_by_link_text_in_table('nav', key).click()
-            head_list = self.get_table_head_text('otable')
-            for item in test_dict[key]['check_head_list']:
-                self.assertTrue(item in head_list, msg=("%s not in head row" % item))
-            column_list = self.get_table_column_text('class', test_dict[key]['class'])
-            self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
-
-            self.driver.find_element_by_id("edit-columns-button").click()
-            for item2 in test_dict[key]['check_column_list']:
-                self.driver.find_element_by_id(item2).click()
-            self.driver.find_element_by_id("edit-columns-button").click()
-            # TBD: save screen here
-
-
-        ##############
-        #  CASE 906  #
-        ##############
-    def test_906(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Packages').click()
-        # find "bash" in first column (Packages)
-        self.driver.find_element_by_xpath("//*[@id='otable']//td[1]//*[text()='bash']").click()
-        # save sceen here to observe...
-        # step 6
-        self.driver.find_element_by_partial_link_text("Generated files").click()
-        head_list = self.get_table_head_text('otable')
-        for item in ['File', 'Size']:
-            self.assertTrue(item in head_list, msg=("%s not in head row" % item))
-        c_list = self.get_table_column_text('class', 'path')
-        self.assertTrue(is_list_sequenced(c_list), msg=("column not in order"))
-        # step 7
-        self.driver.find_element_by_partial_link_text("Runtime dependencies").click()
-        # save sceen here to observe...
-        # note that here table name is not 'otable'
-        head_list = self.get_table_head_text('dependencies')
-        for item in ['Package', 'Version', 'Size']:
-            self.assertTrue(item in head_list, msg=("%s not in head row" % item))
-        c_list = self.get_table_column_text_by_column_number('dependencies', 1)
-        self.assertTrue(is_list_sequenced(c_list), msg=("list not in order"))
-        texts = ['Size', 'License', 'Recipe', 'Recipe version', 'Layer', \
-                     'Layer commit']
-        self.failUnless(self.is_text_present(texts))
-
-
-        ##############
-        #  CASE 910  #
-        ##############
-    def test_910(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        image_type="core-image-minimal"
-        test_package1="busybox"
-        test_package2="lib"
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text(image_type).click()
-        self.driver.find_element_by_link_text("Recipes").click()
-        self.save_screenshot(screenshot_type='selenium', append_name='step3')
-
-        self.table_name = 'otable'
-        # This is how we find the "default" rows-number!
-        rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
-        print rows_displayed
-        self.assertTrue(self.get_table_element(self.table_name, rows_displayed))
-        self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1))
-
-        # Check the default table is sorted by Recipe
-        tasks_column_count = len(self.driver.find_elements_by_xpath("/html/body/div[2]/div/div[2]/div[2]/table/tbody/tr/td[1]"))
-        print tasks_column_count
-        default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
-        #print default_column_list
-
-        self.assertTrue(is_list_sequenced(default_column_list))
-
-        # Search text box background text is "Search recipes"
-        self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
-
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys(test_package1)
-        self.driver.find_element_by_id("search-button").click()
-        # Save screen here
-        self.save_screenshot(screenshot_type='selenium', append_name='step4')
-        self.driver.find_element_by_css_selector("i.icon-remove").click()
-        self.save_screenshot(screenshot_type='selenium', append_name='step4_2')
-
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("depends_on").click()
-        self.driver.find_element_by_id("layer_version__branch").click()
-        self.driver.find_element_by_id("layer_version__layer__commit").click()
-        self.driver.find_element_by_id("depends_by").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-
-        self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
-        # Check the inverted table by Recipe
-        # Recipe doesn't have class name
-        #inverted_tasks_column_count = len(self.driver.find_elements_by_xpath("/html/body/div[2]/div/div[2]/div[2]/table/tbody/tr/td[1]"))
-        #print inverted_tasks_column_count
-        #inverted_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
-        #print inverted_column_list
-
-        #self.driver.find_element_by_partial_link_text("zlib").click()
-        #self.driver.back()
-        #self.assertTrue(is_list_inverted(inverted_column_list))
-        #self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
-
-        table_head_dict = {'Recipe':'recipe__name', 'Recipe file':'recipe_file', 'Section':'recipe_section', \
-                'License':'recipe_license', 'Layer':'layer_version__layer__name', \
-                'Layer branch':'layer_version__branch'}
-        for key in table_head_dict:
-            self.find_element_by_link_text_in_table(self.table_name, key).click()
-            column_list = self.get_table_column_text("class", table_head_dict[key])
-            if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
-                    or (not column_list) :
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
-                self.driver.find_element_by_partial_link_text("acl").click()
-                self.driver.back()
-                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
-                # Search text box background text is "Search recipes"
-                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
-                self.driver.find_element_by_id("search").clear()
-                self.driver.find_element_by_id("search").send_keys(test_package2)
-                self.driver.find_element_by_id("search-button").click()
-                column_search_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_search_list) or is_list_inverted(column_search_list))
-                self.driver.find_element_by_css_selector("i.icon-remove").click()
-            elif is_list_inverted(column_list):
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_list))
-                self.driver.find_element_by_partial_link_text("acl").click()
-                self.driver.back()
-                self.assertTrue(is_list_sequenced(column_list))
-                # Search text box background text is "Search recipes"
-                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
-                self.driver.find_element_by_id("search").clear()
-                self.driver.find_element_by_id("search").send_keys(test_package2)
-                self.driver.find_element_by_id("search-button").click()
-                column_search_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_sequenced(column_search_list))
-                self.driver.find_element_by_css_selector("i.icon-remove").click()
-            else:
-                self.assertTrue(is_list_sequenced(column_list),  msg=("list %s not sequenced" % key))
-                self.find_element_by_link_text_in_table(self.table_name, key).click()
-                column_list = self.get_table_column_text("class", table_head_dict[key])
-                self.assertTrue(is_list_inverted(column_list))
-                try:
-                    self.driver.find_element_by_partial_link_text("acl").click()
-                except:
-                    self.driver.find_element_by_partial_link_text("zlib").click()
-                self.driver.back()
-                self.assertTrue(is_list_inverted(column_list))
-                # Search text box background text is "Search recipes"
-                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
-                self.driver.find_element_by_id("search").clear()
-                self.driver.find_element_by_id("search").send_keys(test_package2)
-                self.driver.find_element_by_id("search-button").click()
-                column_search_list = self.get_table_column_text("class", table_head_dict[key])
-                #print column_search_list
-                self.assertTrue(is_list_inverted(column_search_list))
-                self.driver.find_element_by_css_selector("i.icon-remove").click()
-
-        # Bug 5919
-        for key in table_head_dict:
-            print key
-            self.find_element_by_link_text_in_table(self.table_name, key).click()
-            self.driver.find_element_by_id("edit-columns-button").click()
-            self.driver.find_element_by_id(table_head_dict[key]).click()
-            self.driver.find_element_by_id("edit-columns-button").click()
-            self.browser_delay()
-            # After hide the column, the default table should be sorted by Recipe
-            tasks_column_count = len(self.driver.find_elements_by_partial_link_text("acl"))
-            #print tasks_column_count
-            default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
-            #print default_column_list
-            self.assertTrue(is_list_sequenced(default_column_list))
-
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("recipe_file").click()
-        self.driver.find_element_by_id("recipe_section").click()
-        self.driver.find_element_by_id("recipe_license").click()
-        self.driver.find_element_by_id("layer_version__layer__name").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-
-
-        ##############
-        #  CASE 911  #
-        ##############
-    def test_911(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
-        # step 3-5
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("lib")
-        self.driver.find_element_by_id("search-button").click()
-        # save screen here for observation
-        self.save_screenshot(screenshot_type='selenium', append_name='step5')
-        # step 6
-        self.driver.find_element_by_css_selector("i.icon-remove").click()
-        self.driver.find_element_by_id("search").clear()
-        # we deliberately want "no result" here
-        self.driver.find_element_by_id("search").send_keys("no such input")
-        self.driver.find_element_by_id("search-button").click()
-        try:
-            self.find_element_by_text("Show all recipes").click()
-        except:
-            self.fail(msg='Could not identify blank page elements')
-
-        ##############
-        #  CASE 912  #
-        ##############
-    def test_912(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
-        # step 3
-        head_list = self.get_table_head_text('otable')
-        for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
-            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("depends_on").click()
-        self.driver.find_element_by_id("layer_version__branch").click()
-        self.driver.find_element_by_id("layer_version__layer__commit").click()
-        self.driver.find_element_by_id("depends_by").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # check if columns selected above is shown
-        check_list = ['Dependencies', 'Layer branch', 'Layer commit', 'Reverse dependencies']
-        head_list = self.get_table_head_text('otable')
-        time.sleep(2)
-        print head_list
-        for item in check_list:
-            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
-        # un-check 'em all
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("depends_on").click()
-        self.driver.find_element_by_id("layer_version__branch").click()
-        self.driver.find_element_by_id("layer_version__layer__commit").click()
-        self.driver.find_element_by_id("depends_by").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # don't exist any more
-        head_list = self.get_table_head_text('otable')
-        for item in check_list:
-            self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
-
-
-        ##############
-        #  CASE 913  #
-        ##############
-    def test_913(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
-        # step 3
-        head_list = self.get_table_head_text('otable')
-        for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
-            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
-        # step 4
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # save screen
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step4')
-        self.driver.find_element_by_id("edit-columns-button").click()
-
-
-        ##############
-        #  CASE 914  #
-        ##############
-    def test_914(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        image_type="core-image-minimal"
-        test_package1="busybox"
-        test_package2="gdbm"
-        test_package3="gettext-native"
-        driver = self.driver
-        driver.maximize_window()
-        driver.get(self.base_url)
-        driver.find_element_by_link_text(image_type).click()
-        driver.find_element_by_link_text("Recipes").click()
-        driver.find_element_by_link_text(test_package1).click()
-
-        self.table_name = 'information'
-
-        tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr/td[1]"))
-        tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr[1]/td"))
-        print 'rows: '+str(tasks_row_count)
-        print 'columns: '+str(tasks_column_count)
-
-        Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
-        print ("Tasks_column=", Tasks_column)
-
-        key_tasks=["do_fetch", "do_unpack",  "do_patch", "do_configure", "do_compile", "do_install", "do_package", "do_build"]
-        i = 0
-        while i < len(key_tasks):
-            if key_tasks[i] not in Tasks_column:
-                print ("Error! Missing key task: %s" % key_tasks[i])
-            else:
-                print ("%s is in tasks" % key_tasks[i])
-            i = i + 1
-
-        if Tasks_column.index(key_tasks[0]) != 0:
-            print ("Error! %s is not in the right position" % key_tasks[0])
-        else:
-            print ("%s is in right position" % key_tasks[0])
-
-        if Tasks_column[-1] != key_tasks[-1]:
-            print ("Error! %s is not in the right position" % key_tasks[-1])
-        else:
-            print ("%s is in right position" % key_tasks[-1])
-
-        driver.find_element_by_partial_link_text("Packages (").click()
-        packages_name = driver.find_element_by_partial_link_text("Packages (").text
-        print packages_name
-        packages_num = int(filter(str.isdigit, repr(packages_name)))
-        print packages_num
-
-        #switch the table to show more than 10 rows at a time
-        self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").click()
-        Select(driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select")).select_by_value('150')
-        self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").send_keys(Keys.ENTER)
-
-        packages_row_count = len(driver.find_elements_by_xpath("//*[@id='otable']/tbody/tr/td[1]"))
-        print packages_row_count
-
-        if packages_num != packages_row_count:
-            print ("Error! The packages number is not correct")
-        else:
-            print ("The packages number is correct")
-
-        driver.find_element_by_partial_link_text("Build dependencies (").click()
-        depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
-        print depends_name
-        depends_num = int(filter(str.isdigit, repr(depends_name)))
-        print depends_num
-
-        if depends_num == 0:
-            depends_message = repr(driver.find_element_by_css_selector("div.alert.alert-info").text)
-            print depends_message
-            if depends_message.find("has no build dependencies.") < 0:
-                print ("Error! The message isn't expected.")
-            else:
-                print ("The message is expected")
-        else:
-            depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
-            print depends_row_count
-            if depends_num != depends_row_count:
-                print ("Error! The dependent packages number is not correct")
-            else:
-                print ("The dependent packages number is correct")
-
-        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
-        rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
-        print rdepends_name
-        rdepends_num = int(filter(str.isdigit, repr(rdepends_name)))
-        print rdepends_num
-
-        if rdepends_num == 0:
-            rdepends_message = repr(driver.find_element_by_css_selector("#brought-in-by > div.alert.alert-info").text)
-            print rdepends_message
-            if rdepends_message.find("has no reverse build dependencies.") < 0:
-                print ("Error! The message isn't expected.")
-            else:
-                print ("The message is expected")
-        else:
-            print ("The reverse dependent packages number is correct")
-
-        driver.find_element_by_link_text("Recipes").click()
-        driver.find_element_by_link_text(test_package2).click()
-        driver.find_element_by_partial_link_text("Packages (").click()
-        driver.find_element_by_partial_link_text("Build dependencies (").click()
-        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
-
-
-        driver.find_element_by_link_text("Recipes").click()
-        driver.find_element_by_link_text(test_package3).click()
-
-        native_tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr/td[1]"))
-        native_tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr[1]/td"))
-        print native_tasks_row_count
-        print native_tasks_column_count
-
-        Native_Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
-        print ("Native_Tasks_column=", Native_Tasks_column)
-
-        native_key_tasks=["do_fetch", "do_unpack",  "do_patch", "do_configure", "do_compile", "do_install", "do_build"]
-        i = 0
-        while i < len(native_key_tasks):
-            if native_key_tasks[i] not in Native_Tasks_column:
-                print ("Error! Missing key task: %s" % native_key_tasks[i])
-            else:
-                print ("%s is in tasks" % native_key_tasks[i])
-            i = i + 1
-
-        if Native_Tasks_column.index(native_key_tasks[0]) != 0:
-            print ("Error! %s is not in the right position" % native_key_tasks[0])
-        else:
-            print ("%s is in right position" % native_key_tasks[0])
-
-        if Native_Tasks_column[-1] != native_key_tasks[-1]:
-            print ("Error! %s is not in the right position" % native_key_tasks[-1])
-        else:
-            print ("%s is in right position" % native_key_tasks[-1])
-
-        driver.find_element_by_partial_link_text("Packages (").click()
-        native_packages_name = driver.find_element_by_partial_link_text("Packages (").text
-        print native_packages_name
-        native_packages_num = int(filter(str.isdigit, repr(native_packages_name)))
-        print native_packages_num
-
-        if native_packages_num != 0:
-            print ("Error! Native task shouldn't have any packages.")
-        else:
-            native_package_message = repr(driver.find_element_by_css_selector("#packages-built > div.alert.alert-info").text)
-            print native_package_message
-            if native_package_message.find("does not build any packages.") < 0:
-                print ("Error! The message for native task isn't expected.")
-            else:
-                print ("The message for native task is expected.")
-
-        driver.find_element_by_partial_link_text("Build dependencies (").click()
-        native_depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
-        print native_depends_name
-        native_depends_num = int(filter(str.isdigit, repr(native_depends_name)))
-        print native_depends_num
-
-        native_depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
-        print native_depends_row_count
-
-        if native_depends_num != native_depends_row_count:
-            print ("Error! The dependent packages number is not correct")
-        else:
-            print ("The dependent packages number is correct")
-
-        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
-        native_rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
-        print native_rdepends_name
-        native_rdepends_num = int(filter(str.isdigit, repr(native_rdepends_name)))
-        print native_rdepends_num
-
-        native_rdepends_row_count = len(driver.find_elements_by_xpath("//*[@id='brought-in-by']/table/tbody/tr/td[1]"))
-        print native_rdepends_row_count
-
-        if native_rdepends_num != native_rdepends_row_count:
-            print ("Error! The reverse dependent packages number is not correct")
-        else:
-            print ("The reverse dependent packages number is correct")
-
-        driver.find_element_by_link_text("Recipes").click()
-
-
-        ##############
-        #  CASE 915  #
-        ##############
-    def test_915(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # step 3
-        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
-        self.driver.find_element_by_link_text("BitBake variables").click()
-        # step 4
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("lib")
-        self.driver.find_element_by_id("search-button").click()
-        # save screen to see result
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step4')
-        # step 5
-        self.driver.find_element_by_css_selector("i.icon-remove").click()
-        head_list = self.get_table_head_text('otable')
-        print head_list
-        print len(head_list)
-        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
-                        msg=("head row contents wrong"))
-        # step 8
-        # search other string. and click "Variable" to re-sort, check if table
-        # head is still the same
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("poky")
-        self.driver.find_element_by_id("search-button").click()
-        self.find_element_by_link_text_in_table('otable', 'Variable').click()
-        head_list = self.get_table_head_text('otable')
-        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
-                        msg=("head row contents wrong"))
-        self.find_element_by_link_text_in_table('otable', 'Variable').click()
-        head_list = self.get_table_head_text('otable')
-        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
-                        msg=("head row contents wrong"))
-
-
-        ##############
-        #  CASE 916  #
-        ##############
-    def test_916(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # step 2-3
-        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
-        self.driver.find_element_by_link_text("BitBake variables").click()
-        variable_list = self.get_table_column_text('class', 'variable_name')
-        self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
-        # step 4
-        self.find_element_by_link_text_in_table('otable', 'Variable').click()
-        variable_list = self.get_table_column_text('class', 'variable_name')
-        self.assertTrue(is_list_inverted(variable_list), msg=("list not inverted"))
-        self.find_element_by_link_text_in_table('otable', 'Variable').click()
-        # step 5
-        # searching won't change the sequentiality
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("lib")
-        self.driver.find_element_by_id("search-button").click()
-        variable_list = self.get_table_column_text('class', 'variable_name')
-        self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
-
-
-        ##############
-        #  CASE 923  #
-        ##############
-    def test_923(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # Step 2
-        # default sequence in "Completed on" column is inverted
-        c_list = self.get_table_column_text('class', 'completed_on')
-        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
-        # step 3
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("started_on").click()
-        self.driver.find_element_by_id("time").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        head_list = self.get_table_head_text('otable')
-        for item in ['Outcome', 'Recipe', 'Machine', 'Started on', 'Completed on', 'Failed tasks', 'Errors', 'Warnings', 'Time', "Image files", "Project"]:
-            self.failUnless(item in head_list, msg=item+' is missing from table head.')
-
-
-        ##############
-        #  CASE 924  #
-        ##############
-    def test_924(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # Please refer to case 924 requirement
-        # default sequence in "Completed on" column is inverted
-        c_list = self.get_table_column_text('class', 'completed_on')
-        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
-        # Step 4
-        # click Errors , order in "Completed on" should be disturbed. Then hide
-        # error column to check if order in "Completed on" can be restored
-#THIS TEST IS NO LONGER VALID DUE TO DESIGN CHANGES. LEAVING IN PENDING UPDATES TO DESIGN
-        #self.find_element_by_link_text_in_table('otable', 'Errors').click()
-        #self.driver.find_element_by_id("edit-columns-button").click()
-        #self.driver.find_element_by_id("errors_no").click()
-        #self.driver.find_element_by_id("edit-columns-button").click()
-        # Note: without time.sleep here, there'll be unpredictable error..TBD
-        time.sleep(1)
-        c_list = self.get_table_column_text('class', 'completed_on')
-        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
-
-
-        ##############
-        #  CASE 940  #
-        ##############
-    def test_940(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # Step 2-3
-        self.find_element_by_link_text_in_table('nav', 'Packages').click()
-        check_head_list = ['Package', 'Package version', 'Size', 'Recipe']
-        head_list = self.get_table_head_text('otable')
-        self.assertTrue(head_list == check_head_list, msg=("head row not as expected"))
-        # Step 4
-        # pulldown menu
-        option_ids = ['recipe__layer_version__layer__name', 'recipe__layer_version__branch', \
-                      'recipe__layer_version__layer__commit', 'license', 'recipe__version']
-        self.driver.find_element_by_id("edit-columns-button").click()
-        for item in option_ids:
-            if not self.driver.find_element_by_id(item).is_selected():
-                self.driver.find_element_by_id(item).click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # save screen here to observe that 'Package' and 'Package version' is
-        # not selectable
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step4')
-
-
-        ##############
-        #  CASE 941  #
-        ##############
-    def test_941(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # Step 2-3
-        self.find_element_by_link_text_in_table('nav', 'Packages').click()
-        # column -- Package
-        column_list = self.get_table_column_text_by_column_number('otable', 1)
-        self.assertTrue(is_list_sequenced(column_list), msg=("list not in order"))
-        self.find_element_by_link_text_in_table('otable', 'Size').click()
-
-
-        ##############
-        #  CASE 942  #
-        ##############
-    def test_942(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.driver.find_element_by_link_text("Packages").click()
-        #get initial table header
-        head_list = self.get_table_head_text('otable')
-        #remove the Recipe column from table header
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("recipe__name").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        #get modified table header
-        new_head = self.get_table_head_text('otable')
-        self.assertTrue(head_list > new_head)
-
-        ##############
-        #  CASE 943  #
-        ##############
-    def test_943(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.driver.find_element_by_link_text("Packages").click()
-        #search for the "bash" package -> this should definitely be present
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("bash")
-        self.driver.find_element_by_id("search-button").click()
-        #check for the search result message "XX packages found"
-        self.assertTrue(self.is_text_present("packages found"), msg=("no packages found text"))
-
-
-        ##############
-        #  CASE 944  #
-        ##############
-    def test_944(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # step 1: test Recipes page stuff
-        self.driver.find_element_by_link_text("Recipes").click()
-        # for these 3 items, default status is not-checked
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("layer_version__branch").click()
-        self.driver.find_element_by_id("layer_version__layer__commit").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # otable is the recipes table here
-        otable_head_text = self.get_table_head_text('otable')
-        for item in ["Layer", "Layer branch", "Layer commit"]:
-            self.failIf(item not in otable_head_text, msg=item+' not in table head.')
-        # click the fist recipe, whatever it is
-        self.get_table_element("otable", 1, 1).click()
-        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit", "Recipe file"]), \
-                        msg=("text not in web page"))
-
-        # step 2: test Packages page stuff. almost same as above
-        self.driver.back()
-        self.browser_delay()
-        self.driver.find_element_by_link_text("Packages").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("recipe__layer_version__layer__name").click()
-        self.driver.find_element_by_id("recipe__layer_version__branch").click()
-        self.driver.find_element_by_id("recipe__layer_version__layer__commit").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        otable_head_text = self.get_table_head_text("otable")
-        for item in ["Layer", "Layer branch", "Layer commit"]:
-            self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
-        # click the fist recipe, whatever it is
-        self.get_table_element("otable", 1, 1).click()
-        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
-                        msg=("text not in web page"))
-
-        # step 3: test Packages core-image-minimal(images) stuff. almost same as above. Note when future element-id changes...
-        self.driver.back()
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        self.driver.find_element_by_id("layer_name").click()
-        self.driver.find_element_by_id("layer_branch").click()
-        self.driver.find_element_by_id("layer_commit").click()
-        self.driver.find_element_by_id("edit-columns-button").click()
-        otable_head_text = self.get_table_head_text("otable")
-        for item in ["Layer", "Layer branch", "Layer commit"]:
-            self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
-        # click the fist recipe, whatever it is
-        self.get_table_element("otable", 1, 1).click()
-        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
-                        msg=("text not in web page"))
-
-        # step 4: check Configuration page
-        self.driver.back()
-        self.driver.find_element_by_link_text("Configuration").click()
-        otable_head_text = self.get_table_head_text()
-        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
-                        msg=("text not in web page"))
-
-
-        ##############
-        #  CASE 945  #
-        ##############
-    def test_945(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        for item in ["Packages", "Recipes", "Tasks"]:
-            self.driver.get(self.base_url)
-            self.driver.find_element_by_link_text("core-image-minimal").click()
-            self.driver.find_element_by_link_text(items).click()
-
-            # this may be page specific. If future page content changes, try to replace it with new xpath
-            xpath_showrows = "/html/body/div[4]/div/div/div[2]/div[2]/div[2]/div/div/div[2]/select"
-            xpath_table = "html/body/div[4]/div/div/div[2]/div[2]/table/tbody"#"id=('otable')/tbody"
-            self.driver.find_element_by_xpath(xpath_showrows).click()
-            rows_displayed = int(self.driver.find_element_by_xpath(xpath_showrows + "/option[2]").text)
-
-            # not sure if this is a Selenium Select bug: If page is not refreshed here, "select(by visible text)" operation will go back to 100-row page
-            # Sure we can use driver.get(url) to refresh page, but since page will vary, we use click link text here
-            self.driver.find_element_by_link_text(items).click()
-            Select(self.driver.find_element_by_css_selector("select.pagesize")).select_by_visible_text(str(rows_displayed))
-            self.failUnless(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed) +"]"))
-            self.failIf(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed+1) +"]"))
-
-            # click 1st package, then go back to check if it's still those rows shown.
-            self.driver.find_element_by_xpath(xpath_otable + "/tr[1]/td[1]/a").click()
-            time.sleep(3)
-            self.driver.find_element_by_link_text(item).click()
-            self.assertTrue(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected) +"]"),\
-                            msg=("Row %d should exist" %option_tobeselected))
-            self.assertFalse(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected+1) +"]"),\
-                            msg=("Row %d should not exist" %(option_tobeselected+1)))
-
-
-
-        ##############
-        #  CASE 946  #
-        ##############
-    def test_946(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.driver.find_element_by_link_text("Configuration").click()
-        # step 3-4
-        check_list = ["Summary", "BitBake variables"]
-        for item in check_list:
-            if not self.is_element_present(how=By.LINK_TEXT, what=item):
-                self.log.error("%s not found" %item)
-        if not self.is_text_present(['Layers', 'Layer', 'Layer branch', 'Layer commit']):
-            self.log.error("text not found")
-        # step 5
-        self.driver.find_element_by_link_text("BitBake variables").click()
-        if not self.is_text_present(['Variable', 'Value', 'Set in file', 'Description']):
-            self.log.error("text not found")
-        # This may be unstable because it's page-specific
-        # step 6: this is how we find filter beside "Set in file"
-        temp_element = self.find_element_by_text_in_table('otable', "Set in file")
-        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
-        self.browser_delay()
-        self.driver.find_element_by_xpath("(//input[@name='filter'])[3]").click()
-        btns = self.driver.find_elements_by_css_selector("button.btn.btn-primary")
-        for btn in btns:
-            try:
-                btn.click()
-                break
-            except:
-                pass
-        # save screen here
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step6')
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # save screen here
-        # step 7
-        # we should manually check the step 6-8 result using screenshot
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step7')
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # step 9
-        # click the 1st item, no matter what it is
-        self.driver.find_element_by_xpath("//*[@id='otable']/tbody/tr[1]/td[1]/a").click()
-        # give it 1 sec so the pop-up becomes the "active_element"
-        time.sleep(1)
-        element = self.driver.switch_to.active_element
-        check_list = ['Order', 'Configuration file', 'Operation', 'Line number']
-        for item in check_list:
-            if item not in element.text:
-                self.log.error("%s not found" %item)
-        # any better way to close this pop-up? ... TBD
-        element.find_element_by_class_name("close").click()
-        # step 10 : need to manually check "Yocto Manual" in saved screen
-        self.driver.find_element_by_css_selector("i.icon-share.get-info").click()
-        # save screen here
-        time.sleep(5)
-        self.save_screenshot(screenshot_type='native', append_name='step10')
-
-
-        ##############
-        #  CASE 947  #
-        ##############
-    def test_947(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
-        # step 2
-        self.driver.find_element_by_link_text("BitBake variables").click()
-        # step 3
-        def xpath_option(column_name):
-            # return xpath of options under "Edit columns" button
-            return self.shortest_xpath('id', 'navTab') + self.shortest_xpath('id', 'editcol') \
-                + self.shortest_xpath('id', column_name)
-        self.driver.find_element_by_id('edit-columns-button').click()
-        # by default, option "Description" and "Set in file" were checked
-        self.driver.find_element_by_xpath(xpath_option('description')).click()
-        self.driver.find_element_by_xpath(xpath_option('file')).click()
-        self.driver.find_element_by_id('edit-columns-button').click()
-        check_list = ['Description', 'Set in file']
-        head_list = self.get_table_head_text('otable')
-        for item in check_list:
-            self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
-        # check these 2 options and verify again
-        self.driver.find_element_by_id('edit-columns-button').click()
-        self.driver.find_element_by_xpath(xpath_option('description')).click()
-        self.driver.find_element_by_xpath(xpath_option('file')).click()
-        self.driver.find_element_by_id('edit-columns-button').click()
-        head_list = self.get_table_head_text('otable')
-        for item in check_list:
-            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
-
-
-        ##############
-        #  CASE 948  #
-        ##############
-    def test_948(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
-        self.driver.find_element_by_link_text("BitBake variables").click()
-        #get number of variables visible by default
-        number_before_search = self.driver.find_element_by_class_name('page-header').text
-        # search for a while...
-        self.driver.find_element_by_id("search").clear()
-        self.driver.find_element_by_id("search").send_keys("BB")
-        self.driver.find_element_by_id("search-button").click()
-        #get number of variables visible after search
-        number_after_search = self.driver.find_element_by_class_name('page-header').text
-        self.assertTrue(number_before_search > number_after_search, msg=("items should be less after search"))
-
-
-        ##############
-        #  CASE 949  #
-        ##############
-    def test_949(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        self.find_element_by_link_text_in_table('nav', 'core-image-minimal').click()
-        # step 3
-        try:
-            self.driver.find_element_by_partial_link_text("Packages included")
-            self.driver.find_element_by_partial_link_text("Directory structure")
-        except Exception,e:
-            self.log.error(e)
-            self.assertFalse(True)
-        # step 4
-        head_list = self.get_table_head_text('otable')
-        for item in ['Package', 'Package version', 'Size', 'Dependencies', 'Reverse dependencies', 'Recipe']:
-            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
-        # step 5-6
-        self.driver.find_element_by_id("edit-columns-button").click()
-        selectable_class = 'checkbox'
-        # minimum-table : means unselectable items
-        unselectable_class = 'checkbox muted'
-        selectable_check_list = ['Dependencies', 'Layer', 'Layer branch', 'Layer commit', \
-                                 'License', 'Recipe', 'Recipe version', 'Reverse dependencies', \
-                                 'Size', 'Size over total (%)']
-        unselectable_check_list = ['Package', 'Package version']
-        selectable_list = list()
-        unselectable_list = list()
-        selectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + selectable_class + "']")
-        unselectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + unselectable_class + "']")
-        for element in selectable_elements:
-            selectable_list.append(element.text)
-        for element in unselectable_elements:
-            unselectable_list.append(element.text)
-        # check them
-        for item in selectable_check_list:
-            self.assertTrue(item in selectable_list, msg=("%s not found in dropdown menu" % item))
-        for item in unselectable_check_list:
-            self.assertTrue(item in unselectable_list, msg=("%s not found in dropdown menu" % item))
-        self.driver.find_element_by_id("edit-columns-button").click()
-        # step 7
-        self.driver.find_element_by_partial_link_text("Directory structure").click()
-        head_list = self.get_table_head_text('dirtable')
-        for item in ['Directory / File', 'Symbolic link to', 'Source package', 'Size', 'Permissions', 'Owner', 'Group']:
-            self.assertTrue(item in head_list, msg=("%s not found in Directory structure table head" % item))
-
-        ##############
-        #  CASE 950  #
-        ##############
-    def test_950(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # step3&4: so far we're not sure if there's "successful build" or "failed
-        # build".If either of them doesn't exist, we can still go on other steps
-        check_list = ['Configuration', 'Tasks', 'Recipes', 'Packages', 'Time', 'CPU usage', 'Disk I/O']
-        has_successful_build = 1
-        has_failed_build = 1
-        try:
-            pass_icon = self.driver.find_element_by_xpath("//*[@class='icon-ok-sign success']")
-        except Exception:
-            self.log.info("no successful build exists")
-            has_successful_build = 0
-            pass
-        if has_successful_build:
-            pass_icon.click()
-            # save screen here to check if it matches requirement.
-            self.browser_delay()
-            self.save_screenshot(screenshot_type='selenium', append_name='step3_1')
-            for item in check_list:
-                try:
-                    self.find_element_by_link_text_in_table('nav', item)
-                except Exception:
-                    self.assertFalse(True, msg=("link  %s cannot be found in the page" % item))
-            # step 6
-            check_list_2 = ['Packages included', 'Total package size', \
-                      'License manifest', 'Image files']
-            self.assertTrue(self.is_text_present(check_list_2), msg=("text not in web page"))
-            self.driver.back()
-        try:
-            fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
-        except Exception:
-            has_failed_build = 0
-            self.log.info("no failed build exists")
-            pass
-        if has_failed_build:
-            fail_icon.click()
-            # save screen here to check if it matches requirement.
-            self.browser_delay()
-            self.save_screenshot(screenshot_type='selenium', append_name='step3_2')
-            for item in check_list:
-                try:
-                    self.find_element_by_link_text_in_table('nav', item)
-                except Exception:
-                    self.assertFalse(True, msg=("link  %s cannot be found in the page" % item))
-            # step 7 involved
-            check_list_3 = ['Machine', 'Distro', 'Layers', 'Total number of tasks', 'Tasks executed', \
-                      'Tasks not executed', 'Reuse', 'Recipes built', 'Packages built']
-            self.assertTrue(self.is_text_present(check_list_3), msg=("text not in web page"))
-            self.driver.back()
-
-
-        ##############
-        #  CASE 951  #
-        ##############
-    def test_951(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # currently test case itself isn't responsible for creating "1 successful and
-        # 1 failed build"
-        has_successful_build = 1
-        has_failed_build = 1
-        try:
-            fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
-        except Exception:
-            has_failed_build = 0
-            self.log.info("no failed build exists")
-            pass
-        # if there's failed build, we can proceed
-        if has_failed_build:
-            self.driver.find_element_by_partial_link_text("error").click()
-            self.driver.back()
-        # not sure if there "must be" some warnings, so here save a screen
-        self.browser_delay()
-        self.save_screenshot(screenshot_type='selenium', append_name='step4')
-
-
-        ##############
-        #  CASE 955  #
-        ##############
-    def test_955(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.log.info(" You should manually create all images before test starts!")
-        # So far the case itself is not responsable for creating all sorts of images.
-        # So assuming they are already there
-        # step 2
-        self.driver.find_element_by_link_text("core-image-minimal").click()
-        # save screen here to see the page component
-
-
-        ##############
-        #  CASE 956  #
-        ##############
-    def test_956(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        # step 2-3 need to run manually
-        self.log.info("step 2-3: checking the help message when you hover on help icon of target,\
-                       tasks, recipes, packages need to run manually")
-        self.driver.find_element_by_partial_link_text("Manual").click()
-        if not self.is_text_present("Manual"):
-            self.log.error("please check [Toaster manual] link on page")
-            self.failIf(True)
-
-####################################################################################################
-# Starting backend tests ###########################################################################
-####################################################################################################
-
-        ##############
-        #  CASE 1066 #
-        ##############
-    def test_1066(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select count(name) from orm_project a, auth_user b where a.user_id = b.id and b.username='_anonuser';"
-        cursor.execute(query)
-        data = cursor.fetchone()
-        self.failUnless(data >= 1)
-
-
-        ##############
-        #  CASE 1071 #
-        ##############
-    def test_1071(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_release;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        for i in range(0,4):
-            data[i] = data[i][0]
-        data.sort()
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data = []
-        for i in range (0,4):
-            json_data.append(json_parse['releases'][i]['name'])
-        json_data.sort()
-        print json_data
-        self.failUnless(data == json_data)
-
-        ##############
-        #  CASE 1072 #
-        ##############
-    def test_1072(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select value from orm_toastersetting where name like 'DEFCONF%';"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        for i in range(0,6):
-            data[i] = data[i][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data=json_parse['config']
-        json_data = json_data.values()
-        print json_data
-        self.failUnless(data == json_data)
-
-
-        ##############
-        #  CASE 1074 #
-        ##############
-    def test_1074(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_layersource;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        for i in range(0,3):
-            data[i] = data[i][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data = []
-        for i in range(0,3):
-            json_data.append(json_parse['layersources'][i]['name'])
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-        ##############
-        #  CASE 1075 #
-        ##############
-    def test_1075(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select value from orm_toastersetting where name like 'DEFAULT_RELEASE';"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        data = data[0][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data = json_parse['defaultrelease']
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-        ##############
-        #  CASE 1076 #
-        ##############
-    def test_1076(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-
-        print 'Checking branches for "Local Yocto Project"'
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_branch where layer_source_id=1;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        lenght = len(data)
-        try:
-            for i in range(0,lenght):
-                data[i] = data[i][0]
-        except:
-            pass
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_location = json_parse['layersources'][0]['name']
-        print json_location
-        json_data = json_parse['layersources'][0]['branches']
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-        print 'Checking branches for "OpenEmbedded"'
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_branch where layer_source_id=3;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        lenght = len(data)
-        for i in range(0,lenght):
-            data[i] = data[i][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_location = json_parse['layersources'][1]['name']
-        print json_location
-        json_data = json_parse['layersources'][1]['branches']
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-        print 'Checking branches for "Imported layers"'
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_branch where layer_source_id=2;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        lenght = len(data)
-        for i in range(0,lenght):
-            data[i] = data[i][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_location = json_parse['layersources'][2]['name']
-        print json_location
-        json_data = json_parse['layersources'][2]['branches']
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-
-        ##############
-        #  CASE 1077 #
-        ##############
-    def test_1077(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select name from orm_bitbakeversion;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        for i in range(0,4):
-            data[i] = data[i][0]
-        print data
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data = []
-        for i in range(0,4):
-            json_data.append(json_parse['bitbake'][i]['name'])
-        print json_data
-        self.failUnless(set(data) == set(json_data))
-
-        ##############
-        #  CASE 1083 #
-        ##############
-    def test_1083(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_id("new-project-button").click()
-        self.driver.find_element_by_id("new-project-name").send_keys("new-test-project")
-        self.driver.find_element_by_id("create-project-button").click()
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select count(name) from orm_project where name = 'new-test-project';"
-        cursor.execute(query)
-        data = cursor.fetchone()
-        print 'data: %s' % data
-        self.failUnless(data >= 1)
-
-        ##############
-        #  CASE 1084 #
-        ##############
-    def test_1084(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_id("new-project-button").click()
-        self.driver.find_element_by_id("new-project-name").send_keys("new-default-project")
-        self.driver.find_element_by_id("create-project-button").click()
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select a.name from orm_release a, orm_project b where a.id = b.release_id and b.name = 'new-default-project' limit 1;"
-        cursor.execute(query)
-        db_data = str(cursor.fetchone()[0])
-        json_parse = json.loads(open('toasterconf.json').read())
-        json_data = str(json_parse['defaultrelease'])
-        self.failUnless(db_data == json_data)
-
-        ##############
-        #  CASE 1088 #
-        ##############
-    def test_1088(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
-        self.driver.find_element_by_link_text('new-default-project').click()
-        self.driver.find_element_by_id('project-change-form-toggle').click()
-        self.driver.find_element_by_id('project-name-change-input').clear()
-        self.driver.find_element_by_id('project-name-change-input').send_keys('new-name')
-        self.driver.find_element_by_id('project-name-change-btn').click()
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select count(name) from orm_project where name = 'new-name';"
-        cursor.execute(query)
-        data = cursor.fetchone()[0]
-        self.failUnless(data == 1)
-        #reseting project name
-        self.driver.find_element_by_id('project-change-form-toggle').click()
-        self.driver.find_element_by_id('project-name-change-input').clear()
-        self.driver.find_element_by_id('project-name-change-input').send_keys('new-default-project')
-        self.driver.find_element_by_id('project-name-change-btn').click()
-
-
-        ##############
-        #  CASE 1089 #
-        ##############
-    def test_1089(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
-        self.driver.find_element_by_link_text('new-default-project').click()
-        self.driver.find_element_by_id('change-machine-toggle').click()
-        self.driver.find_element_by_id('machine-change-input').clear()
-        self.driver.find_element_by_id('machine-change-input').send_keys('qemuarm64')
-#        self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
-        self.driver.find_element_by_id('machine-change-btn').click()
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select count(id) from orm_projectvariable where name like 'machine' and value like 'qemuarm64';"
-        cursor.execute(query)
-        data = cursor.fetchone()[0]
-        self.failUnless(data == 1)
-        #resetting machine to default value
-        self.driver.find_element_by_id('change-machine-toggle').click()
-        self.driver.find_element_by_id('machine-change-input').clear()
-        self.driver.find_element_by_id('machine-change-input').send_keys('qemux86')
-        self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
-        self.driver.find_element_by_id('machine-change-btn').click()
-
-        ##############
-        #  CASE 1090 #
-        ##############
-    def test_1090(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select username from auth_user where is_superuser = 1;"
-        cursor.execute(query)
-        data = cursor.fetchall()
-        try:
-            data = data[0][0]
-        except:
-            pass
-        print data
-        self.failUnless(data == 'toaster_admin')
-
-        ##############
-        #  CASE 1091 #
-        ##############
-    def test_1091(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        self.driver.get(self.base_url)
-        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
-        self.driver.find_element_by_link_text('new-default-project').click()
-        self.driver.find_element_by_id('release-change-toggle').click()
-        dropdown = self.driver.find_element_by_css_selector('select')
-        for option in dropdown.find_elements_by_tag_name('option'):
-            if option.text == 'Local Yocto Project':
-                option.click()
-        self.driver.find_element_by_id('change-release-btn').click()
-        #wait for the changes to register in the DB
-        time.sleep(1)
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select count(*) from orm_layer_version a, orm_projectlayer b, orm_project c where a.\"commit\"=\"HEAD\" and a.id = b.layercommit_id and b.project_id=c.id and c.name='new-default-project';"
-        cursor.execute(query)
-        data = cursor.fetchone()[0]
-        #resetting release to default
-        self.driver.find_element_by_id('release-change-toggle').click()
-        dropdown = self.driver.find_element_by_css_selector('select')
-        for option in dropdown.find_elements_by_tag_name('option'):
-            if option.text == 'Yocto Project master':
-                option.click()
-        self.driver.find_element_by_id('change-release-btn').click()
-        #wait for the changes to register in the DB
-        time.sleep(1)
-        self.failUnless(data == 3)
-
-        ##############
-        #  CASE 1092 #
-        ##############
-    def test_1092(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-        self.driver.maximize_window()
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select a.name, a.value from orm_projectvariable a, orm_project b where a.project_id = b.id and b.name = 'new-default-project';"
-        cursor.execute(query)
-        data = dict(cursor.fetchall())
-        print data
-        default_values = {u'IMAGE_INSTALL_append': u'', u'PACKAGE_CLASSES': u'package_rpm', u'MACHINE': u'qemux86', u'SDKMACHINE': u'x86_64', u'DISTRO': u'poky', u'IMAGE_FSTYPES': u'ext3 jffs2 tar.bz2'}
-        self.failUnless(data == default_values)
-
-        ##############
-        #  CASE 1093 #
-        ##############
-    def test_1093(self):
-        self.case_no = self.get_case_number()
-        self.log.info(' CASE %s log: ' % str(self.case_no))
-
-        #get initial values
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
-        cursor.execute(query)
-        data_initial = cursor.fetchall()
-        print data_initial
-
-        self.driver.maximize_window()
-        self.driver.get('localhost:8000')#self.base_url)
-        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
-        self.driver.find_element_by_link_text('new-default-project').click()
-        self.driver.find_element_by_id('release-change-toggle').click()
-        dropdown = self.driver.find_element_by_css_selector('select')
-        for option in dropdown.find_elements_by_tag_name('option'):
-            if option.text == 'Local Yocto Project':
-                option.click()
-        self.driver.find_element_by_id('change-release-btn').click()
-        #wait for the changes to register in the DB
-        time.sleep(1)
-
-        #get changed values
-        con=sqlite.connect('toaster.sqlite')
-        cursor = con.cursor()
-        query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
-        cursor.execute(query)
-        data_changed = cursor.fetchall()
-        print data_changed
-
-        #resetting release to default
-        self.driver.find_element_by_id('release-change-toggle').click()
-        dropdown = self.driver.find_element_by_css_selector('select')
-        for option in dropdown.find_elements_by_tag_name('option'):
-            if option.text == 'Yocto Project master':
-                option.click()
-        self.driver.find_element_by_id('change-release-btn').click()
-        #wait for the changes to register in the DB
-        time.sleep(1)
-        self.failUnless(data_initial != data_changed)
diff --git a/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg b/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg
deleted file mode 100644
index 685a9ee..0000000
--- a/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg
+++ /dev/null
@@ -1,25 +0,0 @@
-# Configuration file for toaster_test
-# Sorted by different host type
-
-# test browser could be: firefox; chrome; ie(still under development)
-# logging_level could be: CRITICAL; ERROR; WARNING; INFO; DEBUG; NOTSET
-
-
-[toaster_test_linux]
-toaster_url = 'http://127.0.0.1:8000'
-test_browser = 'firefox'
-test_cases = [946]
-logging_level = 'INFO'
-
-
-[toaster_test_windows]
-toaster_url = 'http://127.0.0.1:8000'
-test_browser = ['ie', 'firefox', 'chrome']
-test_cases = [901, 902, 903]
-logging_level = 'DEBUG'
-
-[toaster_test_darwin]
-toaster_url = 'http://127.0.0.1:8000'
-test_browser = 'firefox'
-test_cases = [901, 902, 903, 904, 906, 910, 911, 912, 913, 914, 915, 916, 923, 924, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 955, 956]
-logging_level = 'INFO'
diff --git a/lib/toaster/tests/toaster_test.cfg b/lib/toaster/tests/toaster_test.cfg
new file mode 100644
index 0000000..6acd166
--- /dev/null
+++ b/lib/toaster/tests/toaster_test.cfg
@@ -0,0 +1,25 @@
+# Configuration file for toaster_test
+# Sorted by different host type
+
+# test browser could be: firefox; chrome; ie(still under development)
+# logging_level could be: CRITICAL; ERROR; WARNING; INFO; DEBUG; NOTSET
+
+
+[toaster_test_linux]
+toaster_url = 'http://localhost:8000'
+test_browser = 'firefox'
+test_cases = [946]
+logging_level = 'INFO'
+
+
+[toaster_test_windows]
+toaster_url = 'http://localhost:8000'
+test_browser = ['ie', 'firefox', 'chrome']
+test_cases = [901, 902, 903]
+logging_level = 'DEBUG'
+
+[toaster_test_darwin]
+toaster_url = 'http://localhost:8000'
+test_browser = 'firefox'
+test_cases = [901, 902, 903, 904, 906, 910, 911, 912, 913, 914, 915, 916, 923, 924, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 955, 956]
+logging_level = 'INFO'
diff --git a/lib/toaster/tests/ui.py b/lib/toaster/tests/ui.py
new file mode 100644
index 0000000..d8f838a
--- /dev/null
+++ b/lib/toaster/tests/ui.py
@@ -0,0 +1,2376 @@
+#!/usr/bin/python
+# Copyright
+
+# DESCRIPTION
+# This is toaster automation base class and test cases file
+
+# History:
+# 2015.03.09  inital version
+# 2015.03.23  adding toaster_test.cfg, run_toastertest.py so we can run case by case from outside
+
+# Briefs:
+# This file is comprised of 3 parts:
+# I:   common utils like sorting, getting attribute.. etc
+# II:  base class part, which complies with unittest frame work and
+#      contains class selenium-based functions
+# III: test cases
+#      to add new case: just implement new test_xxx() function in class toaster_cases
+
+# NOTES for cases:
+# case 946:
+# step 6 - 8 needs to be observed using screenshots
+# case 956:
+# step 2 - 3 needs to be run manually
+
+import unittest, time, re, sys, getopt, os, logging, string, errno, exceptions
+import shutil, argparse, ConfigParser, platform, json
+from selenium import webdriver
+from selenium.common.exceptions import NoSuchElementException
+from selenium import selenium
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support.ui import Select
+import sqlite3 as sqlite
+
+
+###########################################
+#                                         #
+# PART I: utils stuff                     #
+#                                         #
+###########################################
+
+class Listattr(object):
+    """
+    Set of list attribute. This is used to determine what the list content is.
+    Later on we may add more attributes here.
+    """
+    NULL = "null"
+    NUMBERS = "numbers"
+    STRINGS = "strings"
+    PERCENT = "percentage"
+    SIZE = "size"
+    UNKNOWN = "unknown"
+
+
+def get_log_root_dir():
+    max_depth = 5
+    parent_dir = '../'
+    for number in range(0, max_depth):
+        if os.path.isdir(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log'):
+            log_root_dir = os.path.abspath(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log')
+            break
+
+    if number == (max_depth - 1):
+        print 'No log dir found. Please check'
+        raise Exception
+
+    return log_root_dir
+
+
+def mkdir_p(dir):
+    try:
+        os.makedirs(dir)
+    except OSError as exc:
+        if exc.errno == errno.EEXIST and os.path.isdir(dir):
+            pass
+        else:
+            raise
+
+
+def get_list_attr(testlist):
+    """
+    To determine the list content
+    """
+    if not testlist:
+        return Listattr.NULL
+    listtest = testlist[:]
+    try:
+        listtest.remove('')
+    except ValueError:
+        pass
+    pattern_percent = re.compile(r"^([0-9])+(\.)?([0-9])*%$")
+    pattern_size = re.compile(r"^([0-9])+(\.)?([0-9])*( )*(K)*(M)*(G)*B$")
+    pattern_number = re.compile(r"^([0-9])+(\.)?([0-9])*$")
+    def get_patterned_number(pattern, tlist):
+        count = 0
+        for item in tlist:
+            if re.search(pattern, item):
+                count += 1
+        return count
+    if get_patterned_number(pattern_percent, listtest) == len(listtest):
+        return Listattr.PERCENT
+    elif get_patterned_number(pattern_size, listtest) == len(listtest):
+        return Listattr.SIZE
+    elif get_patterned_number(pattern_number, listtest) == len(listtest):
+        return Listattr.NUMBERS
+    else:
+        return Listattr.STRINGS
+
+
+def is_list_sequenced(testlist):
+    """
+    Function to tell if list is sequenced
+    Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
+    Each has respective way to determine if it's sequenced.
+    """
+    test_list = testlist[:]
+    try:
+        test_list.remove('')
+    except ValueError:
+        pass
+
+    if get_list_attr(testlist) == Listattr.NULL :
+        return True
+
+    elif get_list_attr(testlist) == Listattr.STRINGS :
+        return (sorted(test_list) == test_list)
+
+    elif get_list_attr(testlist) == Listattr.NUMBERS :
+        list_number = []
+        for item in test_list:
+            list_number.append(eval(item))
+        return (sorted(list_number) == list_number)
+
+    elif get_list_attr(testlist) == Listattr.PERCENT :
+        list_number = []
+        for item in test_list:
+            list_number.append(eval(item.strip('%')))
+        return (sorted(list_number) == list_number)
+
+    elif get_list_attr(testlist) == Listattr.SIZE :
+        list_number = []
+        # currently SIZE is splitted by space
+        for item in test_list:
+            if item.split()[1].upper() == "KB":
+                list_number.append(1024 * eval(item.split()[0]))
+            elif item.split()[1].upper() == "MB":
+                list_number.append(1024 * 1024 * eval(item.split()[0]))
+            elif item.split()[1].upper() == "GB":
+                list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
+            else:
+                list_number.append(eval(item.split()[0]))
+        return (sorted(list_number) == list_number)
+
+    else:
+        print 'Unrecognized list type, please check'
+        return False
+
+
+def is_list_inverted(testlist):
+    """
+    Function to tell if list is inverted
+    Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
+    Each has respective way to determine if it's inverted.
+    """
+    test_list = testlist[:]
+    try:
+        test_list.remove('')
+    except ValueError:
+        pass
+
+    if get_list_attr(testlist) == Listattr.NULL :
+        return True
+
+    elif get_list_attr(testlist) == Listattr.STRINGS :
+        return (sorted(test_list, reverse = True) == test_list)
+
+    elif get_list_attr(testlist) == Listattr.NUMBERS :
+        list_number = []
+        for item in test_list:
+            list_number.append(eval(item))
+        return (sorted(list_number, reverse = True) == list_number)
+
+    elif get_list_attr(testlist) == Listattr.PERCENT :
+        list_number = []
+        for item in test_list:
+            list_number.append(eval(item.strip('%')))
+        return (sorted(list_number, reverse = True) == list_number)
+
+    elif get_list_attr(testlist) == Listattr.SIZE :
+        list_number = []
+        # currently SIZE is splitted by space. such as 0 B; 1 KB; 2 MB
+        for item in test_list:
+            if item.split()[1].upper() == "KB":
+                list_number.append(1024 * eval(item.split()[0]))
+            elif item.split()[1].upper() == "MB":
+                list_number.append(1024 * 1024 * eval(item.split()[0]))
+            elif item.split()[1].upper() == "GB":
+                list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
+            else:
+                list_number.append(eval(item.split()[0]))
+        return (sorted(list_number, reverse = True) == list_number)
+
+    else:
+        print 'Unrecognized list type, please check'
+        return False
+
+def replace_file_content(filename, item, option):
+    f = open(filename)
+    lines = f.readlines()
+    f.close()
+    output = open(filename, 'w')
+    for line in lines:
+        if line.startswith(item):
+            output.write(item + " = '" + option + "'\n")
+        else:
+            output.write(line)
+    output.close()
+
+def extract_number_from_string(s):
+    """
+    extract the numbers in a string. return type is 'list'
+    """
+    return re.findall(r'([0-9]+)', s)
+
+# Below is decorator derived from toaster backend test code
+class NoParsingFilter(logging.Filter):
+    def filter(self, record):
+        return record.levelno == 100
+
+def LogResults(original_class):
+    orig_method = original_class.run
+
+    from time import strftime, gmtime
+    caller = 'toaster'
+    timestamp = strftime('%Y%m%d%H%M%S',gmtime())
+    logfile = os.path.join(os.getcwd(),'results-'+caller+'.'+timestamp+'.log')
+    linkfile = os.path.join(os.getcwd(),'results-'+caller+'.log')
+
+    #rewrite the run method of unittest.TestCase to add testcase logging
+    def run(self, result, *args, **kws):
+        orig_method(self, result, *args, **kws)
+        passed = True
+        testMethod = getattr(self, self._testMethodName)
+        #if test case is decorated then use it's number, else use it's name
+        try:
+            test_case = testMethod.test_case
+        except AttributeError:
+            test_case = self._testMethodName
+
+        class_name = str(testMethod.im_class).split("'")[1]
+
+        #create custom logging level for filtering.
+        custom_log_level = 100
+        logging.addLevelName(custom_log_level, 'RESULTS')
+
+        def results(self, message, *args, **kws):
+            if self.isEnabledFor(custom_log_level):
+                self.log(custom_log_level, message, *args, **kws)
+        logging.Logger.results = results
+
+        logging.basicConfig(filename=logfile,
+                            filemode='w',
+                            format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+                            datefmt='%H:%M:%S',
+                            level=custom_log_level)
+        for handler in logging.root.handlers:
+            handler.addFilter(NoParsingFilter())
+        local_log = logging.getLogger(caller)
+
+        #check status of tests and record it
+
+        for (name, msg) in result.errors:
+            if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
+                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]):
+                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]):
+                local_log.results("Testcase "+str(test_case)+": SKIPPED")
+                passed = False
+        if passed:
+            local_log.results("Testcase "+str(test_case)+": PASSED")
+
+        # Create symlink to the current log
+        if os.path.exists(linkfile):
+            os.remove(linkfile)
+        os.symlink(logfile, linkfile)
+
+    original_class.run = run
+
+    return original_class
+
+
+###########################################
+#                                         #
+# PART II: base class                     #
+#                                         #
+###########################################
+
+ at LogResults
+class toaster_cases_base(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.log = cls.logger_create()
+
+    def setUp(self):
+        self.screenshot_sequence = 1
+        self.verificationErrors = []
+        self.accept_next_alert = True
+        self.host_os = platform.system().lower()
+        if os.getenv('TOASTER_SUITE'):
+            self.target_suite = os.getenv('TOASTER_SUITE')
+        else:
+            self.target_suite = self.host_os
+
+        self.parser = ConfigParser.SafeConfigParser()
+        self.parser.read('toaster_test.cfg')
+        self.base_url = eval(self.parser.get('toaster_test_' + self.target_suite, 'toaster_url'))
+
+        # create log dir . Currently , we put log files in log/tmp. After all
+        # test cases are done, move them to log/$datetime dir
+        self.log_tmp_dir = os.path.abspath(sys.path[0]) + os.sep + 'log' + os.sep + 'tmp'
+        try:
+            mkdir_p(self.log_tmp_dir)
+        except OSError :
+            logging.error("%(asctime)s Cannot create tmp dir under log, please check your privilege")
+        # self.log = self.logger_create()
+        # driver setup
+        self.setup_browser()
+
+    @staticmethod
+    def logger_create():
+        log_file = "toaster-auto-" + time.strftime("%Y%m%d%H%M%S") + ".log"
+        if os.path.exists("toaster-auto.log"): os.remove("toaster-auto.log")
+        os.symlink(log_file, "toaster-auto.log")
+
+        log = logging.getLogger("toaster")
+        log.setLevel(logging.DEBUG)
+
+        fh = logging.FileHandler(filename=log_file, mode='w')
+        fh.setLevel(logging.DEBUG)
+
+        ch = logging.StreamHandler(sys.stdout)
+        ch.setLevel(logging.INFO)
+
+        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+        fh.setFormatter(formatter)
+        ch.setFormatter(formatter)
+
+        log.addHandler(fh)
+        log.addHandler(ch)
+
+        return log
+
+
+    def setup_browser(self, *browser_path):
+        self.browser = eval(self.parser.get('toaster_test_' + self.target_suite, 'test_browser'))
+        print self.browser
+        if self.browser == "firefox":
+            driver = webdriver.Firefox()
+        elif self.browser == "chrome":
+            driver = webdriver.Chrome()
+        elif self.browser == "ie":
+            driver = webdriver.Ie()
+        else:
+            driver = None
+            print "unrecognized browser type, please check"
+        self.driver = driver
+        self.driver.implicitly_wait(30)
+        return self.driver
+
+
+    def save_screenshot(self,  **log_args):
+        """
+        This function is used to save screen either by os interface or selenium interface.
+        How to use:
+        self.save_screenshot(screenshot_type = 'native'/'selenium', log_sub_dir = 'xxx',
+                             append_name = 'stepx')
+        where native means screenshot func provided by OS,
+        selenium means screenshot func provided by selenium webdriver
+        """
+        types = [log_args.get('screenshot_type')]
+        # when no screenshot_type is specified
+        if types == [None]:
+            types = ['native', 'selenium']
+        # normally append_name is used to specify which step..
+        add_name = log_args.get('append_name')
+        if not add_name:
+            add_name = '-'
+        # normally there's no need to specify sub_dir
+        sub_dir = log_args.get('log_sub_dir')
+        if not sub_dir:
+            # use casexxx as sub_dir name
+            sub_dir = 'case' + str(self.case_no)
+        for item in types:
+            log_dir = self.log_tmp_dir + os.sep + sub_dir
+            mkdir_p(log_dir)
+            log_path = log_dir + os.sep +  self.browser + '-' +\
+                    item + '-' + add_name + '-' + str(self.screenshot_sequence) + '.png'
+            if item == 'native':
+                if self.host_os == "linux":
+                    os.system("scrot " + log_path)
+                elif self.host_os=="darwin":
+                    os.system("screencapture -x " + log_path)
+            elif item == 'selenium':
+                self.driver.get_screenshot_as_file(log_path)
+            self.screenshot_sequence += 1
+
+    def browser_delay(self):
+        """
+        currently this is a workaround for some chrome test.
+        Sometimes we need a delay to accomplish some operation.
+        But for firefox, mostly we don't need this.
+        To be discussed
+        """
+        if self.browser == "chrome":
+            time.sleep(1)
+        return
+
+
+# these functions are not contained in WebDriver class..
+    def find_element_by_text(self, string):
+        return self.driver.find_element_by_xpath("//*[text()='" + string + "']")
+
+
+    def find_elements_by_text(self, string):
+        return self.driver.find_elements_by_xpath("//*[text()='" + string + "']")
+
+
+    def find_element_by_text_in_table(self, table_id, text_string):
+        """
+        This is used to search some certain 'text' in certain table
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element = table_element.find_element_by_xpath("//*[text()='" + text_string + "']")
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+        return element
+
+
+    def find_element_by_link_text_in_table(self, table_id, link_text):
+        """
+        Assume there're multiple suitable "find_element_by_link_text".
+        In this circumstance we need to specify "table".
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element = table_element.find_element_by_link_text(link_text)
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+        return element
+
+
+    def find_elements_by_link_text_in_table(self, table_id, link_text):
+        """
+        Search link-text in certain table. This helps to narrow down search area.
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element_list = table_element.find_elements_by_link_text(link_text)
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+        return element_list
+
+
+    def find_element_by_partial_link_text_in_table(self, table_id, link_text):
+        """
+        Search element by partial link text in certain table.
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element = table_element.find_element_by_partial_link_text(link_text)
+            return element
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+
+
+    def find_elements_by_partial_link_text_in_table(self, table_id, link_text):
+        """
+        Assume there're multiple suitable "find_partial_element_by_link_text".
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element_list = table_element.find_elements_by_partial_link_text(link_text)
+            return element_list
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+
+
+    def find_element_by_xpath_in_table(self, table_id, xpath):
+        """
+        This helps to narrow down search area. Especially useful when dealing with pop-up form.
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element = table_element.find_element_by_xpath(xpath)
+        except NoSuchElementException, e:
+            print 'no element found'
+            raise
+        return element
+
+
+    def find_elements_by_xpath_in_table(self, table_id, xpath):
+        """
+        This helps to narrow down search area. Especially useful when dealing with pop-up form.
+        """
+        try:
+            table_element = self.get_table_element(table_id)
+            element_list = table_element.find_elements_by_xpath(xpath)
+        except NoSuchElementException, e:
+            print 'no elements found'
+            raise
+        return element_list
+
+
+    def shortest_xpath(self, pname, pvalue):
+        return "//*[@" + pname + "='" + pvalue + "']"
+
+
+#usually elements in the same column are with same class name. for instance: class="outcome" .TBD
+    def get_table_column_text(self, attr_name, attr_value):
+        c_xpath = self.shortest_xpath(attr_name, attr_value)
+        elements = self.driver.find_elements_by_xpath(c_xpath)
+        c_list = []
+        for element in elements:
+            c_list.append(element.text)
+        return c_list
+
+
+    def get_table_column_text_by_column_number(self, table_id, column_number):
+        c_xpath = "//*[@id='" + table_id + "']//td[" + str(column_number) + "]"
+        elements = self.driver.find_elements_by_xpath(c_xpath)
+        c_list = []
+        for element in elements:
+            c_list.append(element.text)
+        return c_list
+
+
+    def get_table_head_text(self, *table_id):
+#now table_id is a tuple...
+        if table_id:
+            thead_xpath = "//*[@id='" + table_id[0] + "']//thead//th[text()]"
+            elements = self.driver.find_elements_by_xpath(thead_xpath)
+            c_list = []
+            for element in elements:
+                if element.text:
+                    c_list.append(element.text)
+            return c_list
+#default table on page
+        else:
+            return self.driver.find_element_by_xpath("//*/table/thead").text
+
+
+
+    def get_table_element(self, table_id, *coordinate):
+        if len(coordinate) == 0:
+#return whole-table element
+            element_xpath = "//*[@id='" + table_id + "']"
+            try:
+                element = self.driver.find_element_by_xpath(element_xpath)
+            except NoSuchElementException, e:
+                raise
+            return element
+        row = coordinate[0]
+
+        if len(coordinate) == 1:
+#return whole-row element
+            element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]"
+            try:
+                element = self.driver.find_element_by_xpath(element_xpath)
+            except NoSuchElementException, e:
+                return False
+            return element
+#now we are looking for an element with specified X and Y
+        column = coordinate[1]
+
+        element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]/td[" + str(column) + "]"
+        try:
+            element = self.driver.find_element_by_xpath(element_xpath)
+        except NoSuchElementException, e:
+            return False
+        return element
+
+
+    def get_table_data(self, table_id, row_count, column_count):
+        row = 1
+        Lists = []
+        while row <= row_count:
+            column = 1
+            row_content=[]
+            while column <= column_count:
+                s= "//*[@id='" + table_id + "']/tbody/tr[" + str(row) +"]/td[" + str(column) + "]"
+                v = self.driver.find_element_by_xpath(s).text
+                row_content.append(v)
+                column = column + 1
+                print("row_content=",row_content)
+            Lists.extend(row_content)
+            print Lists[row-1][0]
+            row = row + 1
+        return Lists
+
+    # The is_xxx_present functions only returns True/False
+    # All the log work is done in test procedure, so we can easily trace back
+    # using logging
+    def is_text_present (self, patterns):
+        for pattern in patterns:
+            if str(pattern) not in self.driver.page_source:
+                print 'Text "'+pattern+'" is missing'
+                return False
+        return True
+
+
+    def is_element_present(self, how, what):
+        try:
+            self.driver.find_element(how, what)
+        except NoSuchElementException, e:
+            print 'Could not find element '+str(what)+' by ' + str(how)
+            return False
+        return True
+
+
+    def is_alert_present(self):
+        try: self.driver.switch_to_alert()
+        except NoAlertPresentException, e: return False
+        return True
+
+
+    def close_alert_and_get_its_text(self):
+        try:
+            alert = self.driver.switch_to_alert()
+            alert_text = alert.text
+            if self.accept_next_alert:
+                alert.accept()
+            else:
+                alert.dismiss()
+            return alert_text
+        finally: self.accept_next_alert = True
+
+
+    def get_case_number(self):
+        """
+        what case are we running now
+        """
+        funcname = sys._getframe(1).f_code.co_name
+        caseno_str = funcname.strip('test_')
+        try:
+            caseno = int(caseno_str)
+        except ValueError:
+            print "get case number error! please check if func name is test_xxx"
+            return False
+        return caseno
+
+
+    def tearDown(self):
+        self.log.info(' END: CASE %s log \n\n' % str(self.case_no))
+        self.driver.quit()
+        self.assertEqual([], self.verificationErrors)
+
+
+###################################################################
+#                                                                 #
+# PART III: test cases                                            #
+# please refer to                                                 #
+# https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=xxx  #
+#                                                                 #
+###################################################################
+
+# Note: to comply with the unittest framework, we call these test_xxx functions
+# from run_toastercases.py to avoid calling setUp() and tearDown() multiple times
+
+
+class toaster_cases(toaster_cases_base):
+        ##############
+        #  CASE 901  #
+        ##############
+    def test_901(self):
+        # the reason why get_case_number is not in setUp function is that
+        # otherwise it returns "setUp" instead of "test_xxx"
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # open all columns
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # adding explicitly wait for chromedriver..-_-
+        self.browser_delay()
+        self.driver.find_element_by_id("started_on").click()
+        self.browser_delay()
+        self.driver.find_element_by_id("time").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # dict: {lint text name : actual class name}
+        table_head_dict = {'Outcome':'outcome', 'Recipe':'target', 'Machine':'machine', 'Started on':'started_on', 'Completed on':'completed_on', \
+                'Errors':'errors_no', 'Warnings':'warnings_no', 'Time':'time'}
+        for key in table_head_dict:
+            try:
+                self.driver.find_element_by_link_text(key).click()
+            except Exception, e:
+                self.log.error("%s cannot be found on page" % key)
+                raise
+            column_list = self.get_table_column_text("class", table_head_dict[key])
+            # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
+            # the point is, after another click, it should be another order
+            if is_list_inverted(column_list):
+                self.driver.find_element_by_link_text(key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
+            else:
+                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not sequenced" % key))
+                self.driver.find_element_by_link_text(key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
+        self.log.info("case passed")
+
+
+        ##############
+        #  CASE 902  #
+        ##############
+    def test_902(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # Could add more test patterns here in the future. Also, could search some items other than target column in future..
+        patterns = ["minimal", "sato"]
+        for pattern in patterns:
+            ori_target_column_texts = self.get_table_column_text("class", "target")
+            print ori_target_column_texts
+            self.driver.find_element_by_id("search").clear()
+            self.driver.find_element_by_id("search").send_keys(pattern)
+            self.driver.find_element_by_id("search-button").click()
+            new_target_column_texts = self.get_table_column_text("class", "target")
+            # if nothing found, we still count it as "pass"
+            if new_target_column_texts:
+                for text in new_target_column_texts:
+                    self.assertTrue(text.find(pattern), msg=("%s item doesn't exist " % pattern))
+            self.driver.find_element_by_css_selector("i.icon-remove").click()
+            target_column_texts = self.get_table_column_text("class", "target")
+            self.assertTrue(ori_target_column_texts == target_column_texts, msg=("builds changed after operations"))
+
+
+        ##############
+        #  CASE 903  #
+        ##############
+    def test_903(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # when opening a new page, "started_on" is not displayed by default
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # currently all the delay are for chrome driver -_-
+        self.browser_delay()
+        self.driver.find_element_by_id("started_on").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # step 4
+        items = ["Outcome", "Completed on", "Started on"]
+        for item in items:
+            try:
+                temp_element = self.find_element_by_text_in_table('otable', item)
+                # this is how we find "filter icon" in the same level as temp_element(where "a" means clickable, "i" means icon)
+                self.assertTrue(temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']"))
+            except Exception,e:
+                self.assertFalse(True, msg=(" %s cannot be found! %s" % (item, e)))
+                raise
+        # step 5-6
+        temp_element = self.find_element_by_link_text_in_table('otable', 'Outcome')
+        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
+        self.browser_delay()
+        # the 2nd option, whatever it is
+        self.driver.find_element_by_xpath("(//input[@name='filter'])[2]").click()
+        # click "Apply" button
+        self.driver.find_element_by_xpath("//*[@id='filter_outcome']//*[text()='Apply']").click()
+        # save screen here
+        time.sleep(1)
+        self.save_screenshot(screenshot_type='selenium', append_name='step5')
+        temp_element = self.find_element_by_link_text_in_table('otable', 'Completed on')
+        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
+        self.browser_delay()
+        self.driver.find_element_by_xpath("//*[@id='filter_completed_on']//*[text()='Apply']").click()
+        # save screen here to compare to previous one
+        # please note that for chrome driver, need a little break before saving
+        # screen here -_-
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step6')
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("core-image")
+        self.driver.find_element_by_id("search-button").click()
+
+
+        ##############
+        #  CASE 904  #
+        ##############
+    def test_904(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_partial_link_text("core-image").click()
+        self.driver.find_element_by_link_text("Tasks").click()
+        self.table_name = 'otable'
+        # This is how we find the "default" rows-number!
+        rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
+        print rows_displayed
+        self.assertTrue(self.get_table_element(self.table_name, rows_displayed), msg=("not enough rows displayed"))
+        self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1), \
+                         msg=("more rows displayed than expected"))
+        # Search text box background text is "Search tasks"
+        self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search tasks']"),\
+                        msg=("background text doesn't exist"))
+
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("busybox")
+        self.driver.find_element_by_id("search-button").click()
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step5')
+        self.driver.find_element_by_css_selector("i.icon-remove").click()
+        # Save screen here
+        self.save_screenshot(screenshot_type='selenium', append_name='step5_2')
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("cpu_used").click()
+        self.driver.find_element_by_id("disk_io").click()
+        self.driver.find_element_by_id("recipe_version").click()
+        self.driver.find_element_by_id("time_taken").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # The operation is the same as case901
+        # dict: {lint text name : actual class name}
+        table_head_dict = {'Order':'order', 'Recipe':'recipe_name', 'Task':'task_name', 'Executed':'executed', \
+                           'Outcome':'outcome', 'Cache attempt':'cache_attempt', 'Time (secs)':'time_taken', 'CPU usage':'cpu_used', \
+                           'Disk I/O (ms)':'disk_io'}
+        for key in table_head_dict:
+        # This is tricky here: we are doing so because there may be more than 1
+        # same-name link_text in one page. So we only find element inside the table
+            self.find_element_by_link_text_in_table(self.table_name, key).click()
+            column_list = self.get_table_column_text("class", table_head_dict[key])
+        # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
+        # the point is, after another click, it should be another order
+        # the first case is special:this means every item in column_list is the same, so
+        # after one click, either sequenced or inverted will be fine
+            if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
+                or (not column_list) :
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list), \
+                                msg=("%s column not in any order" % key))
+            elif is_list_inverted(column_list):
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
+            else:
+                self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
+        # step 8-10
+        # filter dict: {link text name : filter table name in xpath}
+        filter_dict = {'Executed':'filter_executed', 'Outcome':'filter_outcome', 'Cache attempt':'filter_cache_attempt'}
+        for key in filter_dict:
+            temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
+            # find the filter icon besides it.
+            # And here we must have break (1 sec) to get the popup stuff
+            temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
+            self.browser_delay()
+            avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
+            for number in range(0, len(avail_options)):
+                avail_options[number].click()
+                self.browser_delay()
+                # click "Apply"
+                self.driver.find_element_by_xpath("//*[@id='" + filter_dict[key]  + "']//*[@type='submit']").click()
+                # insert screen capture here
+                self.browser_delay()
+                self.save_screenshot(screenshot_type='selenium', append_name='step8')
+                # after the last option was clicked, we don't need operation below anymore
+                if number < len(avail_options)-1:
+                     try:
+                        temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
+                        temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
+                        avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
+                     except:
+                        print "in exception"
+                        self.find_element_by_text("Show all tasks").click()
+#                        self.driver.find_element_by_xpath("//*[@id='searchform']/button[2]").click()
+                        temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
+                        temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
+                        avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
+                     self.browser_delay()
+        # step 11
+        for item in ['order', 'task_name', 'executed', 'outcome', 'recipe_name', 'recipe_version']:
+            try:
+                self.find_element_by_xpath_in_table(self.table_name, "./tbody/tr[1]/*[@class='" + item + "']/a").click()
+            except NoSuchElementException, e:
+            # let it go...
+                print 'no item in the colum' + item
+            # insert screen shot here
+            self.save_screenshot(screenshot_type='selenium', append_name='step11')
+            self.driver.back()
+        # step 12-14
+        # about test_dict: please refer to testcase 904 requirement step 12-14
+        test_dict = {
+            'Time':{
+                'class':'time_taken',
+                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Time (secs)'],
+                'check_column_list':['cpu_used', 'cache_attempt', 'disk_io', 'order', 'recipe_version']
+            },
+            'CPU usage':{
+                'class':'cpu_used',
+                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'CPU usage'],
+                'check_column_list':['cache_attempt', 'disk_io', 'order', 'recipe_version', 'time_taken']
+            },
+            'Disk I/O':{
+                'class':'disk_io',
+                'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Disk I/O (ms)'],
+                'check_column_list':['cpu_used', 'cache_attempt', 'order', 'recipe_version', 'time_taken']
+            }
+        }
+        for key in test_dict:
+            self.find_element_by_partial_link_text_in_table('nav', 'core-image').click()
+            self.find_element_by_link_text_in_table('nav', key).click()
+            head_list = self.get_table_head_text('otable')
+            for item in test_dict[key]['check_head_list']:
+                self.assertTrue(item in head_list, msg=("%s not in head row" % item))
+            column_list = self.get_table_column_text('class', test_dict[key]['class'])
+            self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
+
+            self.driver.find_element_by_id("edit-columns-button").click()
+            for item2 in test_dict[key]['check_column_list']:
+                self.driver.find_element_by_id(item2).click()
+            self.driver.find_element_by_id("edit-columns-button").click()
+            # TBD: save screen here
+
+
+        ##############
+        #  CASE 906  #
+        ##############
+    def test_906(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Packages').click()
+        # find "bash" in first column (Packages)
+        self.driver.find_element_by_xpath("//*[@id='otable']//td[1]//*[text()='bash']").click()
+        # save sceen here to observe...
+        # step 6
+        self.driver.find_element_by_partial_link_text("Generated files").click()
+        head_list = self.get_table_head_text('otable')
+        for item in ['File', 'Size']:
+            self.assertTrue(item in head_list, msg=("%s not in head row" % item))
+        c_list = self.get_table_column_text('class', 'path')
+        self.assertTrue(is_list_sequenced(c_list), msg=("column not in order"))
+        # step 7
+        self.driver.find_element_by_partial_link_text("Runtime dependencies").click()
+        # save sceen here to observe...
+        # note that here table name is not 'otable'
+        head_list = self.get_table_head_text('dependencies')
+        for item in ['Package', 'Version', 'Size']:
+            self.assertTrue(item in head_list, msg=("%s not in head row" % item))
+        c_list = self.get_table_column_text_by_column_number('dependencies', 1)
+        self.assertTrue(is_list_sequenced(c_list), msg=("list not in order"))
+        texts = ['Size', 'License', 'Recipe', 'Recipe version', 'Layer', \
+                     'Layer commit']
+        self.failUnless(self.is_text_present(texts))
+
+
+        ##############
+        #  CASE 910  #
+        ##############
+    def test_910(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        image_type="core-image-minimal"
+        test_package1="busybox"
+        test_package2="lib"
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text(image_type).click()
+        self.driver.find_element_by_link_text("Recipes").click()
+        self.save_screenshot(screenshot_type='selenium', append_name='step3')
+
+        self.table_name = 'otable'
+        # This is how we find the "default" rows-number!
+        rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
+        print rows_displayed
+        self.assertTrue(self.get_table_element(self.table_name, rows_displayed))
+        self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1))
+
+        # Check the default table is sorted by Recipe
+        tasks_column_count = len(self.driver.find_elements_by_xpath("/html/body/div[2]/div/div[2]/div[2]/table/tbody/tr/td[1]"))
+        print tasks_column_count
+        default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
+        #print default_column_list
+
+        self.assertTrue(is_list_sequenced(default_column_list))
+
+        # Search text box background text is "Search recipes"
+        self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
+
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys(test_package1)
+        self.driver.find_element_by_id("search-button").click()
+        # Save screen here
+        self.save_screenshot(screenshot_type='selenium', append_name='step4')
+        self.driver.find_element_by_css_selector("i.icon-remove").click()
+        self.save_screenshot(screenshot_type='selenium', append_name='step4_2')
+
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("depends_on").click()
+        self.driver.find_element_by_id("layer_version__branch").click()
+        self.driver.find_element_by_id("layer_version__layer__commit").click()
+        self.driver.find_element_by_id("depends_by").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+
+        self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
+        # Check the inverted table by Recipe
+        # Recipe doesn't have class name
+        #inverted_tasks_column_count = len(self.driver.find_elements_by_xpath("/html/body/div[2]/div/div[2]/div[2]/table/tbody/tr/td[1]"))
+        #print inverted_tasks_column_count
+        #inverted_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
+        #print inverted_column_list
+
+        #self.driver.find_element_by_partial_link_text("zlib").click()
+        #self.driver.back()
+        #self.assertTrue(is_list_inverted(inverted_column_list))
+        #self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
+
+        table_head_dict = {'Recipe':'recipe__name', 'Recipe file':'recipe_file', 'Section':'recipe_section', \
+                'License':'recipe_license', 'Layer':'layer_version__layer__name', \
+                'Layer branch':'layer_version__branch'}
+        for key in table_head_dict:
+            self.find_element_by_link_text_in_table(self.table_name, key).click()
+            column_list = self.get_table_column_text("class", table_head_dict[key])
+            if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
+                    or (not column_list) :
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
+                self.driver.find_element_by_partial_link_text("acl").click()
+                self.driver.back()
+                self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
+                # Search text box background text is "Search recipes"
+                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
+                self.driver.find_element_by_id("search").clear()
+                self.driver.find_element_by_id("search").send_keys(test_package2)
+                self.driver.find_element_by_id("search-button").click()
+                column_search_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_search_list) or is_list_inverted(column_search_list))
+                self.driver.find_element_by_css_selector("i.icon-remove").click()
+            elif is_list_inverted(column_list):
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_list))
+                self.driver.find_element_by_partial_link_text("acl").click()
+                self.driver.back()
+                self.assertTrue(is_list_sequenced(column_list))
+                # Search text box background text is "Search recipes"
+                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
+                self.driver.find_element_by_id("search").clear()
+                self.driver.find_element_by_id("search").send_keys(test_package2)
+                self.driver.find_element_by_id("search-button").click()
+                column_search_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_sequenced(column_search_list))
+                self.driver.find_element_by_css_selector("i.icon-remove").click()
+            else:
+                self.assertTrue(is_list_sequenced(column_list),  msg=("list %s not sequenced" % key))
+                self.find_element_by_link_text_in_table(self.table_name, key).click()
+                column_list = self.get_table_column_text("class", table_head_dict[key])
+                self.assertTrue(is_list_inverted(column_list))
+                try:
+                    self.driver.find_element_by_partial_link_text("acl").click()
+                except:
+                    self.driver.find_element_by_partial_link_text("zlib").click()
+                self.driver.back()
+                self.assertTrue(is_list_inverted(column_list))
+                # Search text box background text is "Search recipes"
+                self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
+                self.driver.find_element_by_id("search").clear()
+                self.driver.find_element_by_id("search").send_keys(test_package2)
+                self.driver.find_element_by_id("search-button").click()
+                column_search_list = self.get_table_column_text("class", table_head_dict[key])
+                #print column_search_list
+                self.assertTrue(is_list_inverted(column_search_list))
+                self.driver.find_element_by_css_selector("i.icon-remove").click()
+
+        # Bug 5919
+        for key in table_head_dict:
+            print key
+            self.find_element_by_link_text_in_table(self.table_name, key).click()
+            self.driver.find_element_by_id("edit-columns-button").click()
+            self.driver.find_element_by_id(table_head_dict[key]).click()
+            self.driver.find_element_by_id("edit-columns-button").click()
+            self.browser_delay()
+            # After hide the column, the default table should be sorted by Recipe
+            tasks_column_count = len(self.driver.find_elements_by_partial_link_text("acl"))
+            #print tasks_column_count
+            default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
+            #print default_column_list
+            self.assertTrue(is_list_sequenced(default_column_list))
+
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("recipe_file").click()
+        self.driver.find_element_by_id("recipe_section").click()
+        self.driver.find_element_by_id("recipe_license").click()
+        self.driver.find_element_by_id("layer_version__layer__name").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+
+
+        ##############
+        #  CASE 911  #
+        ##############
+    def test_911(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
+        # step 3-5
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("lib")
+        self.driver.find_element_by_id("search-button").click()
+        # save screen here for observation
+        self.save_screenshot(screenshot_type='selenium', append_name='step5')
+        # step 6
+        self.driver.find_element_by_css_selector("i.icon-remove").click()
+        self.driver.find_element_by_id("search").clear()
+        # we deliberately want "no result" here
+        self.driver.find_element_by_id("search").send_keys("no such input")
+        self.driver.find_element_by_id("search-button").click()
+        try:
+            self.find_element_by_text("Show all recipes").click()
+        except:
+            self.fail(msg='Could not identify blank page elements')
+
+        ##############
+        #  CASE 912  #
+        ##############
+    def test_912(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
+        # step 3
+        head_list = self.get_table_head_text('otable')
+        for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
+            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("depends_on").click()
+        self.driver.find_element_by_id("layer_version__branch").click()
+        self.driver.find_element_by_id("layer_version__layer__commit").click()
+        self.driver.find_element_by_id("depends_by").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # check if columns selected above is shown
+        check_list = ['Dependencies', 'Layer branch', 'Layer commit', 'Reverse dependencies']
+        head_list = self.get_table_head_text('otable')
+        time.sleep(2)
+        print head_list
+        for item in check_list:
+            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
+        # un-check 'em all
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("depends_on").click()
+        self.driver.find_element_by_id("layer_version__branch").click()
+        self.driver.find_element_by_id("layer_version__layer__commit").click()
+        self.driver.find_element_by_id("depends_by").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # don't exist any more
+        head_list = self.get_table_head_text('otable')
+        for item in check_list:
+            self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
+
+
+        ##############
+        #  CASE 913  #
+        ##############
+    def test_913(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Recipes').click()
+        # step 3
+        head_list = self.get_table_head_text('otable')
+        for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
+            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
+        # step 4
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # save screen
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step4')
+        self.driver.find_element_by_id("edit-columns-button").click()
+
+
+        ##############
+        #  CASE 914  #
+        ##############
+    def test_914(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        image_type="core-image-minimal"
+        test_package1="busybox"
+        test_package2="gdbm"
+        test_package3="gettext-native"
+        driver = self.driver
+        driver.maximize_window()
+        driver.get(self.base_url)
+        driver.find_element_by_link_text(image_type).click()
+        driver.find_element_by_link_text("Recipes").click()
+        driver.find_element_by_link_text(test_package1).click()
+
+        self.table_name = 'information'
+
+        tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr/td[1]"))
+        tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr[1]/td"))
+        print 'rows: '+str(tasks_row_count)
+        print 'columns: '+str(tasks_column_count)
+
+        Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
+        print ("Tasks_column=", Tasks_column)
+
+        key_tasks=["do_fetch", "do_unpack",  "do_patch", "do_configure", "do_compile", "do_install", "do_package", "do_build"]
+        i = 0
+        while i < len(key_tasks):
+            if key_tasks[i] not in Tasks_column:
+                print ("Error! Missing key task: %s" % key_tasks[i])
+            else:
+                print ("%s is in tasks" % key_tasks[i])
+            i = i + 1
+
+        if Tasks_column.index(key_tasks[0]) != 0:
+            print ("Error! %s is not in the right position" % key_tasks[0])
+        else:
+            print ("%s is in right position" % key_tasks[0])
+
+        if Tasks_column[-1] != key_tasks[-1]:
+            print ("Error! %s is not in the right position" % key_tasks[-1])
+        else:
+            print ("%s is in right position" % key_tasks[-1])
+
+        driver.find_element_by_partial_link_text("Packages (").click()
+        packages_name = driver.find_element_by_partial_link_text("Packages (").text
+        print packages_name
+        packages_num = int(filter(str.isdigit, repr(packages_name)))
+        print packages_num
+
+        #switch the table to show more than 10 rows at a time
+        self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").click()
+        Select(driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select")).select_by_value('150')
+        self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").send_keys(Keys.ENTER)
+
+        packages_row_count = len(driver.find_elements_by_xpath("//*[@id='otable']/tbody/tr/td[1]"))
+        print packages_row_count
+
+        if packages_num != packages_row_count:
+            print ("Error! The packages number is not correct")
+        else:
+            print ("The packages number is correct")
+
+        driver.find_element_by_partial_link_text("Build dependencies (").click()
+        depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
+        print depends_name
+        depends_num = int(filter(str.isdigit, repr(depends_name)))
+        print depends_num
+
+        if depends_num == 0:
+            depends_message = repr(driver.find_element_by_css_selector("div.alert.alert-info").text)
+            print depends_message
+            if depends_message.find("has no build dependencies.") < 0:
+                print ("Error! The message isn't expected.")
+            else:
+                print ("The message is expected")
+        else:
+            depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
+            print depends_row_count
+            if depends_num != depends_row_count:
+                print ("Error! The dependent packages number is not correct")
+            else:
+                print ("The dependent packages number is correct")
+
+        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
+        rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
+        print rdepends_name
+        rdepends_num = int(filter(str.isdigit, repr(rdepends_name)))
+        print rdepends_num
+
+        if rdepends_num == 0:
+            rdepends_message = repr(driver.find_element_by_css_selector("#brought-in-by > div.alert.alert-info").text)
+            print rdepends_message
+            if rdepends_message.find("has no reverse build dependencies.") < 0:
+                print ("Error! The message isn't expected.")
+            else:
+                print ("The message is expected")
+        else:
+            print ("The reverse dependent packages number is correct")
+
+        driver.find_element_by_link_text("Recipes").click()
+        driver.find_element_by_link_text(test_package2).click()
+        driver.find_element_by_partial_link_text("Packages (").click()
+        driver.find_element_by_partial_link_text("Build dependencies (").click()
+        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
+
+
+        driver.find_element_by_link_text("Recipes").click()
+        driver.find_element_by_link_text(test_package3).click()
+
+        native_tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr/td[1]"))
+        native_tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr[1]/td"))
+        print native_tasks_row_count
+        print native_tasks_column_count
+
+        Native_Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
+        print ("Native_Tasks_column=", Native_Tasks_column)
+
+        native_key_tasks=["do_fetch", "do_unpack",  "do_patch", "do_configure", "do_compile", "do_install", "do_build"]
+        i = 0
+        while i < len(native_key_tasks):
+            if native_key_tasks[i] not in Native_Tasks_column:
+                print ("Error! Missing key task: %s" % native_key_tasks[i])
+            else:
+                print ("%s is in tasks" % native_key_tasks[i])
+            i = i + 1
+
+        if Native_Tasks_column.index(native_key_tasks[0]) != 0:
+            print ("Error! %s is not in the right position" % native_key_tasks[0])
+        else:
+            print ("%s is in right position" % native_key_tasks[0])
+
+        if Native_Tasks_column[-1] != native_key_tasks[-1]:
+            print ("Error! %s is not in the right position" % native_key_tasks[-1])
+        else:
+            print ("%s is in right position" % native_key_tasks[-1])
+
+        driver.find_element_by_partial_link_text("Packages (").click()
+        native_packages_name = driver.find_element_by_partial_link_text("Packages (").text
+        print native_packages_name
+        native_packages_num = int(filter(str.isdigit, repr(native_packages_name)))
+        print native_packages_num
+
+        if native_packages_num != 0:
+            print ("Error! Native task shouldn't have any packages.")
+        else:
+            native_package_message = repr(driver.find_element_by_css_selector("#packages-built > div.alert.alert-info").text)
+            print native_package_message
+            if native_package_message.find("does not build any packages.") < 0:
+                print ("Error! The message for native task isn't expected.")
+            else:
+                print ("The message for native task is expected.")
+
+        driver.find_element_by_partial_link_text("Build dependencies (").click()
+        native_depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
+        print native_depends_name
+        native_depends_num = int(filter(str.isdigit, repr(native_depends_name)))
+        print native_depends_num
+
+        native_depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
+        print native_depends_row_count
+
+        if native_depends_num != native_depends_row_count:
+            print ("Error! The dependent packages number is not correct")
+        else:
+            print ("The dependent packages number is correct")
+
+        driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
+        native_rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
+        print native_rdepends_name
+        native_rdepends_num = int(filter(str.isdigit, repr(native_rdepends_name)))
+        print native_rdepends_num
+
+        native_rdepends_row_count = len(driver.find_elements_by_xpath("//*[@id='brought-in-by']/table/tbody/tr/td[1]"))
+        print native_rdepends_row_count
+
+        if native_rdepends_num != native_rdepends_row_count:
+            print ("Error! The reverse dependent packages number is not correct")
+        else:
+            print ("The reverse dependent packages number is correct")
+
+        driver.find_element_by_link_text("Recipes").click()
+
+
+        ##############
+        #  CASE 915  #
+        ##############
+    def test_915(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # step 3
+        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
+        self.driver.find_element_by_link_text("BitBake variables").click()
+        # step 4
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("lib")
+        self.driver.find_element_by_id("search-button").click()
+        # save screen to see result
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step4')
+        # step 5
+        self.driver.find_element_by_css_selector("i.icon-remove").click()
+        head_list = self.get_table_head_text('otable')
+        print head_list
+        print len(head_list)
+        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
+                        msg=("head row contents wrong"))
+        # step 8
+        # search other string. and click "Variable" to re-sort, check if table
+        # head is still the same
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("poky")
+        self.driver.find_element_by_id("search-button").click()
+        self.find_element_by_link_text_in_table('otable', 'Variable').click()
+        head_list = self.get_table_head_text('otable')
+        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
+                        msg=("head row contents wrong"))
+        self.find_element_by_link_text_in_table('otable', 'Variable').click()
+        head_list = self.get_table_head_text('otable')
+        self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
+                        msg=("head row contents wrong"))
+
+
+        ##############
+        #  CASE 916  #
+        ##############
+    def test_916(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # step 2-3
+        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
+        self.driver.find_element_by_link_text("BitBake variables").click()
+        variable_list = self.get_table_column_text('class', 'variable_name')
+        self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
+        # step 4
+        self.find_element_by_link_text_in_table('otable', 'Variable').click()
+        variable_list = self.get_table_column_text('class', 'variable_name')
+        self.assertTrue(is_list_inverted(variable_list), msg=("list not inverted"))
+        self.find_element_by_link_text_in_table('otable', 'Variable').click()
+        # step 5
+        # searching won't change the sequentiality
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("lib")
+        self.driver.find_element_by_id("search-button").click()
+        variable_list = self.get_table_column_text('class', 'variable_name')
+        self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
+
+
+        ##############
+        #  CASE 923  #
+        ##############
+    def test_923(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # Step 2
+        # default sequence in "Completed on" column is inverted
+        c_list = self.get_table_column_text('class', 'completed_on')
+        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
+        # step 3
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("started_on").click()
+        self.driver.find_element_by_id("time").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        head_list = self.get_table_head_text('otable')
+        for item in ['Outcome', 'Recipe', 'Machine', 'Started on', 'Completed on', 'Failed tasks', 'Errors', 'Warnings', 'Time', "Image files", "Project"]:
+            self.failUnless(item in head_list, msg=item+' is missing from table head.')
+
+
+        ##############
+        #  CASE 924  #
+        ##############
+    def test_924(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # Please refer to case 924 requirement
+        # default sequence in "Completed on" column is inverted
+        c_list = self.get_table_column_text('class', 'completed_on')
+        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
+        # Step 4
+        # click Errors , order in "Completed on" should be disturbed. Then hide
+        # error column to check if order in "Completed on" can be restored
+#THIS TEST IS NO LONGER VALID DUE TO DESIGN CHANGES. LEAVING IN PENDING UPDATES TO DESIGN
+        #self.find_element_by_link_text_in_table('otable', 'Errors').click()
+        #self.driver.find_element_by_id("edit-columns-button").click()
+        #self.driver.find_element_by_id("errors_no").click()
+        #self.driver.find_element_by_id("edit-columns-button").click()
+        # Note: without time.sleep here, there'll be unpredictable error..TBD
+        time.sleep(1)
+        c_list = self.get_table_column_text('class', 'completed_on')
+        self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
+
+
+        ##############
+        #  CASE 940  #
+        ##############
+    def test_940(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # Step 2-3
+        self.find_element_by_link_text_in_table('nav', 'Packages').click()
+        check_head_list = ['Package', 'Package version', 'Size', 'Recipe']
+        head_list = self.get_table_head_text('otable')
+        self.assertTrue(head_list == check_head_list, msg=("head row not as expected"))
+        # Step 4
+        # pulldown menu
+        option_ids = ['recipe__layer_version__layer__name', 'recipe__layer_version__branch', \
+                      'recipe__layer_version__layer__commit', 'license', 'recipe__version']
+        self.driver.find_element_by_id("edit-columns-button").click()
+        for item in option_ids:
+            if not self.driver.find_element_by_id(item).is_selected():
+                self.driver.find_element_by_id(item).click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # save screen here to observe that 'Package' and 'Package version' is
+        # not selectable
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step4')
+
+
+        ##############
+        #  CASE 941  #
+        ##############
+    def test_941(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # Step 2-3
+        self.find_element_by_link_text_in_table('nav', 'Packages').click()
+        # column -- Package
+        column_list = self.get_table_column_text_by_column_number('otable', 1)
+        self.assertTrue(is_list_sequenced(column_list), msg=("list not in order"))
+        self.find_element_by_link_text_in_table('otable', 'Size').click()
+
+
+        ##############
+        #  CASE 942  #
+        ##############
+    def test_942(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.driver.find_element_by_link_text("Packages").click()
+        #get initial table header
+        head_list = self.get_table_head_text('otable')
+        #remove the Recipe column from table header
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("recipe__name").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        #get modified table header
+        new_head = self.get_table_head_text('otable')
+        self.assertTrue(head_list > new_head)
+
+        ##############
+        #  CASE 943  #
+        ##############
+    def test_943(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.driver.find_element_by_link_text("Packages").click()
+        #search for the "bash" package -> this should definitely be present
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("bash")
+        self.driver.find_element_by_id("search-button").click()
+        #check for the search result message "XX packages found"
+        self.assertTrue(self.is_text_present("packages found"), msg=("no packages found text"))
+
+
+        ##############
+        #  CASE 944  #
+        ##############
+    def test_944(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # step 1: test Recipes page stuff
+        self.driver.find_element_by_link_text("Recipes").click()
+        # for these 3 items, default status is not-checked
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("layer_version__branch").click()
+        self.driver.find_element_by_id("layer_version__layer__commit").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # otable is the recipes table here
+        otable_head_text = self.get_table_head_text('otable')
+        for item in ["Layer", "Layer branch", "Layer commit"]:
+            self.failIf(item not in otable_head_text, msg=item+' not in table head.')
+        # click the fist recipe, whatever it is
+        self.get_table_element("otable", 1, 1).click()
+        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit", "Recipe file"]), \
+                        msg=("text not in web page"))
+
+        # step 2: test Packages page stuff. almost same as above
+        self.driver.back()
+        self.browser_delay()
+        self.driver.find_element_by_link_text("Packages").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("recipe__layer_version__layer__name").click()
+        self.driver.find_element_by_id("recipe__layer_version__branch").click()
+        self.driver.find_element_by_id("recipe__layer_version__layer__commit").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        otable_head_text = self.get_table_head_text("otable")
+        for item in ["Layer", "Layer branch", "Layer commit"]:
+            self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
+        # click the fist recipe, whatever it is
+        self.get_table_element("otable", 1, 1).click()
+        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
+                        msg=("text not in web page"))
+
+        # step 3: test Packages core-image-minimal(images) stuff. almost same as above. Note when future element-id changes...
+        self.driver.back()
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        self.driver.find_element_by_id("layer_name").click()
+        self.driver.find_element_by_id("layer_branch").click()
+        self.driver.find_element_by_id("layer_commit").click()
+        self.driver.find_element_by_id("edit-columns-button").click()
+        otable_head_text = self.get_table_head_text("otable")
+        for item in ["Layer", "Layer branch", "Layer commit"]:
+            self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
+        # click the fist recipe, whatever it is
+        self.get_table_element("otable", 1, 1).click()
+        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
+                        msg=("text not in web page"))
+
+        # step 4: check Configuration page
+        self.driver.back()
+        self.driver.find_element_by_link_text("Configuration").click()
+        otable_head_text = self.get_table_head_text()
+        self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
+                        msg=("text not in web page"))
+
+
+        ##############
+        #  CASE 945  #
+        ##############
+    def test_945(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        for item in ["Packages", "Recipes", "Tasks"]:
+            self.driver.get(self.base_url)
+            self.driver.find_element_by_link_text("core-image-minimal").click()
+            self.driver.find_element_by_link_text(items).click()
+
+            # this may be page specific. If future page content changes, try to replace it with new xpath
+            xpath_showrows = "/html/body/div[4]/div/div/div[2]/div[2]/div[2]/div/div/div[2]/select"
+            xpath_table = "html/body/div[4]/div/div/div[2]/div[2]/table/tbody"#"id=('otable')/tbody"
+            self.driver.find_element_by_xpath(xpath_showrows).click()
+            rows_displayed = int(self.driver.find_element_by_xpath(xpath_showrows + "/option[2]").text)
+
+            # not sure if this is a Selenium Select bug: If page is not refreshed here, "select(by visible text)" operation will go back to 100-row page
+            # Sure we can use driver.get(url) to refresh page, but since page will vary, we use click link text here
+            self.driver.find_element_by_link_text(items).click()
+            Select(self.driver.find_element_by_css_selector("select.pagesize")).select_by_visible_text(str(rows_displayed))
+            self.failUnless(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed) +"]"))
+            self.failIf(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed+1) +"]"))
+
+            # click 1st package, then go back to check if it's still those rows shown.
+            self.driver.find_element_by_xpath(xpath_otable + "/tr[1]/td[1]/a").click()
+            time.sleep(3)
+            self.driver.find_element_by_link_text(item).click()
+            self.assertTrue(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected) +"]"),\
+                            msg=("Row %d should exist" %option_tobeselected))
+            self.assertFalse(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected+1) +"]"),\
+                            msg=("Row %d should not exist" %(option_tobeselected+1)))
+
+
+
+        ##############
+        #  CASE 946  #
+        ##############
+    def test_946(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.driver.find_element_by_link_text("Configuration").click()
+        # step 3-4
+        check_list = ["Summary", "BitBake variables"]
+        for item in check_list:
+            if not self.is_element_present(how=By.LINK_TEXT, what=item):
+                self.log.error("%s not found" %item)
+        if not self.is_text_present(['Layers', 'Layer', 'Layer branch', 'Layer commit']):
+            self.log.error("text not found")
+        # step 5
+        self.driver.find_element_by_link_text("BitBake variables").click()
+        if not self.is_text_present(['Variable', 'Value', 'Set in file', 'Description']):
+            self.log.error("text not found")
+        # This may be unstable because it's page-specific
+        # step 6: this is how we find filter beside "Set in file"
+        temp_element = self.find_element_by_text_in_table('otable', "Set in file")
+        temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
+        self.browser_delay()
+        self.driver.find_element_by_xpath("(//input[@name='filter'])[3]").click()
+        btns = self.driver.find_elements_by_css_selector("button.btn.btn-primary")
+        for btn in btns:
+            try:
+                btn.click()
+                break
+            except:
+                pass
+        # save screen here
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step6')
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # save screen here
+        # step 7
+        # we should manually check the step 6-8 result using screenshot
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step7')
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # step 9
+        # click the 1st item, no matter what it is
+        self.driver.find_element_by_xpath("//*[@id='otable']/tbody/tr[1]/td[1]/a").click()
+        # give it 1 sec so the pop-up becomes the "active_element"
+        time.sleep(1)
+        element = self.driver.switch_to.active_element
+        check_list = ['Order', 'Configuration file', 'Operation', 'Line number']
+        for item in check_list:
+            if item not in element.text:
+                self.log.error("%s not found" %item)
+        # any better way to close this pop-up? ... TBD
+        element.find_element_by_class_name("close").click()
+        # step 10 : need to manually check "Yocto Manual" in saved screen
+        self.driver.find_element_by_css_selector("i.icon-share.get-info").click()
+        # save screen here
+        time.sleep(5)
+        self.save_screenshot(screenshot_type='native', append_name='step10')
+
+
+        ##############
+        #  CASE 947  #
+        ##############
+    def test_947(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
+        # step 2
+        self.driver.find_element_by_link_text("BitBake variables").click()
+        # step 3
+        def xpath_option(column_name):
+            # return xpath of options under "Edit columns" button
+            return self.shortest_xpath('id', 'navTab') + self.shortest_xpath('id', 'editcol') \
+                + self.shortest_xpath('id', column_name)
+        self.driver.find_element_by_id('edit-columns-button').click()
+        # by default, option "Description" and "Set in file" were checked
+        self.driver.find_element_by_xpath(xpath_option('description')).click()
+        self.driver.find_element_by_xpath(xpath_option('file')).click()
+        self.driver.find_element_by_id('edit-columns-button').click()
+        check_list = ['Description', 'Set in file']
+        head_list = self.get_table_head_text('otable')
+        for item in check_list:
+            self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
+        # check these 2 options and verify again
+        self.driver.find_element_by_id('edit-columns-button').click()
+        self.driver.find_element_by_xpath(xpath_option('description')).click()
+        self.driver.find_element_by_xpath(xpath_option('file')).click()
+        self.driver.find_element_by_id('edit-columns-button').click()
+        head_list = self.get_table_head_text('otable')
+        for item in check_list:
+            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
+
+
+        ##############
+        #  CASE 948  #
+        ##############
+    def test_948(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'Configuration').click()
+        self.driver.find_element_by_link_text("BitBake variables").click()
+        #get number of variables visible by default
+        number_before_search = self.driver.find_element_by_class_name('page-header').text
+        # search for a while...
+        self.driver.find_element_by_id("search").clear()
+        self.driver.find_element_by_id("search").send_keys("BB")
+        self.driver.find_element_by_id("search-button").click()
+        #get number of variables visible after search
+        number_after_search = self.driver.find_element_by_class_name('page-header').text
+        self.assertTrue(number_before_search > number_after_search, msg=("items should be less after search"))
+
+
+        ##############
+        #  CASE 949  #
+        ##############
+    def test_949(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        self.find_element_by_link_text_in_table('nav', 'core-image-minimal').click()
+        # step 3
+        try:
+            self.driver.find_element_by_partial_link_text("Packages included")
+            self.driver.find_element_by_partial_link_text("Directory structure")
+        except Exception,e:
+            self.log.error(e)
+            self.assertFalse(True)
+        # step 4
+        head_list = self.get_table_head_text('otable')
+        for item in ['Package', 'Package version', 'Size', 'Dependencies', 'Reverse dependencies', 'Recipe']:
+            self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
+        # step 5-6
+        self.driver.find_element_by_id("edit-columns-button").click()
+        selectable_class = 'checkbox'
+        # minimum-table : means unselectable items
+        unselectable_class = 'checkbox muted'
+        selectable_check_list = ['Dependencies', 'Layer', 'Layer branch', 'Layer commit', \
+                                 'License', 'Recipe', 'Recipe version', 'Reverse dependencies', \
+                                 'Size', 'Size over total (%)']
+        unselectable_check_list = ['Package', 'Package version']
+        selectable_list = list()
+        unselectable_list = list()
+        selectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + selectable_class + "']")
+        unselectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + unselectable_class + "']")
+        for element in selectable_elements:
+            selectable_list.append(element.text)
+        for element in unselectable_elements:
+            unselectable_list.append(element.text)
+        # check them
+        for item in selectable_check_list:
+            self.assertTrue(item in selectable_list, msg=("%s not found in dropdown menu" % item))
+        for item in unselectable_check_list:
+            self.assertTrue(item in unselectable_list, msg=("%s not found in dropdown menu" % item))
+        self.driver.find_element_by_id("edit-columns-button").click()
+        # step 7
+        self.driver.find_element_by_partial_link_text("Directory structure").click()
+        head_list = self.get_table_head_text('dirtable')
+        for item in ['Directory / File', 'Symbolic link to', 'Source package', 'Size', 'Permissions', 'Owner', 'Group']:
+            self.assertTrue(item in head_list, msg=("%s not found in Directory structure table head" % item))
+
+        ##############
+        #  CASE 950  #
+        ##############
+    def test_950(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # step3&4: so far we're not sure if there's "successful build" or "failed
+        # build".If either of them doesn't exist, we can still go on other steps
+        check_list = ['Configuration', 'Tasks', 'Recipes', 'Packages', 'Time', 'CPU usage', 'Disk I/O']
+        has_successful_build = 1
+        has_failed_build = 1
+        try:
+            pass_icon = self.driver.find_element_by_xpath("//*[@class='icon-ok-sign success']")
+        except Exception:
+            self.log.info("no successful build exists")
+            has_successful_build = 0
+            pass
+        if has_successful_build:
+            pass_icon.click()
+            # save screen here to check if it matches requirement.
+            self.browser_delay()
+            self.save_screenshot(screenshot_type='selenium', append_name='step3_1')
+            for item in check_list:
+                try:
+                    self.find_element_by_link_text_in_table('nav', item)
+                except Exception:
+                    self.assertFalse(True, msg=("link  %s cannot be found in the page" % item))
+            # step 6
+            check_list_2 = ['Packages included', 'Total package size', \
+                      'License manifest', 'Image files']
+            self.assertTrue(self.is_text_present(check_list_2), msg=("text not in web page"))
+            self.driver.back()
+        try:
+            fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
+        except Exception:
+            has_failed_build = 0
+            self.log.info("no failed build exists")
+            pass
+        if has_failed_build:
+            fail_icon.click()
+            # save screen here to check if it matches requirement.
+            self.browser_delay()
+            self.save_screenshot(screenshot_type='selenium', append_name='step3_2')
+            for item in check_list:
+                try:
+                    self.find_element_by_link_text_in_table('nav', item)
+                except Exception:
+                    self.assertFalse(True, msg=("link  %s cannot be found in the page" % item))
+            # step 7 involved
+            check_list_3 = ['Machine', 'Distro', 'Layers', 'Total number of tasks', 'Tasks executed', \
+                      'Tasks not executed', 'Reuse', 'Recipes built', 'Packages built']
+            self.assertTrue(self.is_text_present(check_list_3), msg=("text not in web page"))
+            self.driver.back()
+
+
+        ##############
+        #  CASE 951  #
+        ##############
+    def test_951(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # currently test case itself isn't responsible for creating "1 successful and
+        # 1 failed build"
+        has_successful_build = 1
+        has_failed_build = 1
+        try:
+            fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
+        except Exception:
+            has_failed_build = 0
+            self.log.info("no failed build exists")
+            pass
+        # if there's failed build, we can proceed
+        if has_failed_build:
+            self.driver.find_element_by_partial_link_text("error").click()
+            self.driver.back()
+        # not sure if there "must be" some warnings, so here save a screen
+        self.browser_delay()
+        self.save_screenshot(screenshot_type='selenium', append_name='step4')
+
+
+        ##############
+        #  CASE 955  #
+        ##############
+    def test_955(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.log.info(" You should manually create all images before test starts!")
+        # So far the case itself is not responsable for creating all sorts of images.
+        # So assuming they are already there
+        # step 2
+        self.driver.find_element_by_link_text("core-image-minimal").click()
+        # save screen here to see the page component
+
+
+        ##############
+        #  CASE 956  #
+        ##############
+    def test_956(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        # step 2-3 need to run manually
+        self.log.info("step 2-3: checking the help message when you hover on help icon of target,\
+                       tasks, recipes, packages need to run manually")
+        self.driver.find_element_by_partial_link_text("Manual").click()
+        if not self.is_text_present("Manual"):
+            self.log.error("please check [Toaster manual] link on page")
+            self.failIf(True)
+
+####################################################################################################
+# Starting backend tests ###########################################################################
+####################################################################################################
+
+        ##############
+        #  CASE 1066 #
+        ##############
+    def test_1066(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select count(name) from orm_project a, auth_user b where a.user_id = b.id and b.username='_anonuser';"
+        cursor.execute(query)
+        data = cursor.fetchone()
+        self.failUnless(data >= 1)
+
+
+        ##############
+        #  CASE 1071 #
+        ##############
+    def test_1071(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_release;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        for i in range(0,4):
+            data[i] = data[i][0]
+        data.sort()
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data = []
+        for i in range (0,4):
+            json_data.append(json_parse['releases'][i]['name'])
+        json_data.sort()
+        print json_data
+        self.failUnless(data == json_data)
+
+        ##############
+        #  CASE 1072 #
+        ##############
+    def test_1072(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select value from orm_toastersetting where name like 'DEFCONF%';"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        for i in range(0,6):
+            data[i] = data[i][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data=json_parse['config']
+        json_data = json_data.values()
+        print json_data
+        self.failUnless(data == json_data)
+
+
+        ##############
+        #  CASE 1074 #
+        ##############
+    def test_1074(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_layersource;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        for i in range(0,3):
+            data[i] = data[i][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data = []
+        for i in range(0,3):
+            json_data.append(json_parse['layersources'][i]['name'])
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+        ##############
+        #  CASE 1075 #
+        ##############
+    def test_1075(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select value from orm_toastersetting where name like 'DEFAULT_RELEASE';"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        data = data[0][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data = json_parse['defaultrelease']
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+        ##############
+        #  CASE 1076 #
+        ##############
+    def test_1076(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+
+        print 'Checking branches for "Local Yocto Project"'
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_branch where layer_source_id=1;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        lenght = len(data)
+        try:
+            for i in range(0,lenght):
+                data[i] = data[i][0]
+        except:
+            pass
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_location = json_parse['layersources'][0]['name']
+        print json_location
+        json_data = json_parse['layersources'][0]['branches']
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+        print 'Checking branches for "OpenEmbedded"'
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_branch where layer_source_id=3;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        lenght = len(data)
+        for i in range(0,lenght):
+            data[i] = data[i][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_location = json_parse['layersources'][1]['name']
+        print json_location
+        json_data = json_parse['layersources'][1]['branches']
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+        print 'Checking branches for "Imported layers"'
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_branch where layer_source_id=2;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        lenght = len(data)
+        for i in range(0,lenght):
+            data[i] = data[i][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_location = json_parse['layersources'][2]['name']
+        print json_location
+        json_data = json_parse['layersources'][2]['branches']
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+
+        ##############
+        #  CASE 1077 #
+        ##############
+    def test_1077(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select name from orm_bitbakeversion;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        for i in range(0,4):
+            data[i] = data[i][0]
+        print data
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data = []
+        for i in range(0,4):
+            json_data.append(json_parse['bitbake'][i]['name'])
+        print json_data
+        self.failUnless(set(data) == set(json_data))
+
+        ##############
+        #  CASE 1083 #
+        ##############
+    def test_1083(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_id("new-project-button").click()
+        self.driver.find_element_by_id("new-project-name").send_keys("new-test-project")
+        self.driver.find_element_by_id("create-project-button").click()
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select count(name) from orm_project where name = 'new-test-project';"
+        cursor.execute(query)
+        data = cursor.fetchone()
+        print 'data: %s' % data
+        self.failUnless(data >= 1)
+
+        ##############
+        #  CASE 1084 #
+        ##############
+    def test_1084(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_id("new-project-button").click()
+        self.driver.find_element_by_id("new-project-name").send_keys("new-default-project")
+        self.driver.find_element_by_id("create-project-button").click()
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select a.name from orm_release a, orm_project b where a.id = b.release_id and b.name = 'new-default-project' limit 1;"
+        cursor.execute(query)
+        db_data = str(cursor.fetchone()[0])
+        json_parse = json.loads(open('toasterconf.json').read())
+        json_data = str(json_parse['defaultrelease'])
+        self.failUnless(db_data == json_data)
+
+        ##############
+        #  CASE 1088 #
+        ##############
+    def test_1088(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
+        self.driver.find_element_by_link_text('new-default-project').click()
+        self.driver.find_element_by_id('project-change-form-toggle').click()
+        self.driver.find_element_by_id('project-name-change-input').clear()
+        self.driver.find_element_by_id('project-name-change-input').send_keys('new-name')
+        self.driver.find_element_by_id('project-name-change-btn').click()
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select count(name) from orm_project where name = 'new-name';"
+        cursor.execute(query)
+        data = cursor.fetchone()[0]
+        self.failUnless(data == 1)
+        #reseting project name
+        self.driver.find_element_by_id('project-change-form-toggle').click()
+        self.driver.find_element_by_id('project-name-change-input').clear()
+        self.driver.find_element_by_id('project-name-change-input').send_keys('new-default-project')
+        self.driver.find_element_by_id('project-name-change-btn').click()
+
+
+        ##############
+        #  CASE 1089 #
+        ##############
+    def test_1089(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
+        self.driver.find_element_by_link_text('new-default-project').click()
+        self.driver.find_element_by_id('change-machine-toggle').click()
+        self.driver.find_element_by_id('machine-change-input').clear()
+        self.driver.find_element_by_id('machine-change-input').send_keys('qemuarm64')
+#        self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
+        self.driver.find_element_by_id('machine-change-btn').click()
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select count(id) from orm_projectvariable where name like 'machine' and value like 'qemuarm64';"
+        cursor.execute(query)
+        data = cursor.fetchone()[0]
+        self.failUnless(data == 1)
+        #resetting machine to default value
+        self.driver.find_element_by_id('change-machine-toggle').click()
+        self.driver.find_element_by_id('machine-change-input').clear()
+        self.driver.find_element_by_id('machine-change-input').send_keys('qemux86')
+        self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
+        self.driver.find_element_by_id('machine-change-btn').click()
+
+        ##############
+        #  CASE 1090 #
+        ##############
+    def test_1090(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select username from auth_user where is_superuser = 1;"
+        cursor.execute(query)
+        data = cursor.fetchall()
+        try:
+            data = data[0][0]
+        except:
+            pass
+        print data
+        self.failUnless(data == 'toaster_admin')
+
+        ##############
+        #  CASE 1091 #
+        ##############
+    def test_1091(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        self.driver.get(self.base_url)
+        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
+        self.driver.find_element_by_link_text('new-default-project').click()
+        self.driver.find_element_by_id('release-change-toggle').click()
+        dropdown = self.driver.find_element_by_css_selector('select')
+        for option in dropdown.find_elements_by_tag_name('option'):
+            if option.text == 'Local Yocto Project':
+                option.click()
+        self.driver.find_element_by_id('change-release-btn').click()
+        #wait for the changes to register in the DB
+        time.sleep(1)
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select count(*) from orm_layer_version a, orm_projectlayer b, orm_project c where a.\"commit\"=\"HEAD\" and a.id = b.layercommit_id and b.project_id=c.id and c.name='new-default-project';"
+        cursor.execute(query)
+        data = cursor.fetchone()[0]
+        #resetting release to default
+        self.driver.find_element_by_id('release-change-toggle').click()
+        dropdown = self.driver.find_element_by_css_selector('select')
+        for option in dropdown.find_elements_by_tag_name('option'):
+            if option.text == 'Yocto Project master':
+                option.click()
+        self.driver.find_element_by_id('change-release-btn').click()
+        #wait for the changes to register in the DB
+        time.sleep(1)
+        self.failUnless(data == 3)
+
+        ##############
+        #  CASE 1092 #
+        ##############
+    def test_1092(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+        self.driver.maximize_window()
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select a.name, a.value from orm_projectvariable a, orm_project b where a.project_id = b.id and b.name = 'new-default-project';"
+        cursor.execute(query)
+        data = dict(cursor.fetchall())
+        print data
+        default_values = {u'IMAGE_INSTALL_append': u'', u'PACKAGE_CLASSES': u'package_rpm', u'MACHINE': u'qemux86', u'SDKMACHINE': u'x86_64', u'DISTRO': u'poky', u'IMAGE_FSTYPES': u'ext3 jffs2 tar.bz2'}
+        self.failUnless(data == default_values)
+
+        ##############
+        #  CASE 1093 #
+        ##############
+    def test_1093(self):
+        self.case_no = self.get_case_number()
+        self.log.info(' CASE %s log: ' % str(self.case_no))
+
+        #get initial values
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
+        cursor.execute(query)
+        data_initial = cursor.fetchall()
+        print data_initial
+
+        self.driver.maximize_window()
+        self.driver.get('localhost:8000')#self.base_url)
+        self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
+        self.driver.find_element_by_link_text('new-default-project').click()
+        self.driver.find_element_by_id('release-change-toggle').click()
+        dropdown = self.driver.find_element_by_css_selector('select')
+        for option in dropdown.find_elements_by_tag_name('option'):
+            if option.text == 'Local Yocto Project':
+                option.click()
+        self.driver.find_element_by_id('change-release-btn').click()
+        #wait for the changes to register in the DB
+        time.sleep(1)
+
+        #get changed values
+        con=sqlite.connect('toaster.sqlite')
+        cursor = con.cursor()
+        query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
+        cursor.execute(query)
+        data_changed = cursor.fetchall()
+        print data_changed
+
+        #resetting release to default
+        self.driver.find_element_by_id('release-change-toggle').click()
+        dropdown = self.driver.find_element_by_css_selector('select')
+        for option in dropdown.find_elements_by_tag_name('option'):
+            if option.text == 'Yocto Project master':
+                option.click()
+        self.driver.find_element_by_id('change-release-btn').click()
+        #wait for the changes to register in the DB
+        time.sleep(1)
+        self.failUnless(data_initial != data_changed)
-- 
2.1.4




More information about the bitbake-devel mailing list