summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/toaster
diff options
context:
space:
mode:
authorMichael Wood <michael.g.wood@intel.com>2016-10-28 18:48:45 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2016-11-04 12:50:55 +0000
commit5bc6fa01e0581c703808597878244cf9a38cefba (patch)
treec4ead257ecf58e3937c3c354636466a7ebdbf163 /bitbake/lib/toaster
parent953b2f50f003a53fc387edfdeb8aa96d904526b2 (diff)
downloadpoky-5bc6fa01e0581c703808597878244cf9a38cefba.tar.gz
bitbake: toaster: Remove contrib tts
Remove the "Toaster test system". We don't need a home brew test "framework" as the django test runner is more than adequate. None of these tests here are currently working and have been obsoleted by the work done on unit and browser tests in ./tests/. (Bitbake rev: 7a82e45ca5c4d470f62f83e72d00cbe45baa1537) Signed-off-by: Michael Wood <michael.g.wood@intel.com> Signed-off-by: Ed Bartosh <ed.bartosh@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster')
-rw-r--r--bitbake/lib/toaster/contrib/README6
-rw-r--r--bitbake/lib/toaster/contrib/tts/README41
-rw-r--r--bitbake/lib/toaster/contrib/tts/TODO9
-rw-r--r--bitbake/lib/toaster/contrib/tts/config.py98
-rwxr-xr-xbitbake/lib/toaster/contrib/tts/launcher.py101
-rw-r--r--bitbake/lib/toaster/contrib/tts/log/.create0
-rwxr-xr-xbitbake/lib/toaster/contrib/tts/recv.py56
-rwxr-xr-xbitbake/lib/toaster/contrib/tts/runner.py222
-rw-r--r--bitbake/lib/toaster/contrib/tts/settings.json5
-rw-r--r--bitbake/lib/toaster/contrib/tts/shellutils.py141
-rw-r--r--bitbake/lib/toaster/contrib/tts/tests.py115
-rwxr-xr-xbitbake/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py155
-rwxr-xr-xbitbake/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py2376
-rw-r--r--bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg25
-rw-r--r--bitbake/lib/toaster/contrib/tts/urlcheck.py53
-rw-r--r--bitbake/lib/toaster/contrib/tts/urllist.py39
16 files changed, 0 insertions, 3442 deletions
diff --git a/bitbake/lib/toaster/contrib/README b/bitbake/lib/toaster/contrib/README
deleted file mode 100644
index 46d0ff008e..0000000000
--- a/bitbake/lib/toaster/contrib/README
+++ /dev/null
@@ -1,6 +0,0 @@
1contrib directory for toaster
2
3This directory holds code that works with Toaster, without being an integral part of the Toaster project.
4It is intended for testing code, testing fixtures, tools for Toaster, etc.
5
6NOTE: This directory is NOT a Python module.
diff --git a/bitbake/lib/toaster/contrib/tts/README b/bitbake/lib/toaster/contrib/tts/README
deleted file mode 100644
index 22fa5673ba..0000000000
--- a/bitbake/lib/toaster/contrib/tts/README
+++ /dev/null
@@ -1,41 +0,0 @@
1
2Toaster Testing Framework
3Yocto Project
4
5
6Rationale
7------------
8As Toaster contributions grow with the number of people that contribute code, verifying each patch prior to submitting upstream becomes a hard-to-scale problem for humans. We devised this system in order to run patch-level validation, trying to eliminate common problems from submitted patches, in an automated fashion.
9
10The Toaster Testing Framework is a set of Python scripts that provides an extensible way to write smoke and regression tests that will be run on each patch set sent for review on the toaster mailing list.
11
12
13Usage
14------------
15There are three main executable scripts in this directory.
16 * runner.py is designed to be run from the command line. It requires, as mandatory parameter, a branch name on poky-contrib, branch which contains the patches to be tested. The program will auto-discover the available tests residing in this directory by looking for unittest classes, and will run the tests on the branch dumping the output to the standard output. Optionally, it can take parameters inhibiting the branch checkout, or specifying a single test to be run, for debugging purposes.
17 * launcher.py is a designed to be run from a crontab or similar scheduling mechanism. It looks up a backlog file containing branches-to-test (named tasks in the source code), select the first one in FIFO manner, and launch runner.py on it. It will await for completion, and email the standard output and standard error dumps from the runner.py execution
18 * recv.py is an email receiver, designed to be called as a pipe from a .forward file. It is used to monitor a mailing list, for example, and add tasks to the backlog based on review requests coming on the mailing list.
19
20
21Installation
22------------
23As prerequisite, we expect a functioning email system on a machine with Python2.
24
25The broad steps to installation
26* set up the .forward on the receiving email account to pipe to the recv.py file
27* edit config.py and settings.json to alter for local installation settings
28* on email receive, verify backlog.txt to see that the tasks are received and marked for processing
29* execute launcher.py in command line to verify that a test occurs with no problems, and that the outgoing email is delivered
30* add launcher.py
31
32
33
34Contribute
35------------
36What we need are tests. Add your own tests to either tests.py file, or to a new file.
37Use "config.logger" to write logs that will make it to email.
38
39Commonly used code should be going to shellutils, and configuration to config.py.
40
41Contribute code by emailing patches to the list: toaster@yoctoproject.org (membership required)
diff --git a/bitbake/lib/toaster/contrib/tts/TODO b/bitbake/lib/toaster/contrib/tts/TODO
deleted file mode 100644
index 117192106f..0000000000
--- a/bitbake/lib/toaster/contrib/tts/TODO
+++ /dev/null
@@ -1,9 +0,0 @@
1We need to implement tests:
2
3automated link checker; currently
4$ linkchecker -t 1000 -F csv http://localhost:8000/
5
6integrate the w3c-validation service; currently
7$ python urlcheck.py
8
9
diff --git a/bitbake/lib/toaster/contrib/tts/config.py b/bitbake/lib/toaster/contrib/tts/config.py
deleted file mode 100644
index 87b427cc3d..0000000000
--- a/bitbake/lib/toaster/contrib/tts/config.py
+++ /dev/null
@@ -1,98 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21# This is the configuration/single module for tts
22# everything that would be a global variable goes here
23
24import os, sys, logging
25import socket
26
27LOGDIR = "log"
28SETTINGS_FILE = os.path.join(os.path.dirname(__file__), "settings.json")
29TEST_DIR_NAME = "tts_testdir"
30
31DEBUG = True
32
33OWN_PID = os.getpid()
34
35W3C_VALIDATOR = "http://icarus.local/w3c-validator/check?doctype=HTML5&uri="
36
37TOASTER_PORT = 56789
38
39TESTDIR = None
40
41#we parse the w3c URL to know where to connect
42
43import urlparse
44
45def get_public_ip():
46 temp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
47 parsed_url = urlparse.urlparse("http://icarus.local/w3c-validator/check?doctype=HTML5&uri=")
48 temp_socket.connect((parsed_url.netloc, 80 if parsed_url.port is None else parsed_url.port))
49 public_ip = temp_socket.getsockname()[0]
50 temp_socket.close()
51 return public_ip
52
53TOASTER_BASEURL = "http://%s:%d/" % (get_public_ip(), TOASTER_PORT)
54
55
56OWN_EMAIL_ADDRESS = "Toaster Testing Framework <alexandru.damian@intel.com>"
57REPORT_EMAIL_ADDRESS = "alexandru.damian@intel.com"
58
59# make sure we have the basic logging infrastructure
60
61#pylint: disable=invalid-name
62# we disable the invalid name because the module-level "logger" is used througout bitbake
63logger = logging.getLogger("toastertest")
64__console__ = logging.StreamHandler(sys.stdout)
65__console__.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s"))
66logger.addHandler(__console__)
67logger.setLevel(logging.DEBUG)
68
69
70# singleton file names
71LOCKFILE = "/tmp/ttf.lock"
72BACKLOGFILE = os.path.join(os.path.dirname(__file__), "backlog.txt")
73
74# task states
75def enum(*sequential, **named):
76 enums = dict(zip(sequential, range(len(sequential))), **named)
77 reverse = dict((value, key) for key, value in enums.items())
78 enums['reverse_mapping'] = reverse
79 return type('Enum', (), enums)
80
81
82class TASKS(object):
83 #pylint: disable=too-few-public-methods
84 PENDING = "PENDING"
85 INPROGRESS = "INPROGRESS"
86 DONE = "DONE"
87
88 @staticmethod
89 def next_task(task):
90 if task == TASKS.PENDING:
91 return TASKS.INPROGRESS
92 if task == TASKS.INPROGRESS:
93 return TASKS.DONE
94 raise Exception("Invalid next task state for %s" % task)
95
96# TTS specific
97CONTRIB_REPO = "git@git.yoctoproject.org:poky-contrib"
98
diff --git a/bitbake/lib/toaster/contrib/tts/launcher.py b/bitbake/lib/toaster/contrib/tts/launcher.py
deleted file mode 100755
index e5794c1c56..0000000000
--- a/bitbake/lib/toaster/contrib/tts/launcher.py
+++ /dev/null
@@ -1,101 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21# Program to run the next task listed from the backlog.txt; designed to be
22# run from crontab.
23
24from __future__ import print_function
25import sys, os, config, shellutils
26from shellutils import ShellCmdException
27
28# Import smtplib for the actual sending function
29import smtplib
30
31# Import the email modules we'll need
32from email.mime.text import MIMEText
33
34def _take_lockfile():
35 return shellutils.lockfile(shellutils.mk_lock_filename())
36
37
38def read_next_task_by_state(task_state, task_name=None):
39 if not os.path.exists(os.path.join(os.path.dirname(__file__), config.BACKLOGFILE)):
40 return None
41 os.rename(config.BACKLOGFILE, config.BACKLOGFILE + ".tmp")
42 task = None
43 with open(config.BACKLOGFILE + ".tmp", "r") as f_in:
44 with open(config.BACKLOGFILE, "w") as f_out:
45 for line in f_in.readlines():
46 if task is None:
47 fields = line.strip().split("|", 2)
48 if fields[1] == task_state:
49 if task_name is None or task_name == fields[0]:
50 task = fields[0]
51 print("Updating %s %s to %s" % (task, task_state, config.TASKS.next_task(task_state)))
52 line = "%s|%s\n" % (task, config.TASKS.next_task(task_state))
53 f_out.write(line)
54 os.remove(config.BACKLOGFILE + ".tmp")
55 return task
56
57def send_report(task_name, plaintext, errtext=None):
58 if errtext is None:
59 msg = MIMEText(plaintext)
60 else:
61 if plaintext is None:
62 plaintext = ""
63 msg = MIMEText("--STDOUT dump--\n\n%s\n\n--STDERR dump--\n\n%s" % (plaintext, errtext))
64
65 msg['Subject'] = "[review-request] %s - smoke test results" % task_name
66 msg['From'] = config.OWN_EMAIL_ADDRESS
67 msg['To'] = config.REPORT_EMAIL_ADDRESS
68
69 smtp_connection = smtplib.SMTP("localhost")
70 smtp_connection.sendmail(config.OWN_EMAIL_ADDRESS, [config.REPORT_EMAIL_ADDRESS], msg.as_string())
71 smtp_connection.quit()
72
73def main():
74 # we don't do anything if we have another instance of us running
75 lock_file = _take_lockfile()
76
77 if lock_file is None:
78 if config.DEBUG:
79 print("Concurrent script in progress, exiting")
80 sys.exit(1)
81
82 next_task = read_next_task_by_state(config.TASKS.PENDING)
83 if next_task is not None:
84 print("Next task is", next_task)
85 errtext = None
86 out = None
87 try:
88 out = shellutils.run_shell_cmd("%s %s" % (os.path.join(os.path.dirname(__file__), "runner.py"), next_task))
89 except ShellCmdException as exc:
90 print("Failed while running the test runner: %s", exc)
91 errtext = exc.__str__()
92 send_report(next_task, out, errtext)
93 read_next_task_by_state(config.TASKS.INPROGRESS, next_task)
94 else:
95 print("No task")
96
97 shellutils.unlockfile(lock_file)
98
99
100if __name__ == "__main__":
101 main()
diff --git a/bitbake/lib/toaster/contrib/tts/log/.create b/bitbake/lib/toaster/contrib/tts/log/.create
deleted file mode 100644
index e69de29bb2..0000000000
--- a/bitbake/lib/toaster/contrib/tts/log/.create
+++ /dev/null
diff --git a/bitbake/lib/toaster/contrib/tts/recv.py b/bitbake/lib/toaster/contrib/tts/recv.py
deleted file mode 100755
index 07efdac44b..0000000000
--- a/bitbake/lib/toaster/contrib/tts/recv.py
+++ /dev/null
@@ -1,56 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21# Program to receive review requests by email and log tasks to backlog.txt
22# Designed to be run by the email system from a .forward file:
23#
24# cat .forward
25# |[full/path]/recv.py
26
27from __future__ import print_function
28import sys, config, shellutils
29
30from email.parser import Parser
31
32def recv_mail(datastring):
33 headers = Parser().parsestr(datastring)
34 return headers['subject']
35
36def main():
37 lock_file = shellutils.lockfile(shellutils.mk_lock_filename(), retry=True)
38
39 if lock_file is None:
40 if config.DEBUG:
41 print("Concurrent script in progress, exiting")
42 sys.exit(1)
43
44 subject = recv_mail(sys.stdin.read())
45
46 subject_parts = subject.split()
47 if "[review-request]" in subject_parts:
48 task_name = subject_parts[subject_parts.index("[review-request]") + 1]
49 with open(config.BACKLOGFILE, "a") as fout:
50 line = "%s|%s\n" % (task_name, config.TASKS.PENDING)
51 fout.write(line)
52
53 shellutils.unlockfile(lock_file)
54
55if __name__ == "__main__":
56 main()
diff --git a/bitbake/lib/toaster/contrib/tts/runner.py b/bitbake/lib/toaster/contrib/tts/runner.py
deleted file mode 100755
index d01386acfa..0000000000
--- a/bitbake/lib/toaster/contrib/tts/runner.py
+++ /dev/null
@@ -1,222 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
22# This is the main test execution controller. It is designed to be run
23# manually from the command line, or to be called from a different program
24# that schedules test execution.
25#
26# Execute runner.py -h for help.
27
28
29
30from __future__ import print_function
31import sys, os
32import unittest, importlib
33import logging, pprint, json
34import re
35from shellutils import ShellCmdException, mkdirhier, run_shell_cmd
36
37import config
38
39# we also log to a file, in addition to console, because our output is important
40__log_file_name__ = os.path.join(os.path.dirname(__file__), "log/tts_%d.log" % config.OWN_PID)
41mkdirhier(os.path.dirname(__log_file_name__))
42__log_file__ = open(__log_file_name__, "w")
43__file_handler__ = logging.StreamHandler(__log_file__)
44__file_handler__.setFormatter(logging.Formatter("%(asctime)s %(levelname)s: %(message)s"))
45
46config.logger.addHandler(__file_handler__)
47
48# set up log directory
49try:
50 if not os.path.exists(config.LOGDIR):
51 os.mkdir(config.LOGDIR)
52 else:
53 if not os.path.isdir(config.LOGDIR):
54 raise Exception("Expected log dir '%s' is not actually a directory." % config.LOGDIR)
55except OSError as exc:
56 raise exc
57
58# creates the under-test-branch as a separate directory
59def set_up_test_branch(settings, branch_name):
60 testdir = "%s/%s.%d" % (settings['workdir'], config.TEST_DIR_NAME, config.OWN_PID)
61
62 # creates the host dir
63 if os.path.exists(testdir):
64 raise Exception("Test dir '%s'is already there, aborting" % testdir)
65
66 # may raise OSError, is to be handled by the caller
67 os.makedirs(testdir)
68
69
70 # copies over the .git from the localclone
71 run_shell_cmd("cp -a '%s'/.git '%s'" % (settings['localclone'], testdir))
72
73 # add the remote if it doesn't exist
74 crt_remotes = run_shell_cmd("git remote -v", cwd=testdir)
75 remotes = [word for line in crt_remotes.split("\n") for word in line.split()]
76 if not config.CONTRIB_REPO in remotes:
77 remote_name = "tts_contrib"
78 run_shell_cmd("git remote add %s %s" % (remote_name, config.CONTRIB_REPO), cwd=testdir)
79 else:
80 remote_name = remotes[remotes.index(config.CONTRIB_REPO) - 1]
81
82 # do the fetch
83 run_shell_cmd("git fetch %s -p" % remote_name, cwd=testdir)
84
85 # do the checkout
86 run_shell_cmd("git checkout origin/master && git branch -D %s; git checkout %s/%s -b %s && git reset --hard" % (branch_name, remote_name, branch_name, branch_name), cwd=testdir)
87
88 return testdir
89
90
91def __search_for_tests():
92 # we find all classes that can run, and run them
93 tests = []
94 for _, _, files_list in os.walk(os.path.dirname(os.path.abspath(__file__))):
95 for module_file in [f[:-3] for f in files_list if f.endswith(".py") and not f.startswith("__init__")]:
96 config.logger.debug("Inspecting module %s", module_file)
97 current_module = importlib.import_module(module_file)
98 crtclass_names = vars(current_module)
99 for name in crtclass_names:
100 tested_value = crtclass_names[name]
101 if isinstance(tested_value, type(unittest.TestCase)) and issubclass(tested_value, unittest.TestCase):
102 tests.append((module_file, name))
103 break
104 return tests
105
106
107# boilerplate to self discover tests and run them
108def execute_tests(dir_under_test, testname):
109
110 if testname is not None and "." in testname:
111 tests = []
112 tests.append(tuple(testname.split(".", 2)))
113 else:
114 tests = __search_for_tests()
115
116 # let's move to the directory under test
117 crt_dir = os.getcwd()
118 os.chdir(dir_under_test)
119
120 # execute each module
121 # pylint: disable=broad-except
122 # we disable the broad-except because we want to actually catch all possible exceptions
123 try:
124 # sorting the tests by the numeric order in the class name
125 tests = sorted(tests, key=lambda x: int(re.search(r"[0-9]+", x[1]).group(0)))
126 config.logger.debug("Discovered test clases: %s", pprint.pformat(tests))
127 unittest.installHandler()
128 suite = unittest.TestSuite()
129 loader = unittest.TestLoader()
130 result = unittest.TestResult()
131 result.failfast = True
132 for module_file, test_name in tests:
133 suite.addTest(loader.loadTestsFromName("%s.%s" % (module_file, test_name)))
134 config.logger.info("Running %d test(s)", suite.countTestCases())
135 suite.run(result)
136
137 for error in result.errors:
138 config.logger.error("Exception on test: %s\n%s", error[0],
139 "\n".join(["-- %s" % x for x in error[1].split("\n")]))
140
141 for failure in result.failures:
142 config.logger.error("Failed test: %s:\n%s\n", failure[0],
143 "\n".join(["-- %s" % x for x in failure[1].split("\n")]))
144
145 config.logger.info("Test results: %d ran, %d errors, %d failures", result.testsRun, len(result.errors), len(result.failures))
146
147 except Exception as exc:
148 import traceback
149 config.logger.error("Exception while running test. Tracedump: \n%s", traceback.format_exc())
150 finally:
151 os.chdir(crt_dir)
152 return len(result.failures)
153
154# verify that we had a branch-under-test name as parameter
155def validate_args():
156 from optparse import OptionParser
157 parser = OptionParser(usage="usage: %prog [options] branch_under_test")
158
159 parser.add_option("-t", "--test-dir", dest="testdir", default=None, help="Use specified directory to run tests, inhibits the checkout.")
160 parser.add_option("-s", "--single", dest="singletest", default=None, help="Run only the specified test")
161
162 (options, args) = parser.parse_args()
163 if len(args) < 1:
164 raise Exception("Please specify the branch to run on. Use option '-h' when in doubt.")
165 return (options, args)
166
167
168
169
170# load the configuration options
171def read_settings():
172 if not os.path.exists(config.SETTINGS_FILE) or not os.path.isfile(config.SETTINGS_FILE):
173 raise Exception("Config file '%s' cannot be openend" % config.SETTINGS_FILE)
174 return json.loads(open(config.SETTINGS_FILE, "r").read())
175
176
177# cleanup !
178def clean_up(testdir):
179 run_shell_cmd("rm -rf -- '%s'" % testdir)
180
181def dump_info(settings, options, args):
182 """ detailed information about current run configuration, for debugging purposes.
183 """
184 config.logger.debug("Settings:\n%s\nOptions:\n%s\nArguments:\n%s\n", settings, options, args)
185
186def main():
187 (options, args) = validate_args()
188
189 settings = read_settings()
190 need_cleanup = False
191
192 # dump debug info
193 dump_info(settings, options, args)
194
195 testdir = None
196 no_failures = 1
197 try:
198 if options.testdir is not None and os.path.exists(options.testdir):
199 testdir = os.path.abspath(options.testdir)
200 config.logger.info("No checkout, using %s", testdir)
201 else:
202 need_cleanup = True
203 testdir = set_up_test_branch(settings, args[0]) # we expect a branch name as first argument
204
205 config.TESTDIR = testdir # we let tests know where to run
206
207 # ensure that the test dir only contains no *.pyc leftovers
208 run_shell_cmd("find '%s' -type f -name *.pyc -exec rm {} \\;" % testdir)
209
210 no_failures = execute_tests(testdir, options.singletest)
211
212 except ShellCmdException as exc:
213 import traceback
214 config.logger.error("Error while setting up testing. Traceback: \n%s", traceback.format_exc())
215 finally:
216 if need_cleanup and testdir is not None:
217 clean_up(testdir)
218
219 sys.exit(no_failures)
220
221if __name__ == "__main__":
222 main()
diff --git a/bitbake/lib/toaster/contrib/tts/settings.json b/bitbake/lib/toaster/contrib/tts/settings.json
deleted file mode 100644
index bb671eaf27..0000000000
--- a/bitbake/lib/toaster/contrib/tts/settings.json
+++ /dev/null
@@ -1,5 +0,0 @@
1{
2 "repo": "git@git.yoctoproject.org:poky-contrib",
3 "localclone": "/home/ddalex/ssd/yocto/poky",
4 "workdir": "/home/ddalex/ssd/yocto"
5}
diff --git a/bitbake/lib/toaster/contrib/tts/shellutils.py b/bitbake/lib/toaster/contrib/tts/shellutils.py
deleted file mode 100644
index ce64c06340..0000000000
--- a/bitbake/lib/toaster/contrib/tts/shellutils.py
+++ /dev/null
@@ -1,141 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21# Utilities shared by tests and other common bits of code.
22
23import sys, os, subprocess, fcntl, errno
24import config
25from config import logger
26
27
28# License warning; this code is copied from the BitBake project, file bitbake/lib/bb/utils.py
29# The code is originally licensed GPL-2.0, and we redistribute it under still GPL-2.0
30
31# End of copy is marked with #ENDOFCOPY marker
32
33def mkdirhier(directory):
34 """Create a directory like 'mkdir -p', but does not complain if
35 directory already exists like os.makedirs
36 """
37
38 try:
39 os.makedirs(directory)
40 except OSError as exc:
41 if exc.errno != errno.EEXIST:
42 raise exc
43
44def lockfile(name, shared=False, retry=True):
45 """
46 Use the file fn as a lock file, return when the lock has been acquired.
47 Returns a variable to pass to unlockfile().
48 """
49 config.logger.debug("take lockfile %s", name)
50 dirname = os.path.dirname(name)
51 mkdirhier(dirname)
52
53 if not os.access(dirname, os.W_OK):
54 logger.error("Unable to acquire lock '%s', directory is not writable",
55 name)
56 sys.exit(1)
57
58 operation = fcntl.LOCK_EX
59 if shared:
60 operation = fcntl.LOCK_SH
61 if not retry:
62 operation = operation | fcntl.LOCK_NB
63
64 while True:
65 # If we leave the lockfiles lying around there is no problem
66 # but we should clean up after ourselves. This gives potential
67 # for races though. To work around this, when we acquire the lock
68 # we check the file we locked was still the lock file on disk.
69 # by comparing inode numbers. If they don't match or the lockfile
70 # no longer exists, we start again.
71
72 # This implementation is unfair since the last person to request the
73 # lock is the most likely to win it.
74
75 # pylint: disable=broad-except
76 # we disable the broad-except because we want to actually catch all possible exceptions
77 try:
78 lock_file = open(name, 'a+')
79 fileno = lock_file.fileno()
80 fcntl.flock(fileno, operation)
81 statinfo = os.fstat(fileno)
82 if os.path.exists(lock_file.name):
83 statinfo2 = os.stat(lock_file.name)
84 if statinfo.st_ino == statinfo2.st_ino:
85 return lock_file
86 lock_file.close()
87 except Exception as exc:
88 try:
89 lock_file.close()
90 except Exception as exc2:
91 config.logger.error("Failed to close the lockfile: %s", exc2)
92 config.logger.error("Failed to acquire the lockfile: %s", exc)
93 if not retry:
94 return None
95
96def unlockfile(lock_file):
97 """
98 Unlock a file locked using lockfile()
99 """
100 try:
101 # If we had a shared lock, we need to promote to exclusive before
102 # removing the lockfile. Attempt this, ignore failures.
103 fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
104 os.unlink(lock_file.name)
105 except (IOError, OSError):
106 pass
107 fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
108 lock_file.close()
109
110#ENDOFCOPY
111
112
113def mk_lock_filename():
114 our_name = os.path.basename(__file__)
115 our_name = ".%s" % ".".join(reversed(our_name.split(".")))
116 return config.LOCKFILE + our_name
117
118
119
120class ShellCmdException(Exception):
121 pass
122
123def run_shell_cmd(command, cwd=None):
124 if cwd is None:
125 cwd = os.getcwd()
126
127 config.logger.debug("_shellcmd: (%s) %s", cwd, command)
128 process = subprocess.Popen(command, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
129 (out, err) = process.communicate()
130 process.wait()
131 if process.returncode:
132 if len(err) == 0:
133 err = "command: %s \n%s" % (command, out)
134 else:
135 err = "command: %s \n%s" % (command, err)
136 config.logger.warning("_shellcmd: error \n%s\n%s", out, err)
137 raise ShellCmdException(err)
138 else:
139 #config.logger.debug("localhostbecontroller: shellcmd success\n%s" % out)
140 return out
141
diff --git a/bitbake/lib/toaster/contrib/tts/tests.py b/bitbake/lib/toaster/contrib/tts/tests.py
deleted file mode 100644
index c510ebb108..0000000000
--- a/bitbake/lib/toaster/contrib/tts/tests.py
+++ /dev/null
@@ -1,115 +0,0 @@
1#!/usr/bin/python
2
3# ex:ts=4:sw=4:sts=4:et
4# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5#
6# Copyright (C) 2015 Alexandru Damian for Intel Corp.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
22# Test definitions. The runner will look for and auto-discover the tests
23# no matter what they file are they in, as long as they are in the same directory
24# as this file.
25
26import unittest
27from shellutils import run_shell_cmd, ShellCmdException
28import config
29
30import pexpect
31import sys, os, signal, time
32
33class Test00PyCompilable(unittest.TestCase):
34 ''' Verifies that all Python files are syntactically correct '''
35 def test_compile_file(self):
36 try:
37 run_shell_cmd("find . -name *py -type f -print0 | xargs -0 -n1 -P20 python -m py_compile", config.TESTDIR)
38 except ShellCmdException as exc:
39 self.fail("Error compiling python files: %s" % (exc))
40
41 def test_pylint_file(self):
42 try:
43 run_shell_cmd(r"find . -iname \"*\.py\" -type f -print0 | PYTHONPATH=${PYTHONPATH}:. xargs -r -0 -n1 pylint --load-plugins pylint_django -E --reports=n 2>&1", cwd=config.TESTDIR + "/bitbake/lib/toaster")
44 except ShellCmdException as exc:
45 self.fail("Pylint fails: %s\n" % exc)
46
47class Test01PySystemStart(unittest.TestCase):
48 ''' Attempts to start Toaster, verify that it is succesfull, and stop it '''
49 def setUp(self):
50 run_shell_cmd("bash -c 'rm -f build/*log'")
51
52 def test_start_interactive_mode(self):
53 try:
54 run_shell_cmd("bash -c 'source %s/oe-init-build-env && source toaster start webport=%d && source toaster stop'" % (config.TESTDIR, config.TOASTER_PORT), config.TESTDIR)
55 except ShellCmdException as exc:
56 self.fail("Failed starting interactive mode: %s" % (exc))
57
58 def test_start_managed_mode(self):
59 try:
60 run_shell_cmd("%s/bitbake/bin/toaster webport=%d nobrowser & sleep 10 && curl http://localhost:%d/ && kill -2 %%1" % (config.TESTDIR, config.TOASTER_PORT, config.TOASTER_PORT), config.TESTDIR)
61 except ShellCmdException as exc:
62 self.fail("Failed starting managed mode: %s" % (exc))
63
64class Test02HTML5Compliance(unittest.TestCase):
65 def setUp(self):
66 self.origdir = os.getcwd()
67 self.crtdir = os.path.dirname(config.TESTDIR)
68 self.cleanup_database = False
69 os.chdir(self.crtdir)
70 if not os.path.exists(os.path.join(self.crtdir, "toaster.sqlite")):
71 self.cleanup_database = True
72 run_shell_cmd("%s/bitbake/lib/toaster/manage.py syncdb --noinput" % config.TESTDIR)
73 run_shell_cmd("%s/bitbake/lib/toaster/manage.py migrate orm" % config.TESTDIR)
74 run_shell_cmd("%s/bitbake/lib/toaster/manage.py migrate bldcontrol" % config.TESTDIR)
75 run_shell_cmd("%s/bitbake/lib/toaster/manage.py loadconf %s/meta-yocto/conf/toasterconf.json" % (config.TESTDIR, config.TESTDIR))
76 run_shell_cmd("%s/bitbake/lib/toaster/manage.py lsupdates" % config.TESTDIR)
77
78 setup = pexpect.spawn("%s/bitbake/lib/toaster/manage.py checksettings" % config.TESTDIR)
79 setup.logfile = sys.stdout
80 setup.expect(r".*or type the full path to a different directory: ")
81 setup.sendline('')
82 setup.sendline('')
83 setup.expect(r".*or type the full path to a different directory: ")
84 setup.sendline('')
85 setup.expect(r"Enter your option: ")
86 setup.sendline('0')
87
88 self.child = pexpect.spawn("bash", ["%s/bitbake/bin/toaster" % config.TESTDIR, "webport=%d" % config.TOASTER_PORT, "nobrowser"], cwd=self.crtdir)
89 self.child.logfile = sys.stdout
90 self.child.expect("Toaster is now running. You can stop it with Ctrl-C")
91
92 def test_html5_compliance(self):
93 import urllist, urlcheck
94 results = {}
95 for url in urllist.URLS:
96 results[url] = urlcheck.validate_html5(config.TOASTER_BASEURL + url)
97
98 failed = []
99 for url in results:
100 if results[url][1] != 0:
101 failed.append((url, results[url]))
102
103
104 self.assertTrue(len(failed) == 0, "Not all URLs validate: \n%s " % "\n".join(["".join(str(x)) for x in failed]))
105
106 #(config.TOASTER_BASEURL + url, status, errors, warnings))
107
108 def tearDown(self):
109 while self.child.isalive():
110 self.child.kill(signal.SIGINT)
111 time.sleep(1)
112 os.chdir(self.origdir)
113 toaster_sqlite_path = os.path.join(self.crtdir, "toaster.sqlite")
114 if self.cleanup_database and os.path.exists(toaster_sqlite_path):
115 os.remove(toaster_sqlite_path)
diff --git a/bitbake/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py b/bitbake/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py
deleted file mode 100755
index 8ca45a8136..0000000000
--- a/bitbake/lib/toaster/contrib/tts/toasteruitest/run_toastertests.py
+++ /dev/null
@@ -1,155 +0,0 @@
1#!/usr/bin/python
2
3# Copyright
4
5# DESCRIPTION
6# This is script for running all selected toaster cases on
7# selected web browsers manifested in toaster_test.cfg.
8
9# 1. How to start toaster in yocto:
10# $ source poky/oe-init-build-env
11# $ source toaster start
12# $ bitbake core-image-minimal
13
14# 2. How to install selenium on Ubuntu:
15# $ sudo apt-get install scrot python-pip
16# $ sudo pip install selenium
17
18# 3. How to install selenium addon in firefox:
19# Download the lastest firefox addon from http://release.seleniumhq.org/selenium-ide/
20# Then install it. You can also install firebug and firepath addon
21
22# 4. How to start writing a new case:
23# All you need to do is to implement the function test_xxx() and pile it on.
24
25# 5. How to test with Chrome browser
26# Download/install chrome on host
27# Download chromedriver from https://code.google.com/p/chromedriver/downloads/list according to your host type
28# put chromedriver in PATH, (e.g. /usr/bin/, bear in mind to chmod)
29# For windows host, you may put chromedriver.exe in the same directory as chrome.exe
30
31import unittest, sys, os, platform
32import ConfigParser
33import argparse
34from toaster_automation_test import toaster_cases
35
36
37def get_args_parser():
38 description = "Script that runs toaster auto tests."
39 parser = argparse.ArgumentParser(description=description)
40 parser.add_argument('--run-all-tests', required=False, action="store_true", dest="run_all_tests", default=False,
41 help='Run all tests.')
42 parser.add_argument('--run-suite', required=False, dest='run_suite', default=False,
43 help='run suite (defined in cfg file)')
44
45 return parser
46
47
48def get_tests():
49 testslist = []
50
51 prefix = 'toaster_automation_test.toaster_cases'
52
53 for t in dir(toaster_cases):
54 if t.startswith('test_'):
55 testslist.append('.'.join((prefix, t)))
56
57 return testslist
58
59
60def get_tests_from_cfg(suite=None):
61
62 testslist = []
63 config = ConfigParser.SafeConfigParser()
64 config.read('toaster_test.cfg')
65
66 if suite is not None:
67 target_suite = suite.lower()
68
69 # TODO: if suite is valid suite
70
71 else:
72 target_suite = platform.system().lower()
73
74 try:
75 tests_from_cfg = eval(config.get('toaster_test_' + target_suite, 'test_cases'))
76 except:
77 print('Failed to get test cases from cfg file. Make sure the format is correct.')
78 return None
79
80 prefix = 'toaster_automation_test.toaster_cases.test_'
81 for t in tests_from_cfg:
82 testslist.append(prefix + str(t))
83
84 return testslist
85
86def main():
87
88 # In case this script is called from other directory
89 os.chdir(os.path.abspath(sys.path[0]))
90
91 parser = get_args_parser()
92 args = parser.parse_args()
93
94 if args.run_all_tests:
95 testslist = get_tests()
96 elif args.run_suite:
97 testslist = get_tests_from_cfg(args.run_suite)
98 os.environ['TOASTER_SUITE'] = args.run_suite
99 else:
100 testslist = get_tests_from_cfg()
101
102 if not testslist:
103 print('Failed to get test cases.')
104 exit(1)
105
106 suite = unittest.TestSuite()
107 loader = unittest.TestLoader()
108 loader.sortTestMethodsUsing = None
109 runner = unittest.TextTestRunner(verbosity=2, resultclass=buildResultClass(args))
110
111 for test in testslist:
112 try:
113 suite.addTests(loader.loadTestsFromName(test))
114 except:
115 return 1
116
117 result = runner.run(suite)
118
119 if result.wasSuccessful():
120 return 0
121 else:
122 return 1
123
124
125def buildResultClass(args):
126 """Build a Result Class to use in the testcase execution"""
127
128 class StampedResult(unittest.TextTestResult):
129 """
130 Custom TestResult that prints the time when a test starts. As toaster-auto
131 can take a long time (ie a few hours) to run, timestamps help us understand
132 what tests are taking a long time to execute.
133 """
134 def startTest(self, test):
135 import time
136 self.stream.write(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " - ")
137 super(StampedResult, self).startTest(test)
138
139 return StampedResult
140
141
142if __name__ == "__main__":
143
144 try:
145 ret = main()
146 except:
147 ret = 1
148 import traceback
149 traceback.print_exc()
150 finally:
151 if os.getenv('TOASTER_SUITE'):
152 del os.environ['TOASTER_SUITE']
153 sys.exit(ret)
154
155
diff --git a/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py b/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py
deleted file mode 100755
index 1a786fa021..0000000000
--- a/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_automation_test.py
+++ /dev/null
@@ -1,2376 +0,0 @@
1#!/usr/bin/python
2# Copyright
3
4# DESCRIPTION
5# This is toaster automation base class and test cases file
6
7# History:
8# 2015.03.09 inital version
9# 2015.03.23 adding toaster_test.cfg, run_toastertest.py so we can run case by case from outside
10
11# Briefs:
12# This file is comprised of 3 parts:
13# I: common utils like sorting, getting attribute.. etc
14# II: base class part, which complies with unittest frame work and
15# contains class selenium-based functions
16# III: test cases
17# to add new case: just implement new test_xxx() function in class toaster_cases
18
19# NOTES for cases:
20# case 946:
21# step 6 - 8 needs to be observed using screenshots
22# case 956:
23# step 2 - 3 needs to be run manually
24
25import unittest, time, re, sys, getopt, os, logging, string, errno, exceptions
26import shutil, argparse, ConfigParser, platform, json
27from selenium import webdriver
28from selenium.common.exceptions import NoSuchElementException
29from selenium import selenium
30from selenium.webdriver.common.by import By
31from selenium.webdriver.common.keys import Keys
32from selenium.webdriver.support.ui import Select
33import sqlite3 as sqlite
34
35
36###########################################
37# #
38# PART I: utils stuff #
39# #
40###########################################
41
42class Listattr(object):
43 """
44 Set of list attribute. This is used to determine what the list content is.
45 Later on we may add more attributes here.
46 """
47 NULL = "null"
48 NUMBERS = "numbers"
49 STRINGS = "strings"
50 PERCENT = "percentage"
51 SIZE = "size"
52 UNKNOWN = "unknown"
53
54
55def get_log_root_dir():
56 max_depth = 5
57 parent_dir = '../'
58 for number in range(0, max_depth):
59 if os.path.isdir(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log'):
60 log_root_dir = os.path.abspath(sys.path[0] + os.sep + (os.pardir + os.sep)*number + 'log')
61 break
62
63 if number == (max_depth - 1):
64 print('No log dir found. Please check')
65 raise Exception
66
67 return log_root_dir
68
69
70def mkdir_p(dir):
71 try:
72 os.makedirs(dir)
73 except OSError as exc:
74 if exc.errno == errno.EEXIST and os.path.isdir(dir):
75 pass
76 else:
77 raise
78
79
80def get_list_attr(testlist):
81 """
82 To determine the list content
83 """
84 if not testlist:
85 return Listattr.NULL
86 listtest = testlist[:]
87 try:
88 listtest.remove('')
89 except ValueError:
90 pass
91 pattern_percent = re.compile(r"^([0-9])+(\.)?([0-9])*%$")
92 pattern_size = re.compile(r"^([0-9])+(\.)?([0-9])*( )*(K)*(M)*(G)*B$")
93 pattern_number = re.compile(r"^([0-9])+(\.)?([0-9])*$")
94 def get_patterned_number(pattern, tlist):
95 count = 0
96 for item in tlist:
97 if re.search(pattern, item):
98 count += 1
99 return count
100 if get_patterned_number(pattern_percent, listtest) == len(listtest):
101 return Listattr.PERCENT
102 elif get_patterned_number(pattern_size, listtest) == len(listtest):
103 return Listattr.SIZE
104 elif get_patterned_number(pattern_number, listtest) == len(listtest):
105 return Listattr.NUMBERS
106 else:
107 return Listattr.STRINGS
108
109
110def is_list_sequenced(testlist):
111 """
112 Function to tell if list is sequenced
113 Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
114 Each has respective way to determine if it's sequenced.
115 """
116 test_list = testlist[:]
117 try:
118 test_list.remove('')
119 except ValueError:
120 pass
121
122 if get_list_attr(testlist) == Listattr.NULL :
123 return True
124
125 elif get_list_attr(testlist) == Listattr.STRINGS :
126 return (sorted(test_list) == test_list)
127
128 elif get_list_attr(testlist) == Listattr.NUMBERS :
129 list_number = []
130 for item in test_list:
131 list_number.append(eval(item))
132 return (sorted(list_number) == list_number)
133
134 elif get_list_attr(testlist) == Listattr.PERCENT :
135 list_number = []
136 for item in test_list:
137 list_number.append(eval(item.strip('%')))
138 return (sorted(list_number) == list_number)
139
140 elif get_list_attr(testlist) == Listattr.SIZE :
141 list_number = []
142 # currently SIZE is splitted by space
143 for item in test_list:
144 if item.split()[1].upper() == "KB":
145 list_number.append(1024 * eval(item.split()[0]))
146 elif item.split()[1].upper() == "MB":
147 list_number.append(1024 * 1024 * eval(item.split()[0]))
148 elif item.split()[1].upper() == "GB":
149 list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
150 else:
151 list_number.append(eval(item.split()[0]))
152 return (sorted(list_number) == list_number)
153
154 else:
155 print('Unrecognized list type, please check')
156 return False
157
158
159def is_list_inverted(testlist):
160 """
161 Function to tell if list is inverted
162 Currently we may have list made up of: Strings ; numbers ; percentage ; time; size
163 Each has respective way to determine if it's inverted.
164 """
165 test_list = testlist[:]
166 try:
167 test_list.remove('')
168 except ValueError:
169 pass
170
171 if get_list_attr(testlist) == Listattr.NULL :
172 return True
173
174 elif get_list_attr(testlist) == Listattr.STRINGS :
175 return (sorted(test_list, reverse = True) == test_list)
176
177 elif get_list_attr(testlist) == Listattr.NUMBERS :
178 list_number = []
179 for item in test_list:
180 list_number.append(eval(item))
181 return (sorted(list_number, reverse = True) == list_number)
182
183 elif get_list_attr(testlist) == Listattr.PERCENT :
184 list_number = []
185 for item in test_list:
186 list_number.append(eval(item.strip('%')))
187 return (sorted(list_number, reverse = True) == list_number)
188
189 elif get_list_attr(testlist) == Listattr.SIZE :
190 list_number = []
191 # currently SIZE is splitted by space. such as 0 B; 1 KB; 2 MB
192 for item in test_list:
193 if item.split()[1].upper() == "KB":
194 list_number.append(1024 * eval(item.split()[0]))
195 elif item.split()[1].upper() == "MB":
196 list_number.append(1024 * 1024 * eval(item.split()[0]))
197 elif item.split()[1].upper() == "GB":
198 list_number.append(1024 * 1024 * 1024 * eval(item.split()[0]))
199 else:
200 list_number.append(eval(item.split()[0]))
201 return (sorted(list_number, reverse = True) == list_number)
202
203 else:
204 print('Unrecognized list type, please check')
205 return False
206
207def replace_file_content(filename, item, option):
208 f = open(filename)
209 lines = f.readlines()
210 f.close()
211 output = open(filename, 'w')
212 for line in lines:
213 if line.startswith(item):
214 output.write(item + " = '" + option + "'\n")
215 else:
216 output.write(line)
217 output.close()
218
219def extract_number_from_string(s):
220 """
221 extract the numbers in a string. return type is 'list'
222 """
223 return re.findall(r'([0-9]+)', s)
224
225# Below is decorator derived from toaster backend test code
226class NoParsingFilter(logging.Filter):
227 def filter(self, record):
228 return record.levelno == 100
229
230def LogResults(original_class):
231 orig_method = original_class.run
232
233 from time import strftime, gmtime
234 caller = 'toaster'
235 timestamp = strftime('%Y%m%d%H%M%S',gmtime())
236 logfile = os.path.join(os.getcwd(),'results-'+caller+'.'+timestamp+'.log')
237 linkfile = os.path.join(os.getcwd(),'results-'+caller+'.log')
238
239 #rewrite the run method of unittest.TestCase to add testcase logging
240 def run(self, result, *args, **kws):
241 orig_method(self, result, *args, **kws)
242 passed = True
243 testMethod = getattr(self, self._testMethodName)
244 #if test case is decorated then use it's number, else use it's name
245 try:
246 test_case = testMethod.test_case
247 except AttributeError:
248 test_case = self._testMethodName
249
250 class_name = str(testMethod.im_class).split("'")[1]
251
252 #create custom logging level for filtering.
253 custom_log_level = 100
254 logging.addLevelName(custom_log_level, 'RESULTS')
255
256 def results(self, message, *args, **kws):
257 if self.isEnabledFor(custom_log_level):
258 self.log(custom_log_level, message, *args, **kws)
259 logging.Logger.results = results
260
261 logging.basicConfig(filename=logfile,
262 filemode='w',
263 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
264 datefmt='%H:%M:%S',
265 level=custom_log_level)
266 for handler in logging.root.handlers:
267 handler.addFilter(NoParsingFilter())
268 local_log = logging.getLogger(caller)
269
270 #check status of tests and record it
271
272 for (name, msg) in result.errors:
273 if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
274 local_log.results("Testcase "+str(test_case)+": ERROR")
275 local_log.results("Testcase "+str(test_case)+":\n"+msg)
276 passed = False
277 for (name, msg) in result.failures:
278 if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
279 local_log.results("Testcase "+str(test_case)+": FAILED")
280 local_log.results("Testcase "+str(test_case)+":\n"+msg)
281 passed = False
282 for (name, msg) in result.skipped:
283 if (self._testMethodName == str(name).split(' ')[0]) and (class_name in str(name).split(' ')[1]):
284 local_log.results("Testcase "+str(test_case)+": SKIPPED")
285 passed = False
286 if passed:
287 local_log.results("Testcase "+str(test_case)+": PASSED")
288
289 # Create symlink to the current log
290 if os.path.exists(linkfile):
291 os.remove(linkfile)
292 os.symlink(logfile, linkfile)
293
294 original_class.run = run
295
296 return original_class
297
298
299###########################################
300# #
301# PART II: base class #
302# #
303###########################################
304
305@LogResults
306class toaster_cases_base(unittest.TestCase):
307
308 @classmethod
309 def setUpClass(cls):
310 cls.log = cls.logger_create()
311
312 def setUp(self):
313 self.screenshot_sequence = 1
314 self.verificationErrors = []
315 self.accept_next_alert = True
316 self.host_os = platform.system().lower()
317 if os.getenv('TOASTER_SUITE'):
318 self.target_suite = os.getenv('TOASTER_SUITE')
319 else:
320 self.target_suite = self.host_os
321
322 self.parser = ConfigParser.SafeConfigParser()
323 self.parser.read('toaster_test.cfg')
324 self.base_url = eval(self.parser.get('toaster_test_' + self.target_suite, 'toaster_url'))
325
326 # create log dir . Currently , we put log files in log/tmp. After all
327 # test cases are done, move them to log/$datetime dir
328 self.log_tmp_dir = os.path.abspath(sys.path[0]) + os.sep + 'log' + os.sep + 'tmp'
329 try:
330 mkdir_p(self.log_tmp_dir)
331 except OSError :
332 logging.error("%(asctime)s Cannot create tmp dir under log, please check your privilege")
333 # self.log = self.logger_create()
334 # driver setup
335 self.setup_browser()
336
337 @staticmethod
338 def logger_create():
339 log_file = "toaster-auto-" + time.strftime("%Y%m%d%H%M%S") + ".log"
340 if os.path.exists("toaster-auto.log"): os.remove("toaster-auto.log")
341 os.symlink(log_file, "toaster-auto.log")
342
343 log = logging.getLogger("toaster")
344 log.setLevel(logging.DEBUG)
345
346 fh = logging.FileHandler(filename=log_file, mode='w')
347 fh.setLevel(logging.DEBUG)
348
349 ch = logging.StreamHandler(sys.stdout)
350 ch.setLevel(logging.INFO)
351
352 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
353 fh.setFormatter(formatter)
354 ch.setFormatter(formatter)
355
356 log.addHandler(fh)
357 log.addHandler(ch)
358
359 return log
360
361
362 def setup_browser(self, *browser_path):
363 self.browser = eval(self.parser.get('toaster_test_' + self.target_suite, 'test_browser'))
364 print(self.browser)
365 if self.browser == "firefox":
366 driver = webdriver.Firefox()
367 elif self.browser == "chrome":
368 driver = webdriver.Chrome()
369 elif self.browser == "ie":
370 driver = webdriver.Ie()
371 else:
372 driver = None
373 print("unrecognized browser type, please check")
374 self.driver = driver
375 self.driver.implicitly_wait(30)
376 return self.driver
377
378
379 def save_screenshot(self, **log_args):
380 """
381 This function is used to save screen either by os interface or selenium interface.
382 How to use:
383 self.save_screenshot(screenshot_type = 'native'/'selenium', log_sub_dir = 'xxx',
384 append_name = 'stepx')
385 where native means screenshot func provided by OS,
386 selenium means screenshot func provided by selenium webdriver
387 """
388 types = [log_args.get('screenshot_type')]
389 # when no screenshot_type is specified
390 if types == [None]:
391 types = ['native', 'selenium']
392 # normally append_name is used to specify which step..
393 add_name = log_args.get('append_name')
394 if not add_name:
395 add_name = '-'
396 # normally there's no need to specify sub_dir
397 sub_dir = log_args.get('log_sub_dir')
398 if not sub_dir:
399 # use casexxx as sub_dir name
400 sub_dir = 'case' + str(self.case_no)
401 for item in types:
402 log_dir = self.log_tmp_dir + os.sep + sub_dir
403 mkdir_p(log_dir)
404 log_path = log_dir + os.sep + self.browser + '-' +\
405 item + '-' + add_name + '-' + str(self.screenshot_sequence) + '.png'
406 if item == 'native':
407 if self.host_os == "linux":
408 os.system("scrot " + log_path)
409 elif self.host_os=="darwin":
410 os.system("screencapture -x " + log_path)
411 elif item == 'selenium':
412 self.driver.get_screenshot_as_file(log_path)
413 self.screenshot_sequence += 1
414
415 def browser_delay(self):
416 """
417 currently this is a workaround for some chrome test.
418 Sometimes we need a delay to accomplish some operation.
419 But for firefox, mostly we don't need this.
420 To be discussed
421 """
422 if self.browser == "chrome":
423 time.sleep(1)
424 return
425
426
427# these functions are not contained in WebDriver class..
428 def find_element_by_text(self, string):
429 return self.driver.find_element_by_xpath("//*[text()='" + string + "']")
430
431
432 def find_elements_by_text(self, string):
433 return self.driver.find_elements_by_xpath("//*[text()='" + string + "']")
434
435
436 def find_element_by_text_in_table(self, table_id, text_string):
437 """
438 This is used to search some certain 'text' in certain table
439 """
440 try:
441 table_element = self.get_table_element(table_id)
442 element = table_element.find_element_by_xpath("//*[text()='" + text_string + "']")
443 except NoSuchElementException as e:
444 print('no element found')
445 raise
446 return element
447
448
449 def find_element_by_link_text_in_table(self, table_id, link_text):
450 """
451 Assume there're multiple suitable "find_element_by_link_text".
452 In this circumstance we need to specify "table".
453 """
454 try:
455 table_element = self.get_table_element(table_id)
456 element = table_element.find_element_by_link_text(link_text)
457 except NoSuchElementException as e:
458 print('no element found')
459 raise
460 return element
461
462
463 def find_elements_by_link_text_in_table(self, table_id, link_text):
464 """
465 Search link-text in certain table. This helps to narrow down search area.
466 """
467 try:
468 table_element = self.get_table_element(table_id)
469 element_list = table_element.find_elements_by_link_text(link_text)
470 except NoSuchElementException as e:
471 print('no element found')
472 raise
473 return element_list
474
475
476 def find_element_by_partial_link_text_in_table(self, table_id, link_text):
477 """
478 Search element by partial link text in certain table.
479 """
480 try:
481 table_element = self.get_table_element(table_id)
482 element = table_element.find_element_by_partial_link_text(link_text)
483 return element
484 except NoSuchElementException as e:
485 print('no element found')
486 raise
487
488
489 def find_elements_by_partial_link_text_in_table(self, table_id, link_text):
490 """
491 Assume there're multiple suitable "find_partial_element_by_link_text".
492 """
493 try:
494 table_element = self.get_table_element(table_id)
495 element_list = table_element.find_elements_by_partial_link_text(link_text)
496 return element_list
497 except NoSuchElementException as e:
498 print('no element found')
499 raise
500
501
502 def find_element_by_xpath_in_table(self, table_id, xpath):
503 """
504 This helps to narrow down search area. Especially useful when dealing with pop-up form.
505 """
506 try:
507 table_element = self.get_table_element(table_id)
508 element = table_element.find_element_by_xpath(xpath)
509 except NoSuchElementException as e:
510 print('no element found')
511 raise
512 return element
513
514
515 def find_elements_by_xpath_in_table(self, table_id, xpath):
516 """
517 This helps to narrow down search area. Especially useful when dealing with pop-up form.
518 """
519 try:
520 table_element = self.get_table_element(table_id)
521 element_list = table_element.find_elements_by_xpath(xpath)
522 except NoSuchElementException as e:
523 print('no elements found')
524 raise
525 return element_list
526
527
528 def shortest_xpath(self, pname, pvalue):
529 return "//*[@" + pname + "='" + pvalue + "']"
530
531
532#usually elements in the same column are with same class name. for instance: class="outcome" .TBD
533 def get_table_column_text(self, attr_name, attr_value):
534 c_xpath = self.shortest_xpath(attr_name, attr_value)
535 elements = self.driver.find_elements_by_xpath(c_xpath)
536 c_list = []
537 for element in elements:
538 c_list.append(element.text)
539 return c_list
540
541
542 def get_table_column_text_by_column_number(self, table_id, column_number):
543 c_xpath = "//*[@id='" + table_id + "']//td[" + str(column_number) + "]"
544 elements = self.driver.find_elements_by_xpath(c_xpath)
545 c_list = []
546 for element in elements:
547 c_list.append(element.text)
548 return c_list
549
550
551 def get_table_head_text(self, *table_id):
552#now table_id is a tuple...
553 if table_id:
554 thead_xpath = "//*[@id='" + table_id[0] + "']//thead//th[text()]"
555 elements = self.driver.find_elements_by_xpath(thead_xpath)
556 c_list = []
557 for element in elements:
558 if element.text:
559 c_list.append(element.text)
560 return c_list
561#default table on page
562 else:
563 return self.driver.find_element_by_xpath("//*/table/thead").text
564
565
566
567 def get_table_element(self, table_id, *coordinate):
568 if len(coordinate) == 0:
569#return whole-table element
570 element_xpath = "//*[@id='" + table_id + "']"
571 try:
572 element = self.driver.find_element_by_xpath(element_xpath)
573 except NoSuchElementException as e:
574 raise
575 return element
576 row = coordinate[0]
577
578 if len(coordinate) == 1:
579#return whole-row element
580 element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]"
581 try:
582 element = self.driver.find_element_by_xpath(element_xpath)
583 except NoSuchElementException as e:
584 return False
585 return element
586#now we are looking for an element with specified X and Y
587 column = coordinate[1]
588
589 element_xpath = "//*[@id='" + table_id + "']/tbody/tr[" + str(row) + "]/td[" + str(column) + "]"
590 try:
591 element = self.driver.find_element_by_xpath(element_xpath)
592 except NoSuchElementException as e:
593 return False
594 return element
595
596
597 def get_table_data(self, table_id, row_count, column_count):
598 row = 1
599 Lists = []
600 while row <= row_count:
601 column = 1
602 row_content=[]
603 while column <= column_count:
604 s= "//*[@id='" + table_id + "']/tbody/tr[" + str(row) +"]/td[" + str(column) + "]"
605 v = self.driver.find_element_by_xpath(s).text
606 row_content.append(v)
607 column = column + 1
608 print("row_content=",row_content)
609 Lists.extend(row_content)
610 print(Lists[row-1][0])
611 row = row + 1
612 return Lists
613
614 # The is_xxx_present functions only returns True/False
615 # All the log work is done in test procedure, so we can easily trace back
616 # using logging
617 def is_text_present (self, patterns):
618 for pattern in patterns:
619 if str(pattern) not in self.driver.page_source:
620 print('Text "'+pattern+'" is missing')
621 return False
622 return True
623
624
625 def is_element_present(self, how, what):
626 try:
627 self.driver.find_element(how, what)
628 except NoSuchElementException as e:
629 print('Could not find element '+str(what)+' by ' + str(how))
630 return False
631 return True
632
633
634 def is_alert_present(self):
635 try: self.driver.switch_to_alert()
636 except NoAlertPresentException as e: return False
637 return True
638
639
640 def close_alert_and_get_its_text(self):
641 try:
642 alert = self.driver.switch_to_alert()
643 alert_text = alert.text
644 if self.accept_next_alert:
645 alert.accept()
646 else:
647 alert.dismiss()
648 return alert_text
649 finally: self.accept_next_alert = True
650
651
652 def get_case_number(self):
653 """
654 what case are we running now
655 """
656 funcname = sys._getframe(1).f_code.co_name
657 caseno_str = funcname.strip('test_')
658 try:
659 caseno = int(caseno_str)
660 except ValueError:
661 print("get case number error! please check if func name is test_xxx")
662 return False
663 return caseno
664
665
666 def tearDown(self):
667 self.log.info(' END: CASE %s log \n\n' % str(self.case_no))
668 self.driver.quit()
669 self.assertEqual([], self.verificationErrors)
670
671
672###################################################################
673# #
674# PART III: test cases #
675# please refer to #
676# https://bugzilla.yoctoproject.org/tr_show_case.cgi?case_id=xxx #
677# #
678###################################################################
679
680# Note: to comply with the unittest framework, we call these test_xxx functions
681# from run_toastercases.py to avoid calling setUp() and tearDown() multiple times
682
683
684class toaster_cases(toaster_cases_base):
685 ##############
686 # CASE 901 #
687 ##############
688 def test_901(self):
689 # the reason why get_case_number is not in setUp function is that
690 # otherwise it returns "setUp" instead of "test_xxx"
691 self.case_no = self.get_case_number()
692 self.log.info(' CASE %s log: ' % str(self.case_no))
693 self.driver.maximize_window()
694 self.driver.get(self.base_url)
695 # open all columns
696 self.driver.find_element_by_id("edit-columns-button").click()
697 # adding explicitly wait for chromedriver..-_-
698 self.browser_delay()
699 self.driver.find_element_by_id("started_on").click()
700 self.browser_delay()
701 self.driver.find_element_by_id("time").click()
702 self.driver.find_element_by_id("edit-columns-button").click()
703 # dict: {lint text name : actual class name}
704 table_head_dict = {'Outcome':'outcome', 'Recipe':'target', 'Machine':'machine', 'Started on':'started_on', 'Completed on':'completed_on', \
705 'Errors':'errors_no', 'Warnings':'warnings_no', 'Time':'time'}
706 for key in table_head_dict:
707 try:
708 self.driver.find_element_by_link_text(key).click()
709 except Exception as e:
710 self.log.error("%s cannot be found on page" % key)
711 raise
712 column_list = self.get_table_column_text("class", table_head_dict[key])
713 # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
714 # the point is, after another click, it should be another order
715 if is_list_inverted(column_list):
716 self.driver.find_element_by_link_text(key).click()
717 column_list = self.get_table_column_text("class", table_head_dict[key])
718 self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
719 else:
720 self.assertTrue(is_list_sequenced(column_list), msg=("%s column not sequenced" % key))
721 self.driver.find_element_by_link_text(key).click()
722 column_list = self.get_table_column_text("class", table_head_dict[key])
723 self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
724 self.log.info("case passed")
725
726
727 ##############
728 # CASE 902 #
729 ##############
730 def test_902(self):
731 self.case_no = self.get_case_number()
732 self.log.info(' CASE %s log: ' % str(self.case_no))
733 self.driver.maximize_window()
734 self.driver.get(self.base_url)
735 # Could add more test patterns here in the future. Also, could search some items other than target column in future..
736 patterns = ["minimal", "sato"]
737 for pattern in patterns:
738 ori_target_column_texts = self.get_table_column_text("class", "target")
739 print(ori_target_column_texts)
740 self.driver.find_element_by_id("search").clear()
741 self.driver.find_element_by_id("search").send_keys(pattern)
742 self.driver.find_element_by_id("search-button").click()
743 new_target_column_texts = self.get_table_column_text("class", "target")
744 # if nothing found, we still count it as "pass"
745 if new_target_column_texts:
746 for text in new_target_column_texts:
747 self.assertTrue(text.find(pattern), msg=("%s item doesn't exist " % pattern))
748 self.driver.find_element_by_css_selector("i.icon-remove").click()
749 target_column_texts = self.get_table_column_text("class", "target")
750 self.assertTrue(ori_target_column_texts == target_column_texts, msg=("builds changed after operations"))
751
752
753 ##############
754 # CASE 903 #
755 ##############
756 def test_903(self):
757 self.case_no = self.get_case_number()
758 self.log.info(' CASE %s log: ' % str(self.case_no))
759 self.driver.maximize_window()
760 self.driver.get(self.base_url)
761 # when opening a new page, "started_on" is not displayed by default
762 self.driver.find_element_by_id("edit-columns-button").click()
763 # currently all the delay are for chrome driver -_-
764 self.browser_delay()
765 self.driver.find_element_by_id("started_on").click()
766 self.driver.find_element_by_id("edit-columns-button").click()
767 # step 4
768 items = ["Outcome", "Completed on", "Started on"]
769 for item in items:
770 try:
771 temp_element = self.find_element_by_text_in_table('otable', item)
772 # this is how we find "filter icon" in the same level as temp_element(where "a" means clickable, "i" means icon)
773 self.assertTrue(temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']"))
774 except Exception as e:
775 self.assertFalse(True, msg=(" %s cannot be found! %s" % (item, e)))
776 raise
777 # step 5-6
778 temp_element = self.find_element_by_link_text_in_table('otable', 'Outcome')
779 temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
780 self.browser_delay()
781 # the 2nd option, whatever it is
782 self.driver.find_element_by_xpath("(//input[@name='filter'])[2]").click()
783 # click "Apply" button
784 self.driver.find_element_by_xpath("//*[@id='filter_outcome']//*[text()='Apply']").click()
785 # save screen here
786 time.sleep(1)
787 self.save_screenshot(screenshot_type='selenium', append_name='step5')
788 temp_element = self.find_element_by_link_text_in_table('otable', 'Completed on')
789 temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
790 self.browser_delay()
791 self.driver.find_element_by_xpath("//*[@id='filter_completed_on']//*[text()='Apply']").click()
792 # save screen here to compare to previous one
793 # please note that for chrome driver, need a little break before saving
794 # screen here -_-
795 self.browser_delay()
796 self.save_screenshot(screenshot_type='selenium', append_name='step6')
797 self.driver.find_element_by_id("search").clear()
798 self.driver.find_element_by_id("search").send_keys("core-image")
799 self.driver.find_element_by_id("search-button").click()
800
801
802 ##############
803 # CASE 904 #
804 ##############
805 def test_904(self):
806 self.case_no = self.get_case_number()
807 self.log.info(' CASE %s log: ' % str(self.case_no))
808 self.driver.maximize_window()
809 self.driver.get(self.base_url)
810 self.driver.find_element_by_partial_link_text("core-image").click()
811 self.driver.find_element_by_link_text("Tasks").click()
812 self.table_name = 'otable'
813 # This is how we find the "default" rows-number!
814 rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
815 print(rows_displayed)
816 self.assertTrue(self.get_table_element(self.table_name, rows_displayed), msg=("not enough rows displayed"))
817 self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1), \
818 msg=("more rows displayed than expected"))
819 # Search text box background text is "Search tasks"
820 self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search tasks']"),\
821 msg=("background text doesn't exist"))
822
823 self.driver.find_element_by_id("search").clear()
824 self.driver.find_element_by_id("search").send_keys("busybox")
825 self.driver.find_element_by_id("search-button").click()
826 self.browser_delay()
827 self.save_screenshot(screenshot_type='selenium', append_name='step5')
828 self.driver.find_element_by_css_selector("i.icon-remove").click()
829 # Save screen here
830 self.save_screenshot(screenshot_type='selenium', append_name='step5_2')
831 self.driver.find_element_by_id("edit-columns-button").click()
832 self.driver.find_element_by_id("cpu_used").click()
833 self.driver.find_element_by_id("disk_io").click()
834 self.driver.find_element_by_id("recipe_version").click()
835 self.driver.find_element_by_id("time_taken").click()
836 self.driver.find_element_by_id("edit-columns-button").click()
837 # The operation is the same as case901
838 # dict: {lint text name : actual class name}
839 table_head_dict = {'Order':'order', 'Recipe':'recipe_name', 'Task':'task_name', 'Executed':'executed', \
840 'Outcome':'outcome', 'Cache attempt':'cache_attempt', 'Time (secs)':'time_taken', 'CPU usage':'cpu_used', \
841 'Disk I/O (ms)':'disk_io'}
842 for key in table_head_dict:
843 # This is tricky here: we are doing so because there may be more than 1
844 # same-name link_text in one page. So we only find element inside the table
845 self.find_element_by_link_text_in_table(self.table_name, key).click()
846 column_list = self.get_table_column_text("class", table_head_dict[key])
847 # after 1st click, the list should be either sequenced or inverted, but we don't have a "default order" here
848 # the point is, after another click, it should be another order
849 # the first case is special:this means every item in column_list is the same, so
850 # after one click, either sequenced or inverted will be fine
851 if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
852 or (not column_list) :
853 self.find_element_by_link_text_in_table(self.table_name, key).click()
854 column_list = self.get_table_column_text("class", table_head_dict[key])
855 self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list), \
856 msg=("%s column not in any order" % key))
857 elif is_list_inverted(column_list):
858 self.find_element_by_link_text_in_table(self.table_name, key).click()
859 column_list = self.get_table_column_text("class", table_head_dict[key])
860 self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
861 else:
862 self.assertTrue(is_list_sequenced(column_list), msg=("%s column not in order" % key))
863 self.find_element_by_link_text_in_table(self.table_name, key).click()
864 column_list = self.get_table_column_text("class", table_head_dict[key])
865 self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
866 # step 8-10
867 # filter dict: {link text name : filter table name in xpath}
868 filter_dict = {'Executed':'filter_executed', 'Outcome':'filter_outcome', 'Cache attempt':'filter_cache_attempt'}
869 for key in filter_dict:
870 temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
871 # find the filter icon besides it.
872 # And here we must have break (1 sec) to get the popup stuff
873 temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
874 self.browser_delay()
875 avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
876 for number in range(0, len(avail_options)):
877 avail_options[number].click()
878 self.browser_delay()
879 # click "Apply"
880 self.driver.find_element_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@type='submit']").click()
881 # insert screen capture here
882 self.browser_delay()
883 self.save_screenshot(screenshot_type='selenium', append_name='step8')
884 # after the last option was clicked, we don't need operation below anymore
885 if number < len(avail_options)-1:
886 try:
887 temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
888 temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
889 avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
890 except:
891 print("in exception")
892 self.find_element_by_text("Show all tasks").click()
893# self.driver.find_element_by_xpath("//*[@id='searchform']/button[2]").click()
894 temp_element = self.find_element_by_link_text_in_table(self.table_name, key)
895 temp_element.find_element_by_xpath("..//*[@class='icon-filter filtered']").click()
896 avail_options = self.driver.find_elements_by_xpath("//*[@id='" + filter_dict[key] + "']//*[@name='filter'][not(@disabled)]")
897 self.browser_delay()
898 # step 11
899 for item in ['order', 'task_name', 'executed', 'outcome', 'recipe_name', 'recipe_version']:
900 try:
901 self.find_element_by_xpath_in_table(self.table_name, "./tbody/tr[1]/*[@class='" + item + "']/a").click()
902 except NoSuchElementException as e:
903 # let it go...
904 print('no item in the colum' + item)
905 # insert screen shot here
906 self.save_screenshot(screenshot_type='selenium', append_name='step11')
907 self.driver.back()
908 # step 12-14
909 # about test_dict: please refer to testcase 904 requirement step 12-14
910 test_dict = {
911 'Time':{
912 'class':'time_taken',
913 'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Time (secs)'],
914 'check_column_list':['cpu_used', 'cache_attempt', 'disk_io', 'order', 'recipe_version']
915 },
916 'CPU usage':{
917 'class':'cpu_used',
918 'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'CPU usage'],
919 'check_column_list':['cache_attempt', 'disk_io', 'order', 'recipe_version', 'time_taken']
920 },
921 'Disk I/O':{
922 'class':'disk_io',
923 'check_head_list':['Recipe', 'Task', 'Executed', 'Outcome', 'Disk I/O (ms)'],
924 'check_column_list':['cpu_used', 'cache_attempt', 'order', 'recipe_version', 'time_taken']
925 }
926 }
927 for key in test_dict:
928 self.find_element_by_partial_link_text_in_table('nav', 'core-image').click()
929 self.find_element_by_link_text_in_table('nav', key).click()
930 head_list = self.get_table_head_text('otable')
931 for item in test_dict[key]['check_head_list']:
932 self.assertTrue(item in head_list, msg=("%s not in head row" % item))
933 column_list = self.get_table_column_text('class', test_dict[key]['class'])
934 self.assertTrue(is_list_inverted(column_list), msg=("%s column not inverted" % key))
935
936 self.driver.find_element_by_id("edit-columns-button").click()
937 for item2 in test_dict[key]['check_column_list']:
938 self.driver.find_element_by_id(item2).click()
939 self.driver.find_element_by_id("edit-columns-button").click()
940 # TBD: save screen here
941
942
943 ##############
944 # CASE 906 #
945 ##############
946 def test_906(self):
947 self.case_no = self.get_case_number()
948 self.log.info(' CASE %s log: ' % str(self.case_no))
949 self.driver.maximize_window()
950 self.driver.get(self.base_url)
951 self.driver.find_element_by_link_text("core-image-minimal").click()
952 self.find_element_by_link_text_in_table('nav', 'Packages').click()
953 # find "bash" in first column (Packages)
954 self.driver.find_element_by_xpath("//*[@id='otable']//td[1]//*[text()='bash']").click()
955 # save sceen here to observe...
956 # step 6
957 self.driver.find_element_by_partial_link_text("Generated files").click()
958 head_list = self.get_table_head_text('otable')
959 for item in ['File', 'Size']:
960 self.assertTrue(item in head_list, msg=("%s not in head row" % item))
961 c_list = self.get_table_column_text('class', 'path')
962 self.assertTrue(is_list_sequenced(c_list), msg=("column not in order"))
963 # step 7
964 self.driver.find_element_by_partial_link_text("Runtime dependencies").click()
965 # save sceen here to observe...
966 # note that here table name is not 'otable'
967 head_list = self.get_table_head_text('dependencies')
968 for item in ['Package', 'Version', 'Size']:
969 self.assertTrue(item in head_list, msg=("%s not in head row" % item))
970 c_list = self.get_table_column_text_by_column_number('dependencies', 1)
971 self.assertTrue(is_list_sequenced(c_list), msg=("list not in order"))
972 texts = ['Size', 'License', 'Recipe', 'Recipe version', 'Layer', \
973 'Layer commit']
974 self.failUnless(self.is_text_present(texts))
975
976
977 ##############
978 # CASE 910 #
979 ##############
980 def test_910(self):
981 self.case_no = self.get_case_number()
982 self.log.info(' CASE %s log: ' % str(self.case_no))
983 image_type="core-image-minimal"
984 test_package1="busybox"
985 test_package2="lib"
986 self.driver.maximize_window()
987 self.driver.get(self.base_url)
988 self.driver.find_element_by_link_text(image_type).click()
989 self.driver.find_element_by_link_text("Recipes").click()
990 self.save_screenshot(screenshot_type='selenium', append_name='step3')
991
992 self.table_name = 'otable'
993 # This is how we find the "default" rows-number!
994 rows_displayed = int(Select(self.driver.find_element_by_css_selector("select.pagesize")).first_selected_option.text)
995 print(rows_displayed)
996 self.assertTrue(self.get_table_element(self.table_name, rows_displayed))
997 self.assertFalse(self.get_table_element(self.table_name, rows_displayed + 1))
998
999 # Check the default table is sorted by Recipe
1000 tasks_column_count = len(self.driver.find_elements_by_xpath("/html/body/div[2]/div/div[2]/div[2]/table/tbody/tr/td[1]"))
1001 print(tasks_column_count)
1002 default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
1003 #print default_column_list
1004
1005 self.assertTrue(is_list_sequenced(default_column_list))
1006
1007 # Search text box background text is "Search recipes"
1008 self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
1009
1010 self.driver.find_element_by_id("search").clear()
1011 self.driver.find_element_by_id("search").send_keys(test_package1)
1012 self.driver.find_element_by_id("search-button").click()
1013 # Save screen here
1014 self.save_screenshot(screenshot_type='selenium', append_name='step4')
1015 self.driver.find_element_by_css_selector("i.icon-remove").click()
1016 self.save_screenshot(screenshot_type='selenium', append_name='step4_2')
1017
1018 self.driver.find_element_by_id("edit-columns-button").click()
1019 self.driver.find_element_by_id("depends_on").click()
1020 self.driver.find_element_by_id("layer_version__branch").click()
1021 self.driver.find_element_by_id("layer_version__layer__commit").click()
1022 self.driver.find_element_by_id("depends_by").click()
1023 self.driver.find_element_by_id("edit-columns-button").click()
1024
1025 self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
1026 # Check the inverted table by Recipe
1027 # Recipe doesn't have class name
1028 #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]"))
1029 #print inverted_tasks_column_count
1030 #inverted_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
1031 #print inverted_column_list
1032
1033 #self.driver.find_element_by_partial_link_text("zlib").click()
1034 #self.driver.back()
1035 #self.assertTrue(is_list_inverted(inverted_column_list))
1036 #self.find_element_by_link_text_in_table(self.table_name, 'Recipe').click()
1037
1038 table_head_dict = {'Recipe':'recipe__name', 'Recipe file':'recipe_file', 'Section':'recipe_section', \
1039 'License':'recipe_license', 'Layer':'layer_version__layer__name', \
1040 'Layer branch':'layer_version__branch'}
1041 for key in table_head_dict:
1042 self.find_element_by_link_text_in_table(self.table_name, key).click()
1043 column_list = self.get_table_column_text("class", table_head_dict[key])
1044 if (is_list_inverted(column_list) and is_list_sequenced(column_list)) \
1045 or (not column_list) :
1046 self.find_element_by_link_text_in_table(self.table_name, key).click()
1047 column_list = self.get_table_column_text("class", table_head_dict[key])
1048 self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
1049 self.driver.find_element_by_partial_link_text("acl").click()
1050 self.driver.back()
1051 self.assertTrue(is_list_sequenced(column_list) or is_list_inverted(column_list))
1052 # Search text box background text is "Search recipes"
1053 self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
1054 self.driver.find_element_by_id("search").clear()
1055 self.driver.find_element_by_id("search").send_keys(test_package2)
1056 self.driver.find_element_by_id("search-button").click()
1057 column_search_list = self.get_table_column_text("class", table_head_dict[key])
1058 self.assertTrue(is_list_sequenced(column_search_list) or is_list_inverted(column_search_list))
1059 self.driver.find_element_by_css_selector("i.icon-remove").click()
1060 elif is_list_inverted(column_list):
1061 self.find_element_by_link_text_in_table(self.table_name, key).click()
1062 column_list = self.get_table_column_text("class", table_head_dict[key])
1063 self.assertTrue(is_list_sequenced(column_list))
1064 self.driver.find_element_by_partial_link_text("acl").click()
1065 self.driver.back()
1066 self.assertTrue(is_list_sequenced(column_list))
1067 # Search text box background text is "Search recipes"
1068 self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
1069 self.driver.find_element_by_id("search").clear()
1070 self.driver.find_element_by_id("search").send_keys(test_package2)
1071 self.driver.find_element_by_id("search-button").click()
1072 column_search_list = self.get_table_column_text("class", table_head_dict[key])
1073 self.assertTrue(is_list_sequenced(column_search_list))
1074 self.driver.find_element_by_css_selector("i.icon-remove").click()
1075 else:
1076 self.assertTrue(is_list_sequenced(column_list), msg=("list %s not sequenced" % key))
1077 self.find_element_by_link_text_in_table(self.table_name, key).click()
1078 column_list = self.get_table_column_text("class", table_head_dict[key])
1079 self.assertTrue(is_list_inverted(column_list))
1080 try:
1081 self.driver.find_element_by_partial_link_text("acl").click()
1082 except:
1083 self.driver.find_element_by_partial_link_text("zlib").click()
1084 self.driver.back()
1085 self.assertTrue(is_list_inverted(column_list))
1086 # Search text box background text is "Search recipes"
1087 self.assertTrue(self.driver.find_element_by_xpath("//*[@id='searchform']/*[@placeholder='Search recipes']"))
1088 self.driver.find_element_by_id("search").clear()
1089 self.driver.find_element_by_id("search").send_keys(test_package2)
1090 self.driver.find_element_by_id("search-button").click()
1091 column_search_list = self.get_table_column_text("class", table_head_dict[key])
1092 #print column_search_list
1093 self.assertTrue(is_list_inverted(column_search_list))
1094 self.driver.find_element_by_css_selector("i.icon-remove").click()
1095
1096 # Bug 5919
1097 for key in table_head_dict:
1098 print(key)
1099 self.find_element_by_link_text_in_table(self.table_name, key).click()
1100 self.driver.find_element_by_id("edit-columns-button").click()
1101 self.driver.find_element_by_id(table_head_dict[key]).click()
1102 self.driver.find_element_by_id("edit-columns-button").click()
1103 self.browser_delay()
1104 # After hide the column, the default table should be sorted by Recipe
1105 tasks_column_count = len(self.driver.find_elements_by_partial_link_text("acl"))
1106 #print tasks_column_count
1107 default_column_list = self.get_table_column_text_by_column_number(self.table_name, 1)
1108 #print default_column_list
1109 self.assertTrue(is_list_sequenced(default_column_list))
1110
1111 self.driver.find_element_by_id("edit-columns-button").click()
1112 self.driver.find_element_by_id("recipe_file").click()
1113 self.driver.find_element_by_id("recipe_section").click()
1114 self.driver.find_element_by_id("recipe_license").click()
1115 self.driver.find_element_by_id("layer_version__layer__name").click()
1116 self.driver.find_element_by_id("edit-columns-button").click()
1117
1118
1119 ##############
1120 # CASE 911 #
1121 ##############
1122 def test_911(self):
1123 self.case_no = self.get_case_number()
1124 self.log.info(' CASE %s log: ' % str(self.case_no))
1125 self.driver.maximize_window()
1126 self.driver.get(self.base_url)
1127 self.driver.find_element_by_link_text("core-image-minimal").click()
1128 self.find_element_by_link_text_in_table('nav', 'Recipes').click()
1129 # step 3-5
1130 self.driver.find_element_by_id("search").clear()
1131 self.driver.find_element_by_id("search").send_keys("lib")
1132 self.driver.find_element_by_id("search-button").click()
1133 # save screen here for observation
1134 self.save_screenshot(screenshot_type='selenium', append_name='step5')
1135 # step 6
1136 self.driver.find_element_by_css_selector("i.icon-remove").click()
1137 self.driver.find_element_by_id("search").clear()
1138 # we deliberately want "no result" here
1139 self.driver.find_element_by_id("search").send_keys("no such input")
1140 self.driver.find_element_by_id("search-button").click()
1141 try:
1142 self.find_element_by_text("Show all recipes").click()
1143 except:
1144 self.fail(msg='Could not identify blank page elements')
1145
1146 ##############
1147 # CASE 912 #
1148 ##############
1149 def test_912(self):
1150 self.case_no = self.get_case_number()
1151 self.log.info(' CASE %s log: ' % str(self.case_no))
1152 self.driver.maximize_window()
1153 self.driver.get(self.base_url)
1154 self.driver.find_element_by_link_text("core-image-minimal").click()
1155 self.find_element_by_link_text_in_table('nav', 'Recipes').click()
1156 # step 3
1157 head_list = self.get_table_head_text('otable')
1158 for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
1159 self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
1160 self.driver.find_element_by_id("edit-columns-button").click()
1161 self.driver.find_element_by_id("depends_on").click()
1162 self.driver.find_element_by_id("layer_version__branch").click()
1163 self.driver.find_element_by_id("layer_version__layer__commit").click()
1164 self.driver.find_element_by_id("depends_by").click()
1165 self.driver.find_element_by_id("edit-columns-button").click()
1166 # check if columns selected above is shown
1167 check_list = ['Dependencies', 'Layer branch', 'Layer commit', 'Reverse dependencies']
1168 head_list = self.get_table_head_text('otable')
1169 time.sleep(2)
1170 print(head_list)
1171 for item in check_list:
1172 self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
1173 # un-check 'em all
1174 self.driver.find_element_by_id("edit-columns-button").click()
1175 self.driver.find_element_by_id("depends_on").click()
1176 self.driver.find_element_by_id("layer_version__branch").click()
1177 self.driver.find_element_by_id("layer_version__layer__commit").click()
1178 self.driver.find_element_by_id("depends_by").click()
1179 self.driver.find_element_by_id("edit-columns-button").click()
1180 # don't exist any more
1181 head_list = self.get_table_head_text('otable')
1182 for item in check_list:
1183 self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
1184
1185
1186 ##############
1187 # CASE 913 #
1188 ##############
1189 def test_913(self):
1190 self.case_no = self.get_case_number()
1191 self.log.info(' CASE %s log: ' % str(self.case_no))
1192 self.driver.maximize_window()
1193 self.driver.get(self.base_url)
1194 self.driver.find_element_by_link_text("core-image-minimal").click()
1195 self.find_element_by_link_text_in_table('nav', 'Recipes').click()
1196 # step 3
1197 head_list = self.get_table_head_text('otable')
1198 for item in ['Recipe', 'Recipe version', 'Recipe file', 'Section', 'License', 'Layer']:
1199 self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
1200 # step 4
1201 self.driver.find_element_by_id("edit-columns-button").click()
1202 # save screen
1203 self.browser_delay()
1204 self.save_screenshot(screenshot_type='selenium', append_name='step4')
1205 self.driver.find_element_by_id("edit-columns-button").click()
1206
1207
1208 ##############
1209 # CASE 914 #
1210 ##############
1211 def test_914(self):
1212 self.case_no = self.get_case_number()
1213 self.log.info(' CASE %s log: ' % str(self.case_no))
1214 image_type="core-image-minimal"
1215 test_package1="busybox"
1216 test_package2="gdbm"
1217 test_package3="gettext-native"
1218 driver = self.driver
1219 driver.maximize_window()
1220 driver.get(self.base_url)
1221 driver.find_element_by_link_text(image_type).click()
1222 driver.find_element_by_link_text("Recipes").click()
1223 driver.find_element_by_link_text(test_package1).click()
1224
1225 self.table_name = 'information'
1226
1227 tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr/td[1]"))
1228 tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='"+self.table_name+"']/table/tbody/tr[1]/td"))
1229 print('rows: '+str(tasks_row_count))
1230 print('columns: '+str(tasks_column_count))
1231
1232 Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
1233 print ("Tasks_column=", Tasks_column)
1234
1235 key_tasks=["do_fetch", "do_unpack", "do_patch", "do_configure", "do_compile", "do_install", "do_package", "do_build"]
1236 i = 0
1237 while i < len(key_tasks):
1238 if key_tasks[i] not in Tasks_column:
1239 print ("Error! Missing key task: %s" % key_tasks[i])
1240 else:
1241 print ("%s is in tasks" % key_tasks[i])
1242 i = i + 1
1243
1244 if Tasks_column.index(key_tasks[0]) != 0:
1245 print ("Error! %s is not in the right position" % key_tasks[0])
1246 else:
1247 print ("%s is in right position" % key_tasks[0])
1248
1249 if Tasks_column[-1] != key_tasks[-1]:
1250 print ("Error! %s is not in the right position" % key_tasks[-1])
1251 else:
1252 print ("%s is in right position" % key_tasks[-1])
1253
1254 driver.find_element_by_partial_link_text("Packages (").click()
1255 packages_name = driver.find_element_by_partial_link_text("Packages (").text
1256 print(packages_name)
1257 packages_num = int(filter(str.isdigit, repr(packages_name)))
1258 print(packages_num)
1259
1260 #switch the table to show more than 10 rows at a time
1261 self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").click()
1262 Select(driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select")).select_by_value('150')
1263 self.driver.find_element_by_xpath("//*[@id='packages-built']/div[1]/div/select").send_keys(Keys.ENTER)
1264
1265 packages_row_count = len(driver.find_elements_by_xpath("//*[@id='otable']/tbody/tr/td[1]"))
1266 print(packages_row_count)
1267
1268 if packages_num != packages_row_count:
1269 print ("Error! The packages number is not correct")
1270 else:
1271 print ("The packages number is correct")
1272
1273 driver.find_element_by_partial_link_text("Build dependencies (").click()
1274 depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
1275 print(depends_name)
1276 depends_num = int(list(filter(str.isdigit, repr(depends_name))))
1277 print(depends_num)
1278
1279 if depends_num == 0:
1280 depends_message = repr(driver.find_element_by_css_selector("div.alert.alert-info").text)
1281 print(depends_message)
1282 if depends_message.find("has no build dependencies.") < 0:
1283 print ("Error! The message isn't expected.")
1284 else:
1285 print ("The message is expected")
1286 else:
1287 depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
1288 print(depends_row_count)
1289 if depends_num != depends_row_count:
1290 print ("Error! The dependent packages number is not correct")
1291 else:
1292 print ("The dependent packages number is correct")
1293
1294 driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
1295 rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
1296 print(rdepends_name)
1297 rdepends_num = int(filter(str.isdigit, repr(rdepends_name)))
1298 print(rdepends_num)
1299
1300 if rdepends_num == 0:
1301 rdepends_message = repr(driver.find_element_by_css_selector("#brought-in-by > div.alert.alert-info").text)
1302 print(rdepends_message)
1303 if rdepends_message.find("has no reverse build dependencies.") < 0:
1304 print ("Error! The message isn't expected.")
1305 else:
1306 print ("The message is expected")
1307 else:
1308 print ("The reverse dependent packages number is correct")
1309
1310 driver.find_element_by_link_text("Recipes").click()
1311 driver.find_element_by_link_text(test_package2).click()
1312 driver.find_element_by_partial_link_text("Packages (").click()
1313 driver.find_element_by_partial_link_text("Build dependencies (").click()
1314 driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
1315
1316
1317 driver.find_element_by_link_text("Recipes").click()
1318 driver.find_element_by_link_text(test_package3).click()
1319
1320 native_tasks_row_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr/td[1]"))
1321 native_tasks_column_count = len(driver.find_elements_by_xpath("//*[@id='information']/table/tbody/tr[1]/td"))
1322 print(native_tasks_row_count)
1323 print(native_tasks_column_count)
1324
1325 Native_Tasks_column = self.get_table_column_text_by_column_number(self.table_name, 2)
1326 print ("Native_Tasks_column=", Native_Tasks_column)
1327
1328 native_key_tasks=["do_fetch", "do_unpack", "do_patch", "do_configure", "do_compile", "do_install", "do_build"]
1329 i = 0
1330 while i < len(native_key_tasks):
1331 if native_key_tasks[i] not in Native_Tasks_column:
1332 print ("Error! Missing key task: %s" % native_key_tasks[i])
1333 else:
1334 print ("%s is in tasks" % native_key_tasks[i])
1335 i = i + 1
1336
1337 if Native_Tasks_column.index(native_key_tasks[0]) != 0:
1338 print ("Error! %s is not in the right position" % native_key_tasks[0])
1339 else:
1340 print ("%s is in right position" % native_key_tasks[0])
1341
1342 if Native_Tasks_column[-1] != native_key_tasks[-1]:
1343 print ("Error! %s is not in the right position" % native_key_tasks[-1])
1344 else:
1345 print ("%s is in right position" % native_key_tasks[-1])
1346
1347 driver.find_element_by_partial_link_text("Packages (").click()
1348 native_packages_name = driver.find_element_by_partial_link_text("Packages (").text
1349 print(native_packages_name)
1350 native_packages_num = int(filter(str.isdigit, repr(native_packages_name)))
1351 print(native_packages_num)
1352
1353 if native_packages_num != 0:
1354 print ("Error! Native task shouldn't have any packages.")
1355 else:
1356 native_package_message = repr(driver.find_element_by_css_selector("#packages-built > div.alert.alert-info").text)
1357 print(native_package_message)
1358 if native_package_message.find("does not build any packages.") < 0:
1359 print ("Error! The message for native task isn't expected.")
1360 else:
1361 print ("The message for native task is expected.")
1362
1363 driver.find_element_by_partial_link_text("Build dependencies (").click()
1364 native_depends_name = driver.find_element_by_partial_link_text("Build dependencies (").text
1365 print(native_depends_name)
1366 native_depends_num = int(filter(str.isdigit, repr(native_depends_name)))
1367 print(native_depends_num)
1368
1369 native_depends_row_count = len(driver.find_elements_by_xpath("//*[@id='dependencies']/table/tbody/tr/td[1]"))
1370 print(native_depends_row_count)
1371
1372 if native_depends_num != native_depends_row_count:
1373 print ("Error! The dependent packages number is not correct")
1374 else:
1375 print ("The dependent packages number is correct")
1376
1377 driver.find_element_by_partial_link_text("Reverse build dependencies (").click()
1378 native_rdepends_name = driver.find_element_by_partial_link_text("Reverse build dependencies (").text
1379 print(native_rdepends_name)
1380 native_rdepends_num = int(filter(str.isdigit, repr(native_rdepends_name)))
1381 print(native_rdepends_num)
1382
1383 native_rdepends_row_count = len(driver.find_elements_by_xpath("//*[@id='brought-in-by']/table/tbody/tr/td[1]"))
1384 print(native_rdepends_row_count)
1385
1386 if native_rdepends_num != native_rdepends_row_count:
1387 print ("Error! The reverse dependent packages number is not correct")
1388 else:
1389 print ("The reverse dependent packages number is correct")
1390
1391 driver.find_element_by_link_text("Recipes").click()
1392
1393
1394 ##############
1395 # CASE 915 #
1396 ##############
1397 def test_915(self):
1398 self.case_no = self.get_case_number()
1399 self.log.info(' CASE %s log: ' % str(self.case_no))
1400 self.driver.maximize_window()
1401 self.driver.get(self.base_url)
1402 self.driver.find_element_by_link_text("core-image-minimal").click()
1403 # step 3
1404 self.find_element_by_link_text_in_table('nav', 'Configuration').click()
1405 self.driver.find_element_by_link_text("BitBake variables").click()
1406 # step 4
1407 self.driver.find_element_by_id("search").clear()
1408 self.driver.find_element_by_id("search").send_keys("lib")
1409 self.driver.find_element_by_id("search-button").click()
1410 # save screen to see result
1411 self.browser_delay()
1412 self.save_screenshot(screenshot_type='selenium', append_name='step4')
1413 # step 5
1414 self.driver.find_element_by_css_selector("i.icon-remove").click()
1415 head_list = self.get_table_head_text('otable')
1416 print(head_list)
1417 print(len(head_list))
1418 self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
1419 msg=("head row contents wrong"))
1420 # step 8
1421 # search other string. and click "Variable" to re-sort, check if table
1422 # head is still the same
1423 self.driver.find_element_by_id("search").clear()
1424 self.driver.find_element_by_id("search").send_keys("poky")
1425 self.driver.find_element_by_id("search-button").click()
1426 self.find_element_by_link_text_in_table('otable', 'Variable').click()
1427 head_list = self.get_table_head_text('otable')
1428 self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
1429 msg=("head row contents wrong"))
1430 self.find_element_by_link_text_in_table('otable', 'Variable').click()
1431 head_list = self.get_table_head_text('otable')
1432 self.assertTrue(head_list == ['Variable', 'Value', 'Set in file', 'Description'], \
1433 msg=("head row contents wrong"))
1434
1435
1436 ##############
1437 # CASE 916 #
1438 ##############
1439 def test_916(self):
1440 self.case_no = self.get_case_number()
1441 self.log.info(' CASE %s log: ' % str(self.case_no))
1442 self.driver.maximize_window()
1443 self.driver.get(self.base_url)
1444 self.driver.find_element_by_link_text("core-image-minimal").click()
1445 # step 2-3
1446 self.find_element_by_link_text_in_table('nav', 'Configuration').click()
1447 self.driver.find_element_by_link_text("BitBake variables").click()
1448 variable_list = self.get_table_column_text('class', 'variable_name')
1449 self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
1450 # step 4
1451 self.find_element_by_link_text_in_table('otable', 'Variable').click()
1452 variable_list = self.get_table_column_text('class', 'variable_name')
1453 self.assertTrue(is_list_inverted(variable_list), msg=("list not inverted"))
1454 self.find_element_by_link_text_in_table('otable', 'Variable').click()
1455 # step 5
1456 # searching won't change the sequentiality
1457 self.driver.find_element_by_id("search").clear()
1458 self.driver.find_element_by_id("search").send_keys("lib")
1459 self.driver.find_element_by_id("search-button").click()
1460 variable_list = self.get_table_column_text('class', 'variable_name')
1461 self.assertTrue(is_list_sequenced(variable_list), msg=("list not in order"))
1462
1463
1464 ##############
1465 # CASE 923 #
1466 ##############
1467 def test_923(self):
1468 self.case_no = self.get_case_number()
1469 self.log.info(' CASE %s log: ' % str(self.case_no))
1470 self.driver.maximize_window()
1471 self.driver.get(self.base_url)
1472 # Step 2
1473 # default sequence in "Completed on" column is inverted
1474 c_list = self.get_table_column_text('class', 'completed_on')
1475 self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
1476 # step 3
1477 self.driver.find_element_by_id("edit-columns-button").click()
1478 self.driver.find_element_by_id("started_on").click()
1479 self.driver.find_element_by_id("time").click()
1480 self.driver.find_element_by_id("edit-columns-button").click()
1481 head_list = self.get_table_head_text('otable')
1482 for item in ['Outcome', 'Recipe', 'Machine', 'Started on', 'Completed on', 'Failed tasks', 'Errors', 'Warnings', 'Time', "Image files", "Project"]:
1483 self.failUnless(item in head_list, msg=item+' is missing from table head.')
1484
1485
1486 ##############
1487 # CASE 924 #
1488 ##############
1489 def test_924(self):
1490 self.case_no = self.get_case_number()
1491 self.log.info(' CASE %s log: ' % str(self.case_no))
1492 self.driver.maximize_window()
1493 self.driver.get(self.base_url)
1494 # Please refer to case 924 requirement
1495 # default sequence in "Completed on" column is inverted
1496 c_list = self.get_table_column_text('class', 'completed_on')
1497 self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
1498 # Step 4
1499 # click Errors , order in "Completed on" should be disturbed. Then hide
1500 # error column to check if order in "Completed on" can be restored
1501#THIS TEST IS NO LONGER VALID DUE TO DESIGN CHANGES. LEAVING IN PENDING UPDATES TO DESIGN
1502 #self.find_element_by_link_text_in_table('otable', 'Errors').click()
1503 #self.driver.find_element_by_id("edit-columns-button").click()
1504 #self.driver.find_element_by_id("errors_no").click()
1505 #self.driver.find_element_by_id("edit-columns-button").click()
1506 # Note: without time.sleep here, there'll be unpredictable error..TBD
1507 time.sleep(1)
1508 c_list = self.get_table_column_text('class', 'completed_on')
1509 self.assertTrue(is_list_inverted(c_list), msg=("list not inverted"))
1510
1511
1512 ##############
1513 # CASE 940 #
1514 ##############
1515 def test_940(self):
1516 self.case_no = self.get_case_number()
1517 self.log.info(' CASE %s log: ' % str(self.case_no))
1518 self.driver.maximize_window()
1519 self.driver.get(self.base_url)
1520 self.driver.find_element_by_link_text("core-image-minimal").click()
1521 # Step 2-3
1522 self.find_element_by_link_text_in_table('nav', 'Packages').click()
1523 check_head_list = ['Package', 'Package version', 'Size', 'Recipe']
1524 head_list = self.get_table_head_text('otable')
1525 self.assertTrue(head_list == check_head_list, msg=("head row not as expected"))
1526 # Step 4
1527 # pulldown menu
1528 option_ids = ['recipe__layer_version__layer__name', 'recipe__layer_version__branch', \
1529 'recipe__layer_version__layer__commit', 'license', 'recipe__version']
1530 self.driver.find_element_by_id("edit-columns-button").click()
1531 for item in option_ids:
1532 if not self.driver.find_element_by_id(item).is_selected():
1533 self.driver.find_element_by_id(item).click()
1534 self.driver.find_element_by_id("edit-columns-button").click()
1535 # save screen here to observe that 'Package' and 'Package version' is
1536 # not selectable
1537 self.browser_delay()
1538 self.save_screenshot(screenshot_type='selenium', append_name='step4')
1539
1540
1541 ##############
1542 # CASE 941 #
1543 ##############
1544 def test_941(self):
1545 self.case_no = self.get_case_number()
1546 self.log.info(' CASE %s log: ' % str(self.case_no))
1547 self.driver.maximize_window()
1548 self.driver.get(self.base_url)
1549 self.driver.find_element_by_link_text("core-image-minimal").click()
1550 # Step 2-3
1551 self.find_element_by_link_text_in_table('nav', 'Packages').click()
1552 # column -- Package
1553 column_list = self.get_table_column_text_by_column_number('otable', 1)
1554 self.assertTrue(is_list_sequenced(column_list), msg=("list not in order"))
1555 self.find_element_by_link_text_in_table('otable', 'Size').click()
1556
1557
1558 ##############
1559 # CASE 942 #
1560 ##############
1561 def test_942(self):
1562 self.case_no = self.get_case_number()
1563 self.log.info(' CASE %s log: ' % str(self.case_no))
1564 self.driver.maximize_window()
1565 self.driver.get(self.base_url)
1566 self.driver.find_element_by_link_text("core-image-minimal").click()
1567 self.driver.find_element_by_link_text("Packages").click()
1568 #get initial table header
1569 head_list = self.get_table_head_text('otable')
1570 #remove the Recipe column from table header
1571 self.driver.find_element_by_id("edit-columns-button").click()
1572 self.driver.find_element_by_id("recipe__name").click()
1573 self.driver.find_element_by_id("edit-columns-button").click()
1574 #get modified table header
1575 new_head = self.get_table_head_text('otable')
1576 self.assertTrue(head_list > new_head)
1577
1578 ##############
1579 # CASE 943 #
1580 ##############
1581 def test_943(self):
1582 self.case_no = self.get_case_number()
1583 self.log.info(' CASE %s log: ' % str(self.case_no))
1584 self.driver.maximize_window()
1585 self.driver.get(self.base_url)
1586 self.driver.find_element_by_link_text("core-image-minimal").click()
1587 self.driver.find_element_by_link_text("Packages").click()
1588 #search for the "bash" package -> this should definitely be present
1589 self.driver.find_element_by_id("search").clear()
1590 self.driver.find_element_by_id("search").send_keys("bash")
1591 self.driver.find_element_by_id("search-button").click()
1592 #check for the search result message "XX packages found"
1593 self.assertTrue(self.is_text_present("packages found"), msg=("no packages found text"))
1594
1595
1596 ##############
1597 # CASE 944 #
1598 ##############
1599 def test_944(self):
1600 self.case_no = self.get_case_number()
1601 self.log.info(' CASE %s log: ' % str(self.case_no))
1602 self.driver.maximize_window()
1603 self.driver.get(self.base_url)
1604 self.driver.find_element_by_link_text("core-image-minimal").click()
1605 # step 1: test Recipes page stuff
1606 self.driver.find_element_by_link_text("Recipes").click()
1607 # for these 3 items, default status is not-checked
1608 self.driver.find_element_by_id("edit-columns-button").click()
1609 self.driver.find_element_by_id("layer_version__branch").click()
1610 self.driver.find_element_by_id("layer_version__layer__commit").click()
1611 self.driver.find_element_by_id("edit-columns-button").click()
1612 # otable is the recipes table here
1613 otable_head_text = self.get_table_head_text('otable')
1614 for item in ["Layer", "Layer branch", "Layer commit"]:
1615 self.failIf(item not in otable_head_text, msg=item+' not in table head.')
1616 # click the fist recipe, whatever it is
1617 self.get_table_element("otable", 1, 1).click()
1618 self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit", "Recipe file"]), \
1619 msg=("text not in web page"))
1620
1621 # step 2: test Packages page stuff. almost same as above
1622 self.driver.back()
1623 self.browser_delay()
1624 self.driver.find_element_by_link_text("Packages").click()
1625 self.driver.find_element_by_id("edit-columns-button").click()
1626 self.driver.find_element_by_id("recipe__layer_version__layer__name").click()
1627 self.driver.find_element_by_id("recipe__layer_version__branch").click()
1628 self.driver.find_element_by_id("recipe__layer_version__layer__commit").click()
1629 self.driver.find_element_by_id("edit-columns-button").click()
1630 otable_head_text = self.get_table_head_text("otable")
1631 for item in ["Layer", "Layer branch", "Layer commit"]:
1632 self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
1633 # click the fist recipe, whatever it is
1634 self.get_table_element("otable", 1, 1).click()
1635 self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
1636 msg=("text not in web page"))
1637
1638 # step 3: test Packages core-image-minimal(images) stuff. almost same as above. Note when future element-id changes...
1639 self.driver.back()
1640 self.driver.find_element_by_link_text("core-image-minimal").click()
1641 self.driver.find_element_by_id("edit-columns-button").click()
1642 self.driver.find_element_by_id("layer_name").click()
1643 self.driver.find_element_by_id("layer_branch").click()
1644 self.driver.find_element_by_id("layer_commit").click()
1645 self.driver.find_element_by_id("edit-columns-button").click()
1646 otable_head_text = self.get_table_head_text("otable")
1647 for item in ["Layer", "Layer branch", "Layer commit"]:
1648 self.assertFalse(item not in otable_head_text, msg=("item %s should be in head row" % item))
1649 # click the fist recipe, whatever it is
1650 self.get_table_element("otable", 1, 1).click()
1651 self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
1652 msg=("text not in web page"))
1653
1654 # step 4: check Configuration page
1655 self.driver.back()
1656 self.driver.find_element_by_link_text("Configuration").click()
1657 otable_head_text = self.get_table_head_text()
1658 self.assertTrue(self.is_text_present(["Layer", "Layer branch", "Layer commit"]), \
1659 msg=("text not in web page"))
1660
1661
1662 ##############
1663 # CASE 945 #
1664 ##############
1665 def test_945(self):
1666 self.case_no = self.get_case_number()
1667 self.log.info(' CASE %s log: ' % str(self.case_no))
1668 self.driver.maximize_window()
1669 for item in ["Packages", "Recipes", "Tasks"]:
1670 self.driver.get(self.base_url)
1671 self.driver.find_element_by_link_text("core-image-minimal").click()
1672 self.driver.find_element_by_link_text(items).click()
1673
1674 # this may be page specific. If future page content changes, try to replace it with new xpath
1675 xpath_showrows = "/html/body/div[4]/div/div/div[2]/div[2]/div[2]/div/div/div[2]/select"
1676 xpath_table = "html/body/div[4]/div/div/div[2]/div[2]/table/tbody"#"id=('otable')/tbody"
1677 self.driver.find_element_by_xpath(xpath_showrows).click()
1678 rows_displayed = int(self.driver.find_element_by_xpath(xpath_showrows + "/option[2]").text)
1679
1680 # 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
1681 # Sure we can use driver.get(url) to refresh page, but since page will vary, we use click link text here
1682 self.driver.find_element_by_link_text(items).click()
1683 Select(self.driver.find_element_by_css_selector("select.pagesize")).select_by_visible_text(str(rows_displayed))
1684 self.failUnless(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed) +"]"))
1685 self.failIf(self.is_element_present(By.XPATH, xpath_table + "/tr[" + str(rows_displayed+1) +"]"))
1686
1687 # click 1st package, then go back to check if it's still those rows shown.
1688 self.driver.find_element_by_xpath(xpath_otable + "/tr[1]/td[1]/a").click()
1689 time.sleep(3)
1690 self.driver.find_element_by_link_text(item).click()
1691 self.assertTrue(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected) +"]"),\
1692 msg=("Row %d should exist" %option_tobeselected))
1693 self.assertFalse(self.is_element_present(By.XPATH, xpath_otable + "/tr[" + str(option_tobeselected+1) +"]"),\
1694 msg=("Row %d should not exist" %(option_tobeselected+1)))
1695
1696
1697
1698 ##############
1699 # CASE 946 #
1700 ##############
1701 def test_946(self):
1702 self.case_no = self.get_case_number()
1703 self.log.info(' CASE %s log: ' % str(self.case_no))
1704 self.driver.maximize_window()
1705 self.driver.get(self.base_url)
1706 self.driver.find_element_by_link_text("core-image-minimal").click()
1707 self.driver.find_element_by_link_text("Configuration").click()
1708 # step 3-4
1709 check_list = ["Summary", "BitBake variables"]
1710 for item in check_list:
1711 if not self.is_element_present(how=By.LINK_TEXT, what=item):
1712 self.log.error("%s not found" %item)
1713 if not self.is_text_present(['Layers', 'Layer', 'Layer branch', 'Layer commit']):
1714 self.log.error("text not found")
1715 # step 5
1716 self.driver.find_element_by_link_text("BitBake variables").click()
1717 if not self.is_text_present(['Variable', 'Value', 'Set in file', 'Description']):
1718 self.log.error("text not found")
1719 # This may be unstable because it's page-specific
1720 # step 6: this is how we find filter beside "Set in file"
1721 temp_element = self.find_element_by_text_in_table('otable', "Set in file")
1722 temp_element.find_element_by_xpath("..//*/a/i[@class='icon-filter filtered']").click()
1723 self.browser_delay()
1724 self.driver.find_element_by_xpath("(//input[@name='filter'])[3]").click()
1725 btns = self.driver.find_elements_by_css_selector("button.btn.btn-primary")
1726 for btn in btns:
1727 try:
1728 btn.click()
1729 break
1730 except:
1731 pass
1732 # save screen here
1733 self.browser_delay()
1734 self.save_screenshot(screenshot_type='selenium', append_name='step6')
1735 self.driver.find_element_by_id("edit-columns-button").click()
1736 # save screen here
1737 # step 7
1738 # we should manually check the step 6-8 result using screenshot
1739 self.browser_delay()
1740 self.save_screenshot(screenshot_type='selenium', append_name='step7')
1741 self.driver.find_element_by_id("edit-columns-button").click()
1742 # step 9
1743 # click the 1st item, no matter what it is
1744 self.driver.find_element_by_xpath("//*[@id='otable']/tbody/tr[1]/td[1]/a").click()
1745 # give it 1 sec so the pop-up becomes the "active_element"
1746 time.sleep(1)
1747 element = self.driver.switch_to.active_element
1748 check_list = ['Order', 'Configuration file', 'Operation', 'Line number']
1749 for item in check_list:
1750 if item not in element.text:
1751 self.log.error("%s not found" %item)
1752 # any better way to close this pop-up? ... TBD
1753 element.find_element_by_class_name("close").click()
1754 # step 10 : need to manually check "Yocto Manual" in saved screen
1755 self.driver.find_element_by_css_selector("i.icon-share.get-info").click()
1756 # save screen here
1757 time.sleep(5)
1758 self.save_screenshot(screenshot_type='native', append_name='step10')
1759
1760
1761 ##############
1762 # CASE 947 #
1763 ##############
1764 def test_947(self):
1765 self.case_no = self.get_case_number()
1766 self.log.info(' CASE %s log: ' % str(self.case_no))
1767 self.driver.maximize_window()
1768 self.driver.get(self.base_url)
1769 self.driver.find_element_by_link_text("core-image-minimal").click()
1770 self.find_element_by_link_text_in_table('nav', 'Configuration').click()
1771 # step 2
1772 self.driver.find_element_by_link_text("BitBake variables").click()
1773 # step 3
1774 def xpath_option(column_name):
1775 # return xpath of options under "Edit columns" button
1776 return self.shortest_xpath('id', 'navTab') + self.shortest_xpath('id', 'editcol') \
1777 + self.shortest_xpath('id', column_name)
1778 self.driver.find_element_by_id('edit-columns-button').click()
1779 # by default, option "Description" and "Set in file" were checked
1780 self.driver.find_element_by_xpath(xpath_option('description')).click()
1781 self.driver.find_element_by_xpath(xpath_option('file')).click()
1782 self.driver.find_element_by_id('edit-columns-button').click()
1783 check_list = ['Description', 'Set in file']
1784 head_list = self.get_table_head_text('otable')
1785 for item in check_list:
1786 self.assertFalse(item in head_list, msg=("item %s should not be in head row" % item))
1787 # check these 2 options and verify again
1788 self.driver.find_element_by_id('edit-columns-button').click()
1789 self.driver.find_element_by_xpath(xpath_option('description')).click()
1790 self.driver.find_element_by_xpath(xpath_option('file')).click()
1791 self.driver.find_element_by_id('edit-columns-button').click()
1792 head_list = self.get_table_head_text('otable')
1793 for item in check_list:
1794 self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
1795
1796
1797 ##############
1798 # CASE 948 #
1799 ##############
1800 def test_948(self):
1801 self.case_no = self.get_case_number()
1802 self.log.info(' CASE %s log: ' % str(self.case_no))
1803 self.driver.maximize_window()
1804 self.driver.get(self.base_url)
1805 self.driver.find_element_by_link_text("core-image-minimal").click()
1806 self.find_element_by_link_text_in_table('nav', 'Configuration').click()
1807 self.driver.find_element_by_link_text("BitBake variables").click()
1808 #get number of variables visible by default
1809 number_before_search = self.driver.find_element_by_class_name('page-header').text
1810 # search for a while...
1811 self.driver.find_element_by_id("search").clear()
1812 self.driver.find_element_by_id("search").send_keys("BB")
1813 self.driver.find_element_by_id("search-button").click()
1814 #get number of variables visible after search
1815 number_after_search = self.driver.find_element_by_class_name('page-header').text
1816 self.assertTrue(number_before_search > number_after_search, msg=("items should be less after search"))
1817
1818
1819 ##############
1820 # CASE 949 #
1821 ##############
1822 def test_949(self):
1823 self.case_no = self.get_case_number()
1824 self.log.info(' CASE %s log: ' % str(self.case_no))
1825 self.driver.maximize_window()
1826 self.driver.get(self.base_url)
1827 self.driver.find_element_by_link_text("core-image-minimal").click()
1828 self.find_element_by_link_text_in_table('nav', 'core-image-minimal').click()
1829 # step 3
1830 try:
1831 self.driver.find_element_by_partial_link_text("Packages included")
1832 self.driver.find_element_by_partial_link_text("Directory structure")
1833 except Exception as e:
1834 self.log.error(e)
1835 self.assertFalse(True)
1836 # step 4
1837 head_list = self.get_table_head_text('otable')
1838 for item in ['Package', 'Package version', 'Size', 'Dependencies', 'Reverse dependencies', 'Recipe']:
1839 self.assertTrue(item in head_list, msg=("item %s not in head row" % item))
1840 # step 5-6
1841 self.driver.find_element_by_id("edit-columns-button").click()
1842 selectable_class = 'checkbox'
1843 # minimum-table : means unselectable items
1844 unselectable_class = 'checkbox muted'
1845 selectable_check_list = ['Dependencies', 'Layer', 'Layer branch', 'Layer commit', \
1846 'License', 'Recipe', 'Recipe version', 'Reverse dependencies', \
1847 'Size', 'Size over total (%)']
1848 unselectable_check_list = ['Package', 'Package version']
1849 selectable_list = list()
1850 unselectable_list = list()
1851 selectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + selectable_class + "']")
1852 unselectable_elements = self.driver.find_elements_by_xpath("//*[@id='editcol']//*[@class='" + unselectable_class + "']")
1853 for element in selectable_elements:
1854 selectable_list.append(element.text)
1855 for element in unselectable_elements:
1856 unselectable_list.append(element.text)
1857 # check them
1858 for item in selectable_check_list:
1859 self.assertTrue(item in selectable_list, msg=("%s not found in dropdown menu" % item))
1860 for item in unselectable_check_list:
1861 self.assertTrue(item in unselectable_list, msg=("%s not found in dropdown menu" % item))
1862 self.driver.find_element_by_id("edit-columns-button").click()
1863 # step 7
1864 self.driver.find_element_by_partial_link_text("Directory structure").click()
1865 head_list = self.get_table_head_text('dirtable')
1866 for item in ['Directory / File', 'Symbolic link to', 'Source package', 'Size', 'Permissions', 'Owner', 'Group']:
1867 self.assertTrue(item in head_list, msg=("%s not found in Directory structure table head" % item))
1868
1869 ##############
1870 # CASE 950 #
1871 ##############
1872 def test_950(self):
1873 self.case_no = self.get_case_number()
1874 self.log.info(' CASE %s log: ' % str(self.case_no))
1875 self.driver.maximize_window()
1876 self.driver.get(self.base_url)
1877 # step3&4: so far we're not sure if there's "successful build" or "failed
1878 # build".If either of them doesn't exist, we can still go on other steps
1879 check_list = ['Configuration', 'Tasks', 'Recipes', 'Packages', 'Time', 'CPU usage', 'Disk I/O']
1880 has_successful_build = 1
1881 has_failed_build = 1
1882 try:
1883 pass_icon = self.driver.find_element_by_xpath("//*[@class='icon-ok-sign success']")
1884 except Exception:
1885 self.log.info("no successful build exists")
1886 has_successful_build = 0
1887 pass
1888 if has_successful_build:
1889 pass_icon.click()
1890 # save screen here to check if it matches requirement.
1891 self.browser_delay()
1892 self.save_screenshot(screenshot_type='selenium', append_name='step3_1')
1893 for item in check_list:
1894 try:
1895 self.find_element_by_link_text_in_table('nav', item)
1896 except Exception:
1897 self.assertFalse(True, msg=("link %s cannot be found in the page" % item))
1898 # step 6
1899 check_list_2 = ['Packages included', 'Total package size', \
1900 'License manifest', 'Image files']
1901 self.assertTrue(self.is_text_present(check_list_2), msg=("text not in web page"))
1902 self.driver.back()
1903 try:
1904 fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
1905 except Exception:
1906 has_failed_build = 0
1907 self.log.info("no failed build exists")
1908 pass
1909 if has_failed_build:
1910 fail_icon.click()
1911 # save screen here to check if it matches requirement.
1912 self.browser_delay()
1913 self.save_screenshot(screenshot_type='selenium', append_name='step3_2')
1914 for item in check_list:
1915 try:
1916 self.find_element_by_link_text_in_table('nav', item)
1917 except Exception:
1918 self.assertFalse(True, msg=("link %s cannot be found in the page" % item))
1919 # step 7 involved
1920 check_list_3 = ['Machine', 'Distro', 'Layers', 'Total number of tasks', 'Tasks executed', \
1921 'Tasks not executed', 'Reuse', 'Recipes built', 'Packages built']
1922 self.assertTrue(self.is_text_present(check_list_3), msg=("text not in web page"))
1923 self.driver.back()
1924
1925
1926 ##############
1927 # CASE 951 #
1928 ##############
1929 def test_951(self):
1930 self.case_no = self.get_case_number()
1931 self.log.info(' CASE %s log: ' % str(self.case_no))
1932 self.driver.maximize_window()
1933 self.driver.get(self.base_url)
1934 # currently test case itself isn't responsible for creating "1 successful and
1935 # 1 failed build"
1936 has_successful_build = 1
1937 has_failed_build = 1
1938 try:
1939 fail_icon = self.driver.find_element_by_xpath("//*[@class='icon-minus-sign error']")
1940 except Exception:
1941 has_failed_build = 0
1942 self.log.info("no failed build exists")
1943 pass
1944 # if there's failed build, we can proceed
1945 if has_failed_build:
1946 self.driver.find_element_by_partial_link_text("error").click()
1947 self.driver.back()
1948 # not sure if there "must be" some warnings, so here save a screen
1949 self.browser_delay()
1950 self.save_screenshot(screenshot_type='selenium', append_name='step4')
1951
1952
1953 ##############
1954 # CASE 955 #
1955 ##############
1956 def test_955(self):
1957 self.case_no = self.get_case_number()
1958 self.log.info(' CASE %s log: ' % str(self.case_no))
1959 self.driver.maximize_window()
1960 self.driver.get(self.base_url)
1961 self.log.info(" You should manually create all images before test starts!")
1962 # So far the case itself is not responsable for creating all sorts of images.
1963 # So assuming they are already there
1964 # step 2
1965 self.driver.find_element_by_link_text("core-image-minimal").click()
1966 # save screen here to see the page component
1967
1968
1969 ##############
1970 # CASE 956 #
1971 ##############
1972 def test_956(self):
1973 self.case_no = self.get_case_number()
1974 self.log.info(' CASE %s log: ' % str(self.case_no))
1975 self.driver.maximize_window()
1976 self.driver.get(self.base_url)
1977 # step 2-3 need to run manually
1978 self.log.info("step 2-3: checking the help message when you hover on help icon of target,\
1979 tasks, recipes, packages need to run manually")
1980 self.driver.find_element_by_partial_link_text("Manual").click()
1981 if not self.is_text_present("Manual"):
1982 self.log.error("please check [Toaster manual] link on page")
1983 self.failIf(True)
1984
1985####################################################################################################
1986# Starting backend tests ###########################################################################
1987####################################################################################################
1988
1989 ##############
1990 # CASE 1066 #
1991 ##############
1992 def test_1066(self):
1993 self.case_no = self.get_case_number()
1994 self.log.info(' CASE %s log: ' % str(self.case_no))
1995 con=sqlite.connect('toaster.sqlite')
1996 cursor = con.cursor()
1997 query = "select count(name) from orm_project a, auth_user b where a.user_id = b.id and b.username='_anonuser';"
1998 cursor.execute(query)
1999 data = cursor.fetchone()
2000 self.failUnless(data >= 1)
2001
2002
2003 ##############
2004 # CASE 1071 #
2005 ##############
2006 def test_1071(self):
2007 self.case_no = self.get_case_number()
2008 self.log.info(' CASE %s log: ' % str(self.case_no))
2009 con=sqlite.connect('toaster.sqlite')
2010 cursor = con.cursor()
2011 query = "select name from orm_release;"
2012 cursor.execute(query)
2013 data = cursor.fetchall()
2014 for i in range(0,4):
2015 data[i] = data[i][0]
2016 data.sort()
2017 print(data)
2018 json_parse = json.loads(open('toasterconf.json').read())
2019 json_data = []
2020 for i in range (0,4):
2021 json_data.append(json_parse['releases'][i]['name'])
2022 json_data.sort()
2023 print(json_data)
2024 self.failUnless(data == json_data)
2025
2026 ##############
2027 # CASE 1072 #
2028 ##############
2029 def test_1072(self):
2030 self.case_no = self.get_case_number()
2031 self.log.info(' CASE %s log: ' % str(self.case_no))
2032 con=sqlite.connect('toaster.sqlite')
2033 cursor = con.cursor()
2034 query = "select value from orm_toastersetting where name like 'DEFCONF%';"
2035 cursor.execute(query)
2036 data = cursor.fetchall()
2037 for i in range(0,6):
2038 data[i] = data[i][0]
2039 print(data)
2040 json_parse = json.loads(open('toasterconf.json').read())
2041 json_data=json_parse['config']
2042 json_data = json_data.values()
2043 print(json_data)
2044 self.failUnless(data == json_data)
2045
2046
2047 ##############
2048 # CASE 1074 #
2049 ##############
2050 def test_1074(self):
2051 self.case_no = self.get_case_number()
2052 self.log.info(' CASE %s log: ' % str(self.case_no))
2053 con=sqlite.connect('toaster.sqlite')
2054 cursor = con.cursor()
2055 query = "select name from orm_layersource;"
2056 cursor.execute(query)
2057 data = cursor.fetchall()
2058 for i in range(0,3):
2059 data[i] = data[i][0]
2060 print(data)
2061 json_parse = json.loads(open('toasterconf.json').read())
2062 json_data = []
2063 for i in range(0,3):
2064 json_data.append(json_parse['layersources'][i]['name'])
2065 print(json_data)
2066 self.failUnless(set(data) == set(json_data))
2067
2068 ##############
2069 # CASE 1075 #
2070 ##############
2071 def test_1075(self):
2072 self.case_no = self.get_case_number()
2073 self.log.info(' CASE %s log: ' % str(self.case_no))
2074 con=sqlite.connect('toaster.sqlite')
2075 cursor = con.cursor()
2076 query = "select value from orm_toastersetting where name like 'DEFAULT_RELEASE';"
2077 cursor.execute(query)
2078 data = cursor.fetchall()
2079 data = data[0][0]
2080 print(data)
2081 json_parse = json.loads(open('toasterconf.json').read())
2082 json_data = json_parse['defaultrelease']
2083 print(json_data)
2084 self.failUnless(set(data) == set(json_data))
2085
2086 ##############
2087 # CASE 1076 #
2088 ##############
2089 def test_1076(self):
2090 self.case_no = self.get_case_number()
2091 self.log.info(' CASE %s log: ' % str(self.case_no))
2092
2093 print('Checking branches for "Local Yocto Project"')
2094 con=sqlite.connect('toaster.sqlite')
2095 cursor = con.cursor()
2096 query = "select name from orm_branch where layer_source_id=1;"
2097 cursor.execute(query)
2098 data = cursor.fetchall()
2099 lenght = len(data)
2100 try:
2101 for i in range(0,lenght):
2102 data[i] = data[i][0]
2103 except:
2104 pass
2105 print(data)
2106 json_parse = json.loads(open('toasterconf.json').read())
2107 json_location = json_parse['layersources'][0]['name']
2108 print(json_location)
2109 json_data = json_parse['layersources'][0]['branches']
2110 print(json_data)
2111 self.failUnless(set(data) == set(json_data))
2112
2113 print('Checking branches for "OpenEmbedded"')
2114 con=sqlite.connect('toaster.sqlite')
2115 cursor = con.cursor()
2116 query = "select name from orm_branch where layer_source_id=3;"
2117 cursor.execute(query)
2118 data = cursor.fetchall()
2119 lenght = len(data)
2120 for i in range(0,lenght):
2121 data[i] = data[i][0]
2122 print(data)
2123 json_parse = json.loads(open('toasterconf.json').read())
2124 json_location = json_parse['layersources'][1]['name']
2125 print(json_location)
2126 json_data = json_parse['layersources'][1]['branches']
2127 print(json_data)
2128 self.failUnless(set(data) == set(json_data))
2129
2130 print('Checking branches for "Imported layers"')
2131 con=sqlite.connect('toaster.sqlite')
2132 cursor = con.cursor()
2133 query = "select name from orm_branch where layer_source_id=2;"
2134 cursor.execute(query)
2135 data = cursor.fetchall()
2136 lenght = len(data)
2137 for i in range(0,lenght):
2138 data[i] = data[i][0]
2139 print(data)
2140 json_parse = json.loads(open('toasterconf.json').read())
2141 json_location = json_parse['layersources'][2]['name']
2142 print(json_location)
2143 json_data = json_parse['layersources'][2]['branches']
2144 print(json_data)
2145 self.failUnless(set(data) == set(json_data))
2146
2147
2148 ##############
2149 # CASE 1077 #
2150 ##############
2151 def test_1077(self):
2152 self.case_no = self.get_case_number()
2153 self.log.info(' CASE %s log: ' % str(self.case_no))
2154 con=sqlite.connect('toaster.sqlite')
2155 cursor = con.cursor()
2156 query = "select name from orm_bitbakeversion;"
2157 cursor.execute(query)
2158 data = cursor.fetchall()
2159 for i in range(0,4):
2160 data[i] = data[i][0]
2161 print(data)
2162 json_parse = json.loads(open('toasterconf.json').read())
2163 json_data = []
2164 for i in range(0,4):
2165 json_data.append(json_parse['bitbake'][i]['name'])
2166 print(json_data)
2167 self.failUnless(set(data) == set(json_data))
2168
2169 ##############
2170 # CASE 1083 #
2171 ##############
2172 def test_1083(self):
2173 self.case_no = self.get_case_number()
2174 self.log.info(' CASE %s log: ' % str(self.case_no))
2175 self.driver.maximize_window()
2176 self.driver.get(self.base_url)
2177 self.driver.find_element_by_id("new-project-button").click()
2178 self.driver.find_element_by_id("new-project-name").send_keys("new-test-project")
2179 self.driver.find_element_by_id("create-project-button").click()
2180 con=sqlite.connect('toaster.sqlite')
2181 cursor = con.cursor()
2182 query = "select count(name) from orm_project where name = 'new-test-project';"
2183 cursor.execute(query)
2184 data = cursor.fetchone()
2185 print('data: %s' % data)
2186 self.failUnless(data >= 1)
2187
2188 ##############
2189 # CASE 1084 #
2190 ##############
2191 def test_1084(self):
2192 self.case_no = self.get_case_number()
2193 self.log.info(' CASE %s log: ' % str(self.case_no))
2194 self.driver.maximize_window()
2195 self.driver.get(self.base_url)
2196 self.driver.find_element_by_id("new-project-button").click()
2197 self.driver.find_element_by_id("new-project-name").send_keys("new-default-project")
2198 self.driver.find_element_by_id("create-project-button").click()
2199 con=sqlite.connect('toaster.sqlite')
2200 cursor = con.cursor()
2201 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;"
2202 cursor.execute(query)
2203 db_data = str(cursor.fetchone()[0])
2204 json_parse = json.loads(open('toasterconf.json').read())
2205 json_data = str(json_parse['defaultrelease'])
2206 self.failUnless(db_data == json_data)
2207
2208 ##############
2209 # CASE 1088 #
2210 ##############
2211 def test_1088(self):
2212 self.case_no = self.get_case_number()
2213 self.log.info(' CASE %s log: ' % str(self.case_no))
2214 self.driver.maximize_window()
2215 self.driver.get(self.base_url)
2216 self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
2217 self.driver.find_element_by_link_text('new-default-project').click()
2218 self.driver.find_element_by_id('project-change-form-toggle').click()
2219 self.driver.find_element_by_id('project-name-change-input').clear()
2220 self.driver.find_element_by_id('project-name-change-input').send_keys('new-name')
2221 self.driver.find_element_by_id('project-name-change-btn').click()
2222 con=sqlite.connect('toaster.sqlite')
2223 cursor = con.cursor()
2224 query = "select count(name) from orm_project where name = 'new-name';"
2225 cursor.execute(query)
2226 data = cursor.fetchone()[0]
2227 self.failUnless(data == 1)
2228 #reseting project name
2229 self.driver.find_element_by_id('project-change-form-toggle').click()
2230 self.driver.find_element_by_id('project-name-change-input').clear()
2231 self.driver.find_element_by_id('project-name-change-input').send_keys('new-default-project')
2232 self.driver.find_element_by_id('project-name-change-btn').click()
2233
2234
2235 ##############
2236 # CASE 1089 #
2237 ##############
2238 def test_1089(self):
2239 self.case_no = self.get_case_number()
2240 self.log.info(' CASE %s log: ' % str(self.case_no))
2241 self.driver.maximize_window()
2242 self.driver.get(self.base_url)
2243 self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
2244 self.driver.find_element_by_link_text('new-default-project').click()
2245 self.driver.find_element_by_id('change-machine-toggle').click()
2246 self.driver.find_element_by_id('machine-change-input').clear()
2247 self.driver.find_element_by_id('machine-change-input').send_keys('qemuarm64')
2248# self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
2249 self.driver.find_element_by_id('machine-change-btn').click()
2250 con=sqlite.connect('toaster.sqlite')
2251 cursor = con.cursor()
2252 query = "select count(id) from orm_projectvariable where name like 'machine' and value like 'qemuarm64';"
2253 cursor.execute(query)
2254 data = cursor.fetchone()[0]
2255 self.failUnless(data == 1)
2256 #resetting machine to default value
2257 self.driver.find_element_by_id('change-machine-toggle').click()
2258 self.driver.find_element_by_id('machine-change-input').clear()
2259 self.driver.find_element_by_id('machine-change-input').send_keys('qemux86')
2260 self.driver.find_element_by_id('machine-change-input').send_keys(Keys.RETURN)
2261 self.driver.find_element_by_id('machine-change-btn').click()
2262
2263 ##############
2264 # CASE 1090 #
2265 ##############
2266 def test_1090(self):
2267 self.case_no = self.get_case_number()
2268 self.log.info(' CASE %s log: ' % str(self.case_no))
2269 con=sqlite.connect('toaster.sqlite')
2270 cursor = con.cursor()
2271 query = "select username from auth_user where is_superuser = 1;"
2272 cursor.execute(query)
2273 data = cursor.fetchall()
2274 try:
2275 data = data[0][0]
2276 except:
2277 pass
2278 print(data)
2279 self.failUnless(data == 'toaster_admin')
2280
2281 ##############
2282 # CASE 1091 #
2283 ##############
2284 def test_1091(self):
2285 self.case_no = self.get_case_number()
2286 self.log.info(' CASE %s log: ' % str(self.case_no))
2287 self.driver.maximize_window()
2288 self.driver.get(self.base_url)
2289 self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
2290 self.driver.find_element_by_link_text('new-default-project').click()
2291 self.driver.find_element_by_id('release-change-toggle').click()
2292 dropdown = self.driver.find_element_by_css_selector('select')
2293 for option in dropdown.find_elements_by_tag_name('option'):
2294 if option.text == 'Local Yocto Project':
2295 option.click()
2296 self.driver.find_element_by_id('change-release-btn').click()
2297 #wait for the changes to register in the DB
2298 time.sleep(1)
2299 con=sqlite.connect('toaster.sqlite')
2300 cursor = con.cursor()
2301 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';"
2302 cursor.execute(query)
2303 data = cursor.fetchone()[0]
2304 #resetting release to default
2305 self.driver.find_element_by_id('release-change-toggle').click()
2306 dropdown = self.driver.find_element_by_css_selector('select')
2307 for option in dropdown.find_elements_by_tag_name('option'):
2308 if option.text == 'Yocto Project master':
2309 option.click()
2310 self.driver.find_element_by_id('change-release-btn').click()
2311 #wait for the changes to register in the DB
2312 time.sleep(1)
2313 self.failUnless(data == 3)
2314
2315 ##############
2316 # CASE 1092 #
2317 ##############
2318 def test_1092(self):
2319 self.case_no = self.get_case_number()
2320 self.log.info(' CASE %s log: ' % str(self.case_no))
2321 self.driver.maximize_window()
2322 con=sqlite.connect('toaster.sqlite')
2323 cursor = con.cursor()
2324 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';"
2325 cursor.execute(query)
2326 data = dict(cursor.fetchall())
2327 print(data)
2328 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'}
2329 self.failUnless(data == default_values)
2330
2331 ##############
2332 # CASE 1093 #
2333 ##############
2334 def test_1093(self):
2335 self.case_no = self.get_case_number()
2336 self.log.info(' CASE %s log: ' % str(self.case_no))
2337
2338 #get initial values
2339 con=sqlite.connect('toaster.sqlite')
2340 cursor = con.cursor()
2341 query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
2342 cursor.execute(query)
2343 data_initial = cursor.fetchall()
2344 print(data_initial)
2345
2346 self.driver.maximize_window()
2347 self.driver.get('localhost:8000')#self.base_url)
2348 self.driver.find_element_by_css_selector("a[href='/toastergui/projects/']").click()
2349 self.driver.find_element_by_link_text('new-default-project').click()
2350 self.driver.find_element_by_id('release-change-toggle').click()
2351 dropdown = self.driver.find_element_by_css_selector('select')
2352 for option in dropdown.find_elements_by_tag_name('option'):
2353 if option.text == 'Local Yocto Project':
2354 option.click()
2355 self.driver.find_element_by_id('change-release-btn').click()
2356 #wait for the changes to register in the DB
2357 time.sleep(1)
2358
2359 #get changed values
2360 con=sqlite.connect('toaster.sqlite')
2361 cursor = con.cursor()
2362 query = "select layercommit_id from orm_projectlayer a, orm_project b where a.project_id=b.id and b.name='new-default-project';"
2363 cursor.execute(query)
2364 data_changed = cursor.fetchall()
2365 print(data_changed)
2366
2367 #resetting release to default
2368 self.driver.find_element_by_id('release-change-toggle').click()
2369 dropdown = self.driver.find_element_by_css_selector('select')
2370 for option in dropdown.find_elements_by_tag_name('option'):
2371 if option.text == 'Yocto Project master':
2372 option.click()
2373 self.driver.find_element_by_id('change-release-btn').click()
2374 #wait for the changes to register in the DB
2375 time.sleep(1)
2376 self.failUnless(data_initial != data_changed)
diff --git a/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg b/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg
deleted file mode 100644
index 685a9ee6af..0000000000
--- a/bitbake/lib/toaster/contrib/tts/toasteruitest/toaster_test.cfg
+++ /dev/null
@@ -1,25 +0,0 @@
1# Configuration file for toaster_test
2# Sorted by different host type
3
4# test browser could be: firefox; chrome; ie(still under development)
5# logging_level could be: CRITICAL; ERROR; WARNING; INFO; DEBUG; NOTSET
6
7
8[toaster_test_linux]
9toaster_url = 'http://127.0.0.1:8000'
10test_browser = 'firefox'
11test_cases = [946]
12logging_level = 'INFO'
13
14
15[toaster_test_windows]
16toaster_url = 'http://127.0.0.1:8000'
17test_browser = ['ie', 'firefox', 'chrome']
18test_cases = [901, 902, 903]
19logging_level = 'DEBUG'
20
21[toaster_test_darwin]
22toaster_url = 'http://127.0.0.1:8000'
23test_browser = 'firefox'
24test_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]
25logging_level = 'INFO'
diff --git a/bitbake/lib/toaster/contrib/tts/urlcheck.py b/bitbake/lib/toaster/contrib/tts/urlcheck.py
deleted file mode 100644
index 001fcee96a..0000000000
--- a/bitbake/lib/toaster/contrib/tts/urlcheck.py
+++ /dev/null
@@ -1,53 +0,0 @@
1from __future__ import print_function
2import sys
3
4import httplib2
5import config
6import urllist
7
8
9config.logger.info("Testing %s with %s", config.TOASTER_BASEURL, config.W3C_VALIDATOR)
10
11def validate_html5(url):
12 http_client = httplib2.Http(None)
13 status = "Failed"
14 errors = -1
15 warnings = -1
16
17 urlrequest = config.W3C_VALIDATOR+url
18
19 # pylint: disable=broad-except
20 # we disable the broad-except because we want to actually catch all possible exceptions
21 try:
22 resp, _ = http_client.request(urlrequest, "HEAD")
23 if resp['x-w3c-validator-status'] != "Abort":
24 status = resp['x-w3c-validator-status']
25 errors = int(resp['x-w3c-validator-errors'])
26 warnings = int(resp['x-w3c-validator-warnings'])
27
28 if status == 'Invalid':
29 config.logger.warning("Failed %s is %s\terrors %s warnings %s (check at %s)", url, status, errors, warnings, urlrequest)
30 else:
31 config.logger.debug("OK! %s", url)
32
33 except Exception as exc:
34 config.logger.warning("Failed validation call: %s", exc)
35 return (status, errors, warnings)
36
37
38def print_validation(url):
39 status, errors, warnings = validate_html5(url)
40 config.logger.error("url %s is %s\terrors %s warnings %s (check at %s)", url, status, errors, warnings, config.W3C_VALIDATOR+url)
41
42
43def main():
44 print("Testing %s with %s" % (config.TOASTER_BASEURL, config.W3C_VALIDATOR))
45
46 if len(sys.argv) > 1:
47 print_validation(sys.argv[1])
48 else:
49 for url in urllist.URLS:
50 print_validation(config.TOASTER_BASEURL+url)
51
52if __name__ == "__main__":
53 main()
diff --git a/bitbake/lib/toaster/contrib/tts/urllist.py b/bitbake/lib/toaster/contrib/tts/urllist.py
deleted file mode 100644
index 6db9ffc7b5..0000000000
--- a/bitbake/lib/toaster/contrib/tts/urllist.py
+++ /dev/null
@@ -1,39 +0,0 @@
1URLS = [
2 'toastergui/landing/',
3 'toastergui/builds/',
4 'toastergui/build/1',
5 'toastergui/build/1/tasks/',
6 'toastergui/build/1/tasks/1/',
7 'toastergui/build/1/task/1',
8 'toastergui/build/1/recipes/',
9 'toastergui/build/1/recipe/1/active_tab/1',
10 'toastergui/build/1/recipe/1',
11 'toastergui/build/1/recipe_packages/1',
12 'toastergui/build/1/packages/',
13 'toastergui/build/1/package/1',
14 'toastergui/build/1/package_built_dependencies/1',
15 'toastergui/build/1/package_included_detail/1/1',
16 'toastergui/build/1/package_included_dependencies/1/1',
17 'toastergui/build/1/package_included_reverse_dependencies/1/1',
18 'toastergui/build/1/target/1',
19 'toastergui/build/1/target/1/targetpkg',
20 'toastergui/build/1/target/1/dirinfo',
21 'toastergui/build/1/target/1/dirinfo_filepath/_/bin/bash',
22 'toastergui/build/1/configuration',
23 'toastergui/build/1/configvars',
24 'toastergui/build/1/buildtime',
25 'toastergui/build/1/cpuusage',
26 'toastergui/build/1/diskio',
27 'toastergui/build/1/target/1/packagefile/1',
28 'toastergui/newproject/',
29 'toastergui/projects/',
30 'toastergui/project/1',
31 'toastergui/project/1/configuration',
32 'toastergui/project/1/builds/',
33 'toastergui/project/1/layers/',
34 'toastergui/project/1/layer/1',
35 'toastergui/project/1/importlayer',
36 'toastergui/project/1/targets/',
37 'toastergui/project/1/machines/',
38 'toastergui/',
39]