From f49a92611953e93a2edbbab8cd142338e577f9cb Mon Sep 17 00:00:00 2001 From: shaofeng Date: Thu, 24 Apr 2014 15:36:50 +0800 Subject: [PATCH 1/1] Tizen TCT 2.2.1 r1 final release version Change-Id: I1d1312808652e38da1d2a1b3903f410cd0511f89 --- CONFIG | 17 + LICENSE | 340 +++++++++++++ README | 1 + VERSION | 4 + debian/changelog | 5 + debian/compat | 1 + debian/control | 13 + debian/copyright | 26 + debian/docs | 2 + debian/postinst | 6 + debian/rules | 4 + packaging/Makefile | 20 + packaging/tct-shell.dsc | 11 + packaging/tct-shell.spec | 50 ++ setup.py | 19 + style/application.js | 198 ++++++++ style/back_top.png | Bin 0 -> 1122 bytes style/blue.jpg | Bin 0 -> 954 bytes style/gray.jpg | Bin 0 -> 866 bytes style/jquery.min.js | 4 + style/orange.jpg | Bin 0 -> 934 bytes style/popup.js | 1215 ++++++++++++++++++++++++++++++++++++++++++++++ style/red.jpg | Bin 0 -> 937 bytes style/summary.xsl | 352 ++++++++++++++ style/testresult.xsl | 571 ++++++++++++++++++++++ style/tests.css | 195 ++++++++ tct-plan-generator | 52 ++ tct-shell | 89 ++++ tctshell/__init__.py | 1 + tctshell/constants.py | 195 ++++++++ tctshell/plan_options.py | 188 +++++++ tctshell/plan_runner.py | 135 ++++++ tctshell/shellrunner.py | 908 ++++++++++++++++++++++++++++++++++ tctshell/shellwrapper.py | 433 +++++++++++++++++ 34 files changed, 5055 insertions(+) create mode 100644 CONFIG create mode 100644 LICENSE create mode 100644 README create mode 100644 VERSION create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/postinst create mode 100644 debian/rules create mode 100644 packaging/Makefile create mode 100644 packaging/tct-shell.dsc create mode 100644 packaging/tct-shell.spec create mode 100644 setup.py create mode 100644 style/application.js create mode 100644 style/back_top.png create mode 100644 style/blue.jpg create mode 100644 style/gray.jpg create mode 100644 style/jquery.min.js create mode 100644 style/orange.jpg create mode 100644 style/popup.js create mode 100644 style/red.jpg create mode 100644 style/summary.xsl create mode 100644 style/testresult.xsl create mode 100644 style/tests.css create mode 100755 tct-plan-generator create mode 100644 tct-shell create mode 100644 tctshell/__init__.py create mode 100644 tctshell/constants.py create mode 100644 tctshell/plan_options.py create mode 100644 tctshell/plan_runner.py create mode 100644 tctshell/shellrunner.py create mode 100644 tctshell/shellwrapper.py diff --git a/CONFIG b/CONFIG new file mode 100644 index 0000000..d7749b6 --- /dev/null +++ b/CONFIG @@ -0,0 +1,17 @@ +[TCTSHELL] +TEST_SUITE_DIR = /opt/tct/packages/ +WRT_LAUNCHR_CMD = WRTLauncher +TCT_SHELL_HOME = /opt/tct/shell +TCT_PLAN_FOLDER = /opt/tct/shell/plan/ + +[Device] +DEVICE_TMP_FOLDER = /tmp/ +DEVICE_TESTS_FILE = /opt/usr/media/tct/opt/%s/tests.xml +DEVICE_CAPABILITY_PATH = /opt/usr/media/Documents/tct/capability.xml +DEVICE_BUILD_INFO_PATH = /opt/usr/media/Documents/tct/buildinfo.xml +DEVICE_DEFAULT_CONF_PATH = /opt/usr/media/tct/opt/tct-testconfig/tct-testconfig.ini +DEVICE_DEFAULT_ISON_CONF_PATH = /opt/usr/media/tct/preconfigure.json +DEVICE_PRECONFIGURE_FILE_PATH = /opt/usr/media/Documents/tct/pre_configure.conf + +[TESTKIT_LITE] +LITE_HOME = /opt/testkit/lite diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6d45519 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README b/README new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..ef70305 --- /dev/null +++ b/VERSION @@ -0,0 +1,4 @@ +[public_version] +version=1.0.14 +[internal_version] +version=1.0.14 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..48eac00 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +tct-shell (1.0.14) unstable; urgency=low + + * Packaged for Ubuntu + + -- Ed Bartosh Mon, 06 May 2013 14:37:15 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +8 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..7f37172 --- /dev/null +++ b/debian/control @@ -0,0 +1,13 @@ +Source: tct-shell +Section: utils +Priority: standard +Maintainer: Lei Yang +Build-Depends: debhelper (>= 8.0.0), python-setuptools +Standards-Version: 3.9.4 + +Package: tct-shell +Architecture: all +Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} +Description: Console tool for Tizen Compliance Test + This tool provides an alternative way to execute TCT with testkit-lite + when testkit-manager is not available diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..1af0f16 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,26 @@ +Format: http://dep.debian.net/deps/dep5 +Source: https://github.com/testkit/testkit-manager + +Files: * +Copyright: 2012-2013 Intel Corp. +License: GPL-2.0 + +Files: debian/* +Copyright: 2012-2013 Intel Corp. +License: GPL-2.0 + +License: GPL-2.0 + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..8ffb463 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +README +CONFIG diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..9c659bf --- /dev/null +++ b/debian/postinst @@ -0,0 +1,6 @@ +#!/bin/sh + +# Set permissions +chmod ugo+rwx /opt/tct/shell + +#DEBHELPER# diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..2d33f6a --- /dev/null +++ b/debian/rules @@ -0,0 +1,4 @@ +#!/usr/bin/make -f + +%: + dh $@ diff --git a/packaging/Makefile b/packaging/Makefile new file mode 100644 index 0000000..f0ec281 --- /dev/null +++ b/packaging/Makefile @@ -0,0 +1,20 @@ +PKG_NAME := tct-shell +SPECFILE = $(addsuffix .spec, $(PKG_NAME)) +PKG_VERSION := $(shell grep '^Version: ' $(SPECFILE)|awk '{print $$2}') + +TARBALL := $(PKG_NAME)_$(PKG_VERSION).tar.gz + +dsc: tarball + $(eval MD5=$(shell md5sum $(TARBALL) | sed "s/ / $(shell stat -c '%s' $(TARBALL)) /")) + @sed -i 's/^Version:.*/Version: $(PKG_VERSION)/' $(PKG_NAME).dsc + @sed -i 's/ [a-f0-9]\+ [0-9]\+ $(PKG_NAME).*tar.*/ $(MD5)/' $(PKG_NAME).dsc + +tarball: + @cd "$$(git rev-parse --show-toplevel)" \ + && git archive --prefix $(PKG_NAME)-$(PKG_VERSION)/ HEAD \ + | gzip > "$(CURDIR)/$(TARBALL)" + +clean: + @rm -f $(PKG_NAME)*.tar.gz + +all: clean tarball dsc diff --git a/packaging/tct-shell.dsc b/packaging/tct-shell.dsc new file mode 100644 index 0000000..b57a386 --- /dev/null +++ b/packaging/tct-shell.dsc @@ -0,0 +1,11 @@ +Source: tct-shell +Section: utils +Priority: standard +Maintainer: Shaofeng +Build-Depends: debhelper (>= 8.0.0), python-setuptools, python-support, tree +Standards-Version: 3.9.2 +Homepage: https://github.com/testkit/ +Version: 1.0.1 +Files: + f64f26693160cf59cdae21e8961cf986 15593 tct-shell_1.0.1.tar.gz + diff --git a/packaging/tct-shell.spec b/packaging/tct-shell.spec new file mode 100644 index 0000000..821ac00 --- /dev/null +++ b/packaging/tct-shell.spec @@ -0,0 +1,50 @@ +Summary: TCT-Shell +Name: tct-shell +Version: 1.0.9 +Release: 1 +License: GPLv2 +Group: Applications/System +Source: %name-%version.tar.gz +BuildRoot: %_tmppath/%name-%version-buildroot +Requires: python + + +%description +TCT-Shell is a wrapper pf testkit-lite. provide an alternative way to execute TCT with testkit-lite when testkit-manager is not available +Provide the following functions: +1. List available test packages. +2. Install/remove test packages on target device according to user's option +3. Trigger testing in 3 ways: through test plan, through package, rerun failed test cases. +4. Show test result summary in visual way. + +%prep +%setup -q + +%build +./autogen +./configure +make + +%install +[ "\$RPM_BUILD_ROOT" != "/" ] && rm -rf "\$RPM_BUILD_ROOT" +make install DESTDIR=$RPM_BUILD_ROOT + +%clean + +%post +# Set permissions +chmod ugo+rwx /opt/tct/shell + +%files +/usr/lib/python2.7/dist-packages/tctshell/* +/opt/tct/shell +/opt/tct/shell/plan +/opt/tct/shell/style +/usr/bin/tct-shell +/usr/bin/tct-plan-generator + +%post +chmod -R 777 /opt/tct/shell +chmod -R 777 /opt/tct/shell/plan + +%changelog diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..817bac8 --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +#!/usr/bin/python + +from glob import glob + +from setuptools import setup, find_packages + +setup( + name = "tct-shell", + description = "Console tool for Tizen Compliance Test", + author = "Cathy Shen", + author_email = "cathy.shen@intel.com", + version = "1.0.14", + include_package_data = True, + data_files = [('/opt/tct/shell/plan', glob('plan/*')), + ('/opt/tct/shell/', ['CONFIG', 'LICENSE', 'VERSION']), + ('/opt/tct/shell/style', glob('style/*'))], + scripts = ['tct-shell', 'tct-plan-generator'], + packages = find_packages(), +) diff --git a/style/application.js b/style/application.js new file mode 100644 index 0000000..dbee8f2 --- /dev/null +++ b/style/application.js @@ -0,0 +1,198 @@ +function getScrollTop() { + return f_scrollTop(); +} + +function f_scrollTop() { + return f_filterResults($(window) ? $(window).scrollTop() : 0, + document.documentElement ? document.documentElement.scrollTop : 0, + document.body ? document.body.scrollTop : 0); +} +function f_filterResults(n_win, n_docel, n_body) { + var n_result = n_win ? n_win : 0; + if (n_docel && (!n_result || (n_result > n_docel))) + n_result = n_docel; + return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result; +} + +function setScrollTop() { + $(window) ? $(window).scrollTop(0) : 0; + document.documentElement ? document.documentElement.scrollTop = 0 : 0; + document.body ? document.body.scrollTop = 0 : 0; +} + +function goTopEx() { + $node = $('#goTopBtn'); + if (getScrollTop() > 0) { + $node.show(); + } else { + $node.hide(); + } + + $(window).scroll(function() { + if (getScrollTop() > 0) { + $node.show(); + } else { + $node.hide(); + } + }); + + $node.click(function() { + setScrollTop(); + }); +} + +function drawRatio() { + $('.suite_item').each(function(i, node) { + drawSuiteRatio(node) + }); +} + +$(".see_all").click(function(){ + $("#see_all").show(); + $("#see_fail").hide(); + $("#see_block").hide(); + $("#see_na").hide(); + updateToggles(); + return false; +}); + +$(".see_failed").click(function(){ + $("#see_all").hide(); + $("#see_fail").show(); + $("#see_block").hide(); + $("#see_na").hide(); + updateToggles(); + return false; +}); + +$(".see_blocked").click(function(){ + $("#see_all").hide(); + $("#see_fail").hide(); + $("#see_block").show(); + $("#see_na").hide(); + updateToggles(); + return false; +}); + +$(".see_na").click(function(){ + $("#see_all").hide(); + $("#see_fail").hide(); + $("#see_block").hide(); + $("#see_na").show(); + updateToggles(); + return false; +}); + +$("a.test_case_popup").click(function(){ + var $this = $(this); + Popup.show($this.attr('id')); + return false; +}); + +$(".see_capabilities").click(function(){ + if ($('#capability_table').css('display') == 'none') { + $("#capability_table").show(); + }else{ + $("#capability_table").hide(); + } + return false; +}); + +function drawSuiteRatio(node) { + arrTitle = new Array("Passed", "Failed", "Blocked", "Not Executed"); + var $node = $(node); + var $total = $node.find('.total'); + var $pass = $node.find('.pass'); + var $fail = $node.find('.fail'); + var $block = $node.find('.block'); + var $na = $node.find('.na'); + var $div = $node.find('.RatioGraphic'); + + var total_int = parseInt($total.text()); + var pass_int = parseInt($pass.text()); + var fail_int = parseInt($fail.text()); + var block_int = parseInt($block.text()); + var na_int = parseInt($na.text()); + + var pass_rate = pass_int * 100 / total_int; + var fail_rate = fail_int * 100 / total_int; + var block_rate = block_int * 100 / total_int; + var na_rate = na_int * 100 / total_int; + + var areaWidth = 380; + + var pass_width = areaWidth * pass_rate / 100; + var fail_width = areaWidth * fail_rate / 100; + var block_width = areaWidth * block_rate / 100; + var na_width = areaWidth * na_rate / 100; + + //pass_rate = Math.round(pass_rate) + //fail_rate = Math.round(fail_rate) + //block_rate = Math.round(block_rate) + //na_rate = Math.round(na_rate) + pass_rate = pass_rate.toFixed(2); + fail_rate = fail_rate.toFixed(2); + block_rate = block_rate.toFixed(2); + na_rate = na_rate.toFixed(2); + + var pass_style = "padding:3px 0px 0px 0px;font-size:9pt;height:17px;text-align:center;color:white;font-weight:bold;background:url("blue.jpg");" + var fail_style = "padding:3px 0px 0px 0px;font-size:9pt;height:17px;text-align:center;color:white;font-weight:bold;background:url("red.jpg");" + var block_style = "padding:3px 0px 0px 0px;font-size:9pt;height:17px;text-align:center;color:white;font-weight:bold;background:url("orange.jpg");" + var na_style = "padding:3px 0px 0px 0px;font-size:9pt;height:17px;text-align:center;color:white;font-weight:bold;background:url("gray.jpg");" + + var html = ""; + if (pass_width > 0){ + html += ""; + } + if (fail_width > 0){ + html += ""; + } + if (block_width > 0){ + html += ""; + } + if (na_width > 0){ + html += ""; + } + html += "
"; + if (pass_width > 20){ + html += pass_rate + "%" + } + html += ""; + if (fail_width > 20){ + html += fail_rate + "%" + } + html += ""; + if (block_width > 20){ + html += block_rate + "%" + } + html += ""; + if (na_width > 20){ + html += na_rate + "%" + } + html += "
"; + $div.html(html); +} diff --git a/style/back_top.png b/style/back_top.png new file mode 100644 index 0000000000000000000000000000000000000000..19cbd763108a85a2a7c2954c0172b580a7ccb3b3 GIT binary patch literal 1122 zcmV-o1fBbdP)r0015c0ssI2d=e3600001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1NTWpK~!i%?ODH1 z+dvd{+C)SOk|P+hp$yOgm6jPqnTZ7I)Gcc=!k+>oS=pEnNXbkQnJGvNz!0%z0L2wZ zMoHzq^PTPU`E1|$a1~W&Wgzjp_uhTq`*labaU4TG(~7`{X?0Bdg%Hs6Om!S+dNe&# zjlfhpR}dWy9<9Q;9okCWbT-PlZmxGRYz)9cX@6&K*AzANaPz!$H3sR{X6o*f- z4b*pEk}JU4uwK_a%~5TJF*!IJ;>H6{=Ny#NbHyhHA$=!8ayEz&r+zXFgmpvfhqI?w|FUEqarP39Cc>f-quc3jEdu+U@+o)4zI3^xJNqzVuyjX!~+ej2wH~ z!#?9@^sW!Wb4xOgtua!P|?@BCZTN@<*r+%(Pxu0eDfun^ZQju zN7LD^EJhIPU3jryH{Y7jCsuOOy7#FG2=cVzO?_1 zlUSm)i8WXLUKd>&D<`V6w9Fo>i_18DN0a+_FQn-SR4r6UeIN$n?O7At&)G*bY)h>= zu2H-L&wm;uiKSmp^?}Eaeo3F#ad`p_g zkaTccl`IJj#%&VJotuA|7HL``L^*eEN#;^7^gJj%uZS@*os`d9+Ec76WDhY%&DLh+ zk~gPu{7RB6cX^W=C|O+bb10mksqr3fuC*y~*&4DVsdKGL=1E#fs&zGbAaCd)4J^Z) z7R;>36Esb7Ig3c*^kNl;p$CS#b-#ql94e#c+#y@;{KUk8jLy$r=fXIkvq!3R;o_DW zp;~~4fZ~5Q)UroYk~0FDo}2+zQ?Kb!!!PI6y7%M^u$p?EJ!<&nyjs(fGr(%NP!T_~pF%FN1_9_w2rCUjP6A07*qoM6N<$f@q*1S^xk5 literal 0 HcmV?d00001 diff --git a/style/blue.jpg b/style/blue.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1db8f724fa76eb1f4efb914d24be05c187109d8 GIT binary patch literal 954 zcmex=eAA-4QK!nlZm zfsvIF3>ct*k%^gwm5rT)lZ%^?ff*tPBq6d$k^&4&jLghTEX=H|EG$4{YJqYLEP|{; ziiVDC!hwnGN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXb ztDC!rr&n-DXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g# zUABD1%2k^-Z`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oTTk@SCu%S(U=?B5(qa)L@X*7#m8DOl#UiAmVu=Tcr^qMLDWl0zutf1t zBi|CmOpYi9pn9eTp$4HD$1WIZ_T(D&E;DSK>FjIRYjVusvPR}AiOW61}+8- zMh!;Ip3DVF!4kKUr80XGmjzt*N%l4DF*zlb)n;;8;#Ml}g5=B|uVf*RYM?(BumIgA z)*$QQbO~sVEYxs0nBhm2+=B!b0qtV}TDGM@LtqO7&{>=goIc4J5^Xb%%|f!ps7)#v aXp7m5W0y5@nSs8&EHMS7nw6pc|4jgm*fB5w literal 0 HcmV?d00001 diff --git a/style/gray.jpg b/style/gray.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f1acfdbe94c05b638ac06a84edf7c7bbb1125f3e GIT binary patch literal 866 zcmex=wkpZ3brsCPqdOf&yk%W)NUwVPR%vXJcmvVGd4iP7V$(4t91fUM?^_0R}-120_L^W=16jCP7AKLB{__7~~lk7+Hbd0r?0D z7@3$^SlNJ1;^GD>*eU>YJ~J~D(6Ov6EI`$@KzRlhK~^C}Lq|5@z(jVXLJ_0Ji3>TD zoi-j64Z8S2#W<;`iIYoATtZSxRZU$(Q_IBE%-q7#%Gt%$&E3P(D>x)HEIcAIDmf)J zEj=SMtGJ}Jth}PKs=1}Lt-YhOYtrN?Q>RUzF>}_U#Y>hhTfSoDs!f}>Y~8kf$Ie}c z4j(ys?D&b3r!HN-a`oEv8#iw~eDwIq(`V0LynOZX)8{W=zkUDl^B2fpj10^WZ^38f2KE_o9%~}YXK;@p{B?_ghnW!=dCY3JE*Eh+R}GQb@{A;m&0YWe@)1OTom7e@d9 literal 0 HcmV?d00001 diff --git a/style/jquery.min.js b/style/jquery.min.js new file mode 100644 index 0000000..198b3ff --- /dev/null +++ b/style/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/style/orange.jpg b/style/orange.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac4c7493f8b541b2d97af3982860fdfce378d658 GIT binary patch literal 934 zcmex=eAA-4QK!nlZm zfsvIF3>ct*k%^gwm5rT)lZ%^?ff*tPBq6d$k^&4&jLghTEX=H|EG$4{YJqYLEP|{; ziiVDC!hwnGN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXb ztDC!rr&n-DXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g# zUABD1%2k^-Z`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oTqL?A@(1V@JrB8&TV2R?P3GG}?A^{LuY(jg0VkSov zg9gI_rUsz~p&3UOuq-pwkhqmB?UNK>xaFA1Wr}B8g@Ex zLJT$oT9usHlWW+!ASoDRpTiNL#f+LgUJ%CstzcM?1Tucn1imF64;_Hc1{xpe0W_I^ z0npHgAQm^!hfbG3j^|?VVt_diXl^FN7g9hL$o$JbKrt^ihznR5L>L0t|K9`v=nx~$ literal 0 HcmV?d00001 diff --git a/style/popup.js b/style/popup.js new file mode 100644 index 0000000..5f994f4 --- /dev/null +++ b/style/popup.js @@ -0,0 +1,1215 @@ +/** + * Copyright (c)2005-2009 Matt Kruse (javascripttoolbox.com) + * + * Dual licensed under the MIT and GPL licenses. + * This basically means you can use this code however you want for + * free, but don't claim to have written it yourself! + * Donations always accepted: http://www.JavascriptToolbox.com/donate/ + * + * Please do not link to the .js files on javascripttoolbox.com from + * your site. Copy the files locally to your server instead. + * + */ +/* ******************************************************************* */ +/* UTIL FUNCTIONS */ +/* ******************************************************************* */ +var Util = {'$VERSION':1.06}; + +// Util functions - these are GLOBAL so they +// look like built-in functions. + +// Determine if an object is an array +function isArray(o) { + return (o!=null && typeof(o)=="object" && typeof(o.length)=="number" && (o.length==0 || defined(o[0]))); +}; + +// Determine if an object is an Object +function isObject(o) { + return (o!=null && typeof(o)=="object" && defined(o.constructor) && o.constructor==Object && !defined(o.nodeName)); +}; + +// Determine if a reference is defined +function defined(o) { + return (typeof(o)!="undefined"); +}; + +// Iterate over an array, object, or list of items and run code against each item +// Similar functionality to Perl's map() function +function map(func) { + var i,j,o; + var results = []; + if (typeof(func)=="string") { + func = new Function('$_',func); + } + for (i=1; i>>=4; + } + while(hex.length<6) { hex='0'+hex; } + return "#" + hex; + }; + + // Convert hyphen style names like border-width to camel case like borderWidth + css.hyphen2camel = function(property) { + if (!defined(property) || property==null) { return null; } + if (property.indexOf("-")<0) { return property; } + var str = ""; + var c = null; + var l = property.length; + for (var i=0; i0) { + return bodies[0]; + } + } + return null; + }; + + // Get the amount that the main document has scrolled from top + // -------------------------------------------------------------------- + screen.getScrollTop = function() { + if (document.documentElement && defined(document.documentElement.scrollTop) && document.documentElement.scrollTop>0) { + return document.documentElement.scrollTop; + } + if (document.body && defined(document.body.scrollTop)) { + return document.body.scrollTop; + } + return null; + }; + + // Get the amount that the main document has scrolled from left + // -------------------------------------------------------------------- + screen.getScrollLeft = function() { + if (document.documentElement && defined(document.documentElement.scrollLeft) && document.documentElement.scrollLeft>0) { + return document.documentElement.scrollLeft; + } + if (document.body && defined(document.body.scrollLeft)) { + return document.body.scrollLeft; + } + return null; + }; + + // Util function to default a bad number to 0 + // -------------------------------------------------------------------- + screen.zero = function(n) { + return (!defined(n) || isNaN(n))?0:n; + }; + + // Get the width of the entire document + // -------------------------------------------------------------------- + screen.getDocumentWidth = function() { + var width = 0; + var body = screen.getBody(); + if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) { + var rightMargin = parseInt(CSS.get(body,'marginRight'),10) || 0; + var leftMargin = parseInt(CSS.get(body,'marginLeft'), 10) || 0; + width = Math.max(body.offsetWidth + leftMargin + rightMargin, document.documentElement.clientWidth); + } + else { + width = Math.max(body.clientWidth, body.scrollWidth); + } + if (isNaN(width) || width==0) { + width = screen.zero(self.innerWidth); + } + return width; + }; + + // Get the height of the entire document + // -------------------------------------------------------------------- + screen.getDocumentHeight = function() { + var body = screen.getBody(); + var innerHeight = (defined(self.innerHeight)&&!isNaN(self.innerHeight))?self.innerHeight:0; + if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) { + var topMargin = parseInt(CSS.get(body,'marginTop'),10) || 0; + var bottomMargin = parseInt(CSS.get(body,'marginBottom'), 10) || 0; + return Math.max(body.offsetHeight + topMargin + bottomMargin, document.documentElement.clientHeight, document.documentElement.scrollHeight, screen.zero(self.innerHeight)); + } + return Math.max(body.scrollHeight, body.clientHeight, screen.zero(self.innerHeight)); + }; + + // Get the width of the viewport (viewable area) in the browser window + // -------------------------------------------------------------------- + screen.getViewportWidth = function() { + if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) { + return document.documentElement.clientWidth; + } + else if (document.compatMode && document.body) { + return document.body.clientWidth; + } + return screen.zero(self.innerWidth); + }; + + // Get the height of the viewport (viewable area) in the browser window + // -------------------------------------------------------------------- + screen.getViewportHeight = function() { + if (!window.opera && document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) { + return document.documentElement.clientHeight; + } + else if (document.compatMode && !window.opera && document.body) { + return document.body.clientHeight; + } + return screen.zero(self.innerHeight); + }; + + return screen; +})();var Sort = (function(){ + var sort = {}; + sort.AlphaNumeric = function(a,b) { + if (a==b) { return 0; } + if (a0 && document.anchors[0].x) { + for (var i=0; i0) { + top -= el.scrollTop; + } + if (el.scrollLeft && el.scrollLeft>0) { + left -= el.scrollLeft; + } + } + } + // If this node is also the offsetParent, add on the offsets and reset to the new offsetParent + if (el == offsetParent) { + left += o.offsetLeft; + if (el.clientLeft && el.nodeName!="TABLE") { + left += el.clientLeft; + } + top += o.offsetTop; + if (el.clientTop && el.nodeName!="TABLE") { + top += el.clientTop; + } + o = el; + if (o.offsetParent==null) { + if (o.offsetLeft) { + left += o.offsetLeft; + } + if (o.offsetTop) { + top += o.offsetTop; + } + } + offsetParent = o.offsetParent; + } + } + + + if (originalObject.offsetWidth) { + width = originalObject.offsetWidth; + } + if (originalObject.offsetHeight) { + height = originalObject.offsetHeight; + } + + return {'left':left, 'top':top, 'width':width, 'height':height + }; + }; + + // Retrieve the position of an object's center point + // ================================================= + pos.getCenter = function(o) { + var c = this.get(o); + if (c==null) { return null; } + c.left = c.left + (c.width/2); + c.top = c.top + (c.height/2); + return c; + }; + + return pos; +})();// CLASS CONSTRUCTOR +// -------------------------------------------------------------------- +var Popup = function(div, options) { + this.div = defined(div)?div:null; + this.index = Popup.maxIndex++; + this.ref = "Popup.objects["+this.index+"]"; + Popup.objects[this.index] = this; + // Store a reference to the DIV by id, also + if (typeof(this.div)=="string") { + Popup.objectsById[this.div] = this; + } + if (defined(this.div) && this.div!=null && defined(this.div.id)) { + Popup.objectsById[this.div.id] = this.div.id; + } + // Apply passed-in options + if (defined(options) && options!=null && typeof(options)=="object") { + for (var i in options) { + this[i] = options[i]; + } + } + return this; +}; + +// CLASS PROPERTIES +// -------------------------------------------------------------------- +// Index of popup objects, to maintain a global reference if necessary +Popup.maxIndex = 0; +Popup.objects = {}; +Popup.objectsById = {}; + +// The z-index value that popups will start at +Popup.minZIndex = 101; + +// Class names to assign to other objects +Popup.screenClass = "PopupScreen"; +Popup.iframeClass = "PopupIframe"; +Popup.screenIframeClass = "PopupScreenIframe"; + +// CLASS METHODS +// -------------------------------------------------------------------- + +// Hide all currently-visible non-modal dialogs +Popup.hideAll = function() { + for (var i in Popup.objects) { + var p = Popup.objects[i]; + if (!p.modal && p.autoHide) { + p.hide(); + } + } +}; +// Catch global events as a trigger to hide auto-hide popups +Event.add(document, "mouseup", Popup.hideAll, false); + +// A simple class method to show a popup without creating an instance +Popup.show = function(divObject, referenceObject, position, options, modal) { + var popup; + if (defined(divObject)) { + popup = new Popup(divObject); + } + else { + popup = new Popup(); + popup.destroyDivOnHide = true; + } + if (defined(referenceObject)) { popup.reference = DOM.resolve(referenceObject); } + if (defined(position)) { popup.position = position; } + if (defined(options) && options!=null && typeof(options)=="object") { + for (var i in options) { + popup[i] = options[i]; + } + } + if (typeof(modal)=="boolean") { + popup.modal = modal; + } + popup.destroyObjectsOnHide = true; + popup.show(); + return popup; +}; + +// A simple class method to show a modal popup +Popup.showModal = function(divObject, referenceObject, position, options) { + Popup.show(divObject, referenceObject, position, options, true); +}; + +// A method to retrieve a popup object based on a div ID +Popup.get = function(divId) { + if (defined(Popup.objectsById[divId])) { + return Popup.objectsById[divId]; + } + return null; +}; + +// A method to hide a popup based on a div id +Popup.hide = function(divId) { + var popup = Popup.get(divId); + if (popup!=null) { + popup.hide(); + } +}; + +// PROTOTYPE PROPERTIES +// -------------------------------------------------------------------- +Popup.prototype.content = null; +Popup.prototype.className = "PopupDiv"; +Popup.prototype.style = null; // Styles to be applied to the DIV +Popup.prototype.width = null; +Popup.prototype.height = null; +Popup.prototype.top = null; +Popup.prototype.left = null; +Popup.prototype.offsetLeft = 0; +Popup.prototype.offsetTop = 0; +Popup.prototype.constrainToScreen = true; +Popup.prototype.autoHide = true; +Popup.prototype.useIframeShim = false; /*@cc_on @*/ /*@if (@_win32) {Popup.prototype.useIframeShim = true;} @end @*/ +Popup.prototype.iframe = null; +Popup.prototype.position = null; // vertical: "above top center bottom below", horizontal: "adjacent-left,left,center,right,adjacent-right" +Popup.prototype.reference = null; +Popup.prototype.modal = false; +Popup.prototype.destroyDivOnHide = false; +Popup.prototype.destroyObjectsOnHide = false; +Popup.prototype.screen = null; +Popup.prototype.screenIframeShim = null; +Popup.prototype.screenOpacity=.4; +Popup.prototype.screenColor="#cccccc"; + +// INSTANCE METHODS +// -------------------------------------------------------------------- + +// Show the popup +// -------------------------------------------------------------------- +Popup.prototype.show = function(options, modal) { + this.modal = this.modal || (typeof(modal)=="boolean" && modal); + if (defined(options) && options!=null && typeof(options)=="object") { + for (var i in options) { + this[i] = options[i]; + } + } + this.div = DOM.resolve(this.div); + CSS.setStyle(this.div,'position','absolute'); + + // If there is no div pre-defined to use, create one + if (this.div==null) { + this.div = this.createDiv(); + } + if (this.content!=null) { + this.div.innerHTML = this.content; + this.content = null; + } + if (this.className!=null) { + this.div.className = this.className; + } + if (this.style!=null) { + this.applyStyle(); + } + if (this.width!=null) { + this.div.style.width = this.width+"px"; + this.div.style.overflowX="auto"; + } + if (this.height!=null) { + this.div.style.height = this.height+"px"; + this.div.style.overflowY="auto"; + } + + // Do the actual display - this is a separate method so display transitions can be implemented + this.transition(); + + // Make sure clicks on the DIV don't bubble up to the document + this.div.onclick = function(e) { + Event.cancelBubble(Event.resolve(e)); + }; + this.div.onmouseup = this.div.onclick; + + // Focus to the DIV if possible + if (this.modal && this.div.focus) { + this.div.focus(); + } +}; + +// Show the popup but make it modal +// -------------------------------------------------------------------- +Popup.prototype.transition = function() { + if (this.modal) { + this.addScreen(); + } + + // Make the DIV displayed but hidden so its size can be measured + CSS.setStyle(this.div,'visibility','hidden'); + CSS.setStyle(this.div,'display','block'); + + // Position the popup + this.setPosition(); + + // Add the shim if necessary + if (this.useIframeShim) { + this.addIframeShim(); + } + + // Make sure the DIV is higher than the shim + this.div.style.zIndex = Popup.minZIndex++; + + CSS.setStyle(this.div,'display','block'); + CSS.setStyle(this.div,'visibility','visible'); +}; + +// Show the popup but make it modal +// -------------------------------------------------------------------- +Popup.prototype.showModal = function(options) { + this.show(options,true); +}; + +// Apply user styles to the DIV +// -------------------------------------------------------------------- +Popup.prototype.applyStyle = function() { + if (this.div!=null && this.style!=null && typeof(this.style)=="object") { + for (var i in this.style) { + this.div.style[i] = this.style[i]; + } + } +}; + +// Hide the popup +// -------------------------------------------------------------------- +Popup.prototype.hide = function() { + // If this was a temp object creating on-the-fly, then remove objects from the DOM so + // The document doesn't get littered with extra objects + if (this.destroyDivOnHide) { + DOM.removeNode(this.div); + this.div = null; + delete Popup.objects[this.id]; + } + else if (this.div!=null) { + CSS.setStyle(this.div,'display','none'); + } + + if (this.destroyObjectsOnHide) { + DOM.removeNode(this.iframe); + DOM.removeNode(this.screen); + DOM.removeNode(this.screenIframeShim); + } + else { + if (this.iframe!=null) { + this.iframe.style.display = "none"; + } + if (this.screen!=null) { + this.screen.style.display = "none"; + } + if (this.screenIframeShim!=null) { + this.screenIframeShim.style.display = "none"; + } + } +}; + +// Util funcs for position +// -------------------------------------------------------------------- +Popup.prototype.setTop = function(top) { + this.div.style.top = top+"px"; +}; +Popup.prototype.setLeft = function(left) { + this.div.style.left = left+"px"; +}; +Popup.prototype.getTop = function() { + return parseInt(CSS.getStyle(this.div,"top"),10); +}; +Popup.prototype.getLeft = function() { + return parseInt(CSS.getStyle(this.div,"left"),10); +}; + +// All the logic to position the popup based on various criteria +// -------------------------------------------------------------------- +Popup.prototype.setPosition = function() { + if (this.position!=null) { + var m = this.position.match(/^(\S+)\s+(\S+)/); + if (m!=null && m.length==3) { + var v = m[1]; + var h = m[2]; + + var ref = this.reference; + if (ref==null) { ref = Screen.getBody(); } + var p = Position.get(ref); + var refTop = p.top; + var refLeft = p.left; + var refWidth = DOM.getOuterWidth(ref); + var refHeight = DOM.getOuterHeight(ref); + + var width = DOM.getOuterWidth(this.div); + var height = DOM.getOuterHeight(this.div); + + var scrollLeft = Screen.getScrollLeft(); + var scrollTop = Screen.getScrollTop(); + + // Set vertical position relative to reference object + if (v=="above") { this.setTop(refTop-height+this.offsetTop); } + else if (v=="top") { this.setTop(refTop+this.offsetTop); } + else if (v=="center") { this.setTop(refTop+(refHeight/2)-(height/2)+this.offsetTop); } + else if (v=="bottom") { this.setTop(refTop+refHeight-height+this.offsetTop); } + else if (v=="below") { this.setTop(refTop+refHeight+this.offsetTop); } + + // Set horizontal position relative to reference object + if (h=="adjacent-left") { this.setLeft(refLeft-width+this.offsetLeft); } + else if (h=="left") { this.setLeft(refLeft+this.offsetLeft); } + else if (h=="center") { this.setLeft(refLeft+(refWidth/2)-(width/2)+this.offsetLeft); } + else if (h=="right") { this.setLeft(refLeft+refWidth-width+this.offsetLeft); } + else if (h=="adjacent-right") { this.setLeft(refLeft+refWidth+this.offsetLeft); } + } + } + else if (this.top==null && this.left==null) { + this.center(); + } + else { + if (this.top==null) { this.top=0; } + if (this.left==null) { this.left=0; } + this.div.style.top = this.top+this.offsetTop+"px"; + this.div.style.left = this.left+this.offsetLeft+"px"; + } + + // Re-position to make sure it stays on the screen + if (this.constrainToScreen) { + this.fitToScreen(); + } +}; + +// Append an object to the body +// -------------------------------------------------------------------- +Popup.prototype.appendToBody = function(o) { + var body = Screen.getBody(); + if (body && body.appendChild) { + body.appendChild(o); + } +}; + +// Create a new DIV object to be used for a popup +// -------------------------------------------------------------------- +Popup.prototype.createDiv = function() { + if (document.createElement) { + var d = document.createElement("DIV"); + d.style.position="absolute"; + d.style.display="block"; + d.style.visibility="hidden"; + this.appendToBody(d); + return d; + } + alert("ERROR: Couldn't create DIV element in Popup.prototype.createDiv()"); + return null; +}; + +// Create a new IFRAME object to be used behind the popup +// -------------------------------------------------------------------- +Popup.prototype.createIframe = function() { + if (document.createElement) { + var i= document.createElement("IFRAME"); + i.style.position="absolute"; + i.style.display="block"; + i.style.visibility="hidden"; + i.style.background="none"; + this.appendToBody(i); + return i; + } + else { + alert("ERROR: Couldn't create IFRAME object in Popup.prototype.createIframe()"); + } +}; + +// Add an IFRAME shim for the DIV +// -------------------------------------------------------------------- +Popup.prototype.addIframeShim = function() { + if (this.iframe==null) { + this.iframe = this.createIframe(); + } + this.iframe.className = Popup.iframeClass; + CSS.setStyle(this.iframe,'top',this.getTop()+"px"); + CSS.setStyle(this.iframe,'left',this.getLeft()+"px"); + CSS.setStyle(this.iframe,'width',DOM.getOuterWidth(this.div) + "px"); + CSS.setStyle(this.iframe,'height',DOM.getOuterHeight(this.div) + "px"); + CSS.setStyle(this.iframe,'zIndex',Popup.minZIndex++); + CSS.setStyle(this.iframe,'opacity',0); + CSS.setStyle(this.iframe,'visibility','visible'); + CSS.setStyle(this.iframe,'display','block'); +}; + +// Create a "screen" to make a popup modal +// -------------------------------------------------------------------- +Popup.prototype.addScreen = function() { + if (this.screen==null) { + this.screen = this.createDiv(); + this.screen.style.top="0px"; + this.screen.style.left="0px"; + this.screen.style.backgroundColor = this.screenColor; + this.screen.className=Popup.screenClass;; + CSS.setStyle(this.screen,"opacity",this.screenOpacity); + this.screen.onclick = function(e) { Event.cancelBubble(Event.resolve(e)); } + } + if (this.screenIframeShim==null) { + this.screenIframeShim = this.createIframe(); + this.screenIframeShim.style.top="0px"; + this.screenIframeShim.style.left="0px"; + this.screenIframeShim.className=Popup.screenIframeClass; + CSS.setStyle(this.screenIframeShim,"opacity",0); + } + this.screen.style.width = Screen.getDocumentWidth()+"px"; + this.screen.style.height = Screen.getDocumentHeight()+"px"; + this.screenIframeShim.style.width = Screen.getDocumentWidth()+"px"; + this.screenIframeShim.style.height = Screen.getDocumentHeight()+"px"; + this.screenIframeShim.style.zIndex = Popup.minZIndex++; + this.screenIframeShim.style.visibility="visible"; + this.screenIframeShim.style.display="block"; + this.screen.style.zIndex = Popup.minZIndex++; + this.screen.style.visibility="visible"; + this.screen.style.display="block"; +}; + +// Re-position the DIV so it stays on the screen +// -------------------------------------------------------------------- +Popup.prototype.fitToScreen = function() { + var width = DOM.getOuterWidth(this.div); + var height = DOM.getOuterHeight(this.div); + var top = this.getTop(); + var left = this.getLeft(); + + var clientWidth = Screen.getViewportWidth(); + var clientHeight = Screen.getViewportHeight(); + + var scrollLeft = Screen.getScrollLeft(); + var scrollTop = Screen.getScrollTop(); + + if (top-scrollTop+height>clientHeight) { + top = top - ((top+height) - (scrollTop+clientHeight)); + this.div.style.top = top + "px"; + } + if (left-scrollLeft+width>clientWidth) { + left = left - ((left+width) - (scrollLeft+clientWidth)); + this.div.style.left = left + "px"; + } + if (topeAA-4QK!nlZm zfsvIF3>ct*k%^gwm5rT)lZ%^?ff*tPBq6d$k^&4&jLghTEX=H|EG$4{YJqYLEP|{; ziiVDC!hwnGN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXb ztDC!rr&n-DXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g# zUABD1%2k^-Z`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT6sAHsIig>XiL))b}k321|c8>nspdx@(iG{%BBu(K&!4Y9QI+mEHH&37-Vju zLldT>k$fT&#Q10e(7~!v)3CT7ZvHIBwv~*6t02B`<~5MYY`B#y*2w8_bb+G-D>Ii9 JS3>>&n*dJ776JeO literal 0 HcmV?d00001 diff --git a/style/summary.xsl b/style/summary.xsl new file mode 100644 index 0000000..020ad17 --- /dev/null +++ b/style/summary.xsl @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + \n + + + + +
+ + + +
+ + + +
+
+
diff --git a/style/testresult.xsl b/style/testresult.xsl new file mode 100644 index 0000000..15a1ed5 --- /dev/null +++ b/style/testresult.xsl @@ -0,0 +1,571 @@ + + + + + + + + + + + + + + + \n + + + + +
+ + + +
+ + + +
+
+
diff --git a/style/tests.css b/style/tests.css new file mode 100644 index 0000000..487bca0 --- /dev/null +++ b/style/tests.css @@ -0,0 +1,195 @@ +@charset "UTF-8"; +/* CSS Document */ +#testcasepage div,#testcasepage h1,#testcasepage p,#testcasepage table,#testcasepage tr,#testcasepage th,#testcasepage td + { + margin: 0; + padding: 0; + border: 0; + font-weight: inherit; + font-style: inherit; + font-size: 0.96em; + font-family: arial; + vertical-align: baseline; +} + +#title td, #btc td{ + margin: 0; + padding: 0; + border: 0; + font-weight: inherit; + font-style: inherit; + font-size: 0.96em; + font-family: arial; + vertical-align: baseline; +} + +td.Ratio { + text-align: left; + font-weight: normal; + padding: 4px 10px 4px 5px; + vertical-align: middle; +} + +th.Ratio { + width: 400px; +} + +#testcasepage p { + text-align: left; +} + +#suite_title { + text-align: left; +} + +#btc { + text-align: right; +} + +#btc table { + position: absolute; + right: 0px; + width: 600px; +} + +#testcasepage table { + border-collapse: separate; + border-spacing: 0; + margin-bottom: 1.4em; + vertical-align: middle; +} + +#testcasepage th,#testcasepage td { + text-align: left; + font-weight: normal; + padding: 4px 10px 4px 5px; + vertical-align: middle; +} + +#cases table { + width: 101%; +} + +#cases td { + border-left: 0px; + font-weight: normal; + border-bottom: 0px; +} + +#suite_summary table { + width: 100%; +} + + +#overview table { + width: 101%; +} + +#overview table, #overview td, #overview tr { + border-left: none; + border-bottom: none; + border-right: none; + vertical-align: top; +} + +#overview td{ + width: 50%; +} + +#capability table { + width: 50%; +} + +#fail_cases table { + width: 101%; +} + +#title table { + width: 101%; +} + +#device table { + width: 100%; +} + +#summary table { + width: 100%; +} + +#testcasepage th { + border-bottom: 1px solid #000; + background-color: #AAAAAA; + border-left: 1px solid #000; + border-top: 1px solid #000; + color: #000; + font-weight: bold; + vertical-align: bottom; +} + +#summary th:last-child,#summary td:last-child, #device th:last-child,#device td:last-child, #suite_summary th:last-child,#suite_summary td:last-child,#cases th:last-child,#cases td:last-child,#capability th:last-child,#capability td:last-child { + border-right: 1px solid #000; +} + +#testcasepage td { + font-weight: normal; +} + +#summary td, #device td, #capability td, #suite_summary td, #cases td{ + border-left: 1px solid; + font-weight: normal; + border-bottom: 1px solid; +} + +#testcasepage td.yellow_rate { + background-color: #ffcc00; +} + +#testcasepage td.green_rate { + background-color: #1E90FF; +} + +#testcasepage td.dgreen_rate { + background-color: #339933; +} + +#testcasepage td.red_rate { + background-color: #FF3333; +} + +#testcasepage td.orange_rate { + background-color: #FFA500; +} + +#testcasepage td.gray_rate { + background-color: #AAAAAA; +} + +#title table,#title tr,#title td { + border-left: none; + border-bottom: none; + text-align: center; +} + +#title td:last-child { + border-right: none; +} + +#testcasepage h1 { + font-size: 2em; + font-family: Arial, sans-serif; + font-weight: bold; + line-height: 1; + color: #000; + margin-bottom: 0.75em; + padding-top: 0.25em; + font-weight: bold; +} + +#goTopBtn { + right: 0px; + bottom: 0px; + position: fixed; + + position: absolute; + top: expression(parseInt(document.body.scrollTop)+document.body.clientHeight-40 + ); +} diff --git a/tct-plan-generator b/tct-plan-generator new file mode 100755 index 0000000..3f7c80f --- /dev/null +++ b/tct-plan-generator @@ -0,0 +1,52 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + + +import os +import re +import sys, traceback +import time +import platform +import ctypes +import glob +import ConfigParser +import xml.etree.ElementTree as etree +from optparse import * +from shutil import copyfile +from tempfile import mktemp +from datetime import datetime +from tctshell.plan_options import PlanGeneratorOptions +from tctshell.plan_runner import PlanRunner + + +def main(): + wrapper = PlanGeneratorOptions() + wrapper.parse_options(sys.argv) + output = wrapper.get_output() + + runner = PlanRunner() + runner.load_local_repo(wrapper.get_repository_folder(), wrapper.get_match_regex(), wrapper.get_command(), wrapper.get_unmatch_regex(),wrapper.get_plan_name(),wrapper.get_execute_type()) + + runner.to_xml(output) + +if __name__ == "__main__": + main() + diff --git a/tct-shell b/tct-shell new file mode 100644 index 0000000..5b310d9 --- /dev/null +++ b/tct-shell @@ -0,0 +1,89 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + + +import os +import re +import sys, traceback +import time +import platform +import ctypes +import glob +import ConfigParser +import xml.etree.ElementTree as etree +from optparse import * +from shutil import copyfile +from tempfile import mktemp +from datetime import datetime +from tctshell.shellwrapper import TestKitShellWrapper +from tctshell.shellrunner import WrapperRunner +from tctshell.shellrunner import Guider + + +def main(): + wrapper = TestKitShellWrapper() + wrapper.parse_options(sys.argv) + runner = WrapperRunner() + guider = Guider(runner,wrapper.get_sdb_device_id_param()) + plan = None + suites = [] + result_xml = wrapper.get_output_file_name() + origin_xml = result_xml + + if wrapper.check_sdb_devices() and wrapper.check_sdb_devices_env(): + runner.pull_device_capabilities(wrapper) + runner.pull_build_info(wrapper) + runner.pull_preconfigure_json(wrapper) + guider.init_items() + if wrapper.options.testplan_file is not None: + print "ExecuteTestPlan: %s" % wrapper.options.testplan_file[0] + plan = wrapper.options.testplan_file[0] + elif wrapper.is_suite_mode(): + print "ExecuteTestSuites:" + suites = wrapper.get_suites() + if wrapper.options.fail_result_xml is not None: + origin_xml = os.path.abspath(wrapper.options.fail_result_xml[0]) + guider.set_conf_path(os.path.dirname(origin_xml)) + + if not runner.prepare_testplan(plan, wrapper.running_mode, suites, origin_xml): + sys.exit(1) + + print "is_enable_preconf_func: ", wrapper.is_enable_preconf_func() + if wrapper.is_enable_preconf_func(): + guider.setup_env() + + runner.execute_suite_in_loop(wrapper,guider) + + runner.merge_suite_result(result_xml, wrapper.get_plan_name()) + + if wrapper.is_fail_rerun_mode(): + runner.merge_result_xml(origin_xml, result_xml) + + runner.copy_result_xml(result_xml) + runner.copy_preconfigure_file() + + wrapper.print_result_summary(result_xml) + runner.open_report(wrapper) + + +if __name__ == "__main__": + main() + diff --git a/tctshell/__init__.py b/tctshell/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tctshell/__init__.py @@ -0,0 +1 @@ + diff --git a/tctshell/constants.py b/tctshell/constants.py new file mode 100644 index 0000000..6a51d5d --- /dev/null +++ b/tctshell/constants.py @@ -0,0 +1,195 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + +import ConfigParser +import os,os.path +import zipfile +import subprocess +import time +import shutil +from commodule.killall import killall + +CONFIG_FILE = "/opt/tct/shell/CONFIG" + +parser = ConfigParser.ConfigParser() +parser.read(CONFIG_FILE) + +class Constants: + + TEST_SUITE_DIR = parser.get('TCTSHELL', 'TEST_SUITE_DIR') + + WRT_LAUNCHR_CMD = parser.get('TCTSHELL', 'WRT_LAUNCHR_CMD') + + RELEASED_SUITE_LOCATION = TEST_SUITE_DIR + "%s" + + TCT_SHELL_HOME = parser.get('TCTSHELL', 'TCT_SHELL_HOME') + + TEMP_UNPASSED_XML_FOLDER = TCT_SHELL_HOME + "/tmp/" + + TEMP_RESULT_XML_FOLDER = TCT_SHELL_HOME + "/result/" + + if not TEMP_UNPASSED_XML_FOLDER.endswith('/'): + TEMP_UNPASSED_XML_FOLDER = TEMP_UNPASSED_XML_FOLDER + "/" + + RERUN_OUTPUT = " -o " + TEMP_UNPASSED_XML_FOLDER + "result.xml" + + CAPABILITY_SUITE_NAME = "capability-tests" + + TCT_PLAN_FOLDER = parser.get('TCTSHELL', 'TCT_PLAN_FOLDER') + + STYLE_FOLDER = TCT_SHELL_HOME + "/style/*" + + CAPABILITY_PATH = TCT_SHELL_HOME + "/tmp/capability.xml" + + BUILD_INFO_PATH = TCT_SHELL_HOME + "/tmp/buildinfo.xml" + + DEVICE_CAPABILITY_PATH = parser.get('Device', 'DEVICE_CAPABILITY_PATH') + + DEVICE_BUILD_INFO_PATH = parser.get('Device', 'DEVICE_BUILD_INFO_PATH') + + DEVICE_DEFAULT_CONF = parser.get('Device', 'DEVICE_DEFAULT_CONF_PATH') + + DEVICE_DEFAULT_JSON_CONF = parser.get('Device', 'DEVICE_DEFAULT_ISON_CONF_PATH') + + DEVICE_HEALTH_CHECK_CMD = "/opt/tct/scripts/tct-config-device.sh --check" + + PRE_CONFIGURE_FILE_PATH = "/opt/tct/shell/tmp/pre_configure.conf" + + PRE_CONFIGURE_ISON_FILE_PATH = "/opt/tct/shell/tmp/preconfigure.json" + + DEVICE_CONFIGURE_FILE_PATH = parser.get('Device','DEVICE_PRECONFIGURE_FILE_PATH') + + TCT_TESTCONFIG_PATH = "/opt/usr/media/tct/opt/tct-testconfig/tests.xml" + + HEALTH_CHECK_FILE_PATH = "/opt/tct/scripts/healthcheck.ini" + + #PARAM Option + XMLFILE_PREFF = "-f device:" + EXECUTE_PREFF = " -e " + ONLY_AUTO_CASES = "-A" + ONLY_MANUAL_CASES = "-M" + + #SDB Command + SDB_SHELL_TIMEOUT = 90 + SDB_PUSH = "sdb %s push" + SDB_SHELL = "sdb %s shell" + SDB_DEVICES = "sdb devices" + SDB_PULL = "sdb %s pull" + SDB_CAT_CONFIG = " cat %s" + SDB_GET_DLOG_PRO = SDB_SHELL + " dlogctrl get platformlog" + + + #DEVICE + DEVICE_TMP_FOLDER = parser.get('Device', 'DEVICE_TMP_FOLDER') + DEVICE_ZIP_PATH = "/opt/usr/media/tct" + DEVICE_TESTS_FILE = parser.get('Device', 'DEVICE_TESTS_FILE') + " " + + #Testkit-Lite + LITE_HOME = parser.get('TESTKIT_LITE', 'LITE_HOME') + + INSTALL_RPM = "rpm -i --nodeps /tmp/%s" + UNINSTALL_RPM = "rpm -e --nodeps %s" + REMOVE_RPM = "rm -f /tmp/%s" + INSTALL_ZIP = "inst.sh" + UNINTALL_ZIP = "uninst.sh" + + #Plan Generator + DEFAULT_MATCH_REGEX = "*.zip" + DEFAULT_UNMATCH_REGEX = None + DEFAULT_PLAN_NAME = "Full_test" + DEFAULT_EXECUTE_TYPE = "All" + DEFAULT_PLAN_FILE = "tct_plan" + + REGEX_FOR_SPLIT_PKG_NAME = "-\d\.\d\.\d" + + #Running Mode + RUNNING_MODE_PLAN = "plan" + RUNNING_MODE_RESULT = "result" + RUNNING_MODE_SUITES = "suites" + + @staticmethod + def unzip_package(filepath): + path = os.path.expanduser("~") + os.chdir(path) + if os.path.exists(Constants.DEFAULT_PLAN_FILE): + shutil.rmtree(Constants.DEFAULT_PLAN_FILE) + os.makedirs(Constants.DEFAULT_PLAN_FILE) + f = zipfile.ZipFile(filepath,'r') + #print "new path: ", new_path + for file in f.namelist(): + f.extract(file,path + "/"+Constants.DEFAULT_PLAN_FILE) + + @staticmethod + def clean_unzipf_ile(): + os.chdir(os.path.expanduser("~")) + shutil.rmtree(Constants.DEFAULT_PLAN_FILE) + + @staticmethod + def add_right_enbrace(param): + param = param.rstrip() + param += "\"" + return param + + @staticmethod + def indent(elem, level=0): + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + Constants.indent(elem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + @staticmethod + def copy_style_in_result_folder(result_folder,wrapper): + os.system("cp -r " + Constants.STYLE_FOLDER + " '" + result_folder + "'") + print "Going to open the result report with FireFox, if Firefox is not available on your machine, please copy the folder '%s' to a Firefox machine." % (result_folder + "summary.xml") + print "If you want to rerun,please run cmd: tct-shell --rerun-fail '%s'" % (result_folder + "result.xml") + wrapper.is_manual_or_all_param() + os.system("firefox '%s'summary.xml &" % result_folder) + + @staticmethod + def execute_sdb_command(command): + proc = subprocess.Popen(command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + time_out = 0 + result = [] + exit_code = None + while time_out < Constants.SDB_SHELL_TIMEOUT: + exit_code = proc.poll() + if exit_code is not None:break + time_out += 0.5 + time.sleep(0.5) + if exit_code is None: + print "sdb time out,kill subprocess..." + killall(proc.pid) + else: + result = proc.stdout or proc.stderr + + return exit_code, result + diff --git a/tctshell/plan_options.py b/tctshell/plan_options.py new file mode 100644 index 0000000..d6ae415 --- /dev/null +++ b/tctshell/plan_options.py @@ -0,0 +1,188 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + + +import os +import re +import sys, traceback +import time +import platform +import ctypes +import glob +import ConfigParser +import xml.etree.ElementTree as etree +from optparse import * +from shutil import copyfile +from tempfile import mktemp +from datetime import datetime +from constants import Constants + +#show all available suites +def show_available_suites(option, opt_str, value, parser): + if platform.system() != "Linux": + Constants.TEST_SUITE_DIR = "../suites" + print "Test Suites:...\n" + os.chdir(Constants.TEST_SUITE_DIR) + for files in glob.glob("*.rpm"): + #without ".rpm" + print files[:-4] + sys.exit(1) + +def print_planfolder(option, opt_str, value, parser): + print "Plan folder: %s" % Constants.TCT_PLAN_FOLDER + os.system("tree %s" % Constants.TCT_PLAN_FOLDER) + sys.exit(1) + +def varnarg(option, opt_str, value, parser): + """ parser srg""" + value = [] + import re + for arg in parser.rargs: + if re.search('^--.+', arg) or re.search('^-[\D]', arg): + break + value.append(arg) + + del parser.rargs[:len(value)] + setattr(parser.values, option.dest, value) + +class PlanGeneratorOptions: + + def __init__(self): + print "" + self._j = os.path.join + self._e = os.path.exists + self._d = os.path.dirname + self._b = os.path.basename + self._abspath = os.path.abspath + self.testkit_dir = "/opt/testkit/lite" + self.LOG_DIR = self.testkit_dir + self.PID_FILE = self._j(self.LOG_DIR , "pid.log") + if not platform.system() == "Linux": + self.testkit_dir = self._d(self._abspath(__file__)) + sys.path += [self._j(self.testkit_dir)] + self.testkit_dir = self._j(self.testkit_dir , "results") + #self.clean_context() + self.options = None + self.options = None + self.running_mode = None # plan suite result + self.USAGE = "tct-plan-generator [options] --output \n\ +examples: \n\ + tct-plan-generator -o /testplan.xml\n\ + tct-plan-generator -o /testplan.xml -r /repository_folder\n\ + tct-plan-generator -o /testplan.xml -r /repository_folder --match ''\n\ +\n\ + generate a test plan to include all suites in the local repository: \n\ + tct-plan-generator -o /testplan.xml\n\ + generate a test plan to include all suites in the special repository: \n\ + tct-plan-generator -o /testplan.xml -r /repository_folder\n\ + generate a test plan to include the suites in the special repository which name is matched with 'webapi*.rpm': \n\ + tct-plan-generator -o /testplan.xml -r /repository_folder --match 'webapi*.rpm'\n\ + generate a test plan to include the suites in the special repository which name is matched with 'webapi*.rpm', and exclude the file 'webapi-tizen-push-tests-2.1.6-1.1.armv7l.rpm': \n\ + tct-plan-generator -o /testplan.xml -r /repository_folder --match 'webapi*.rpm' --unmatch push\n\ +\n\ +Note: \n\ + 1) run command 'tct-plan-generator', it might not be able to locate related module, run command 'export PYTHONPATH=/usr/lib/python2.7/site-packages' to resolve this issue" + + + def print_usage(self): + print self.USAGE + + def parse_options(self, argv): + option_list = [ + make_option("-o", "--output", dest="testplan_file", + action="callback", callback=varnarg, + help="Specify the generating testplan in a XML file."), + make_option("-e", "--execute", dest="execute_type", + action="callback", callback=varnarg, + help="Specify the execute_type of testplan in a XML file."), + make_option("-r", "--repository", dest="repository_folder", + action="callback", callback=varnarg, + help="Specify the path of local repository."), + make_option("-m", "--match", dest="match_regex", + action="callback", callback=varnarg, + help="The regex for matching filename."), + make_option("-u", "--unmatch", dest="unmatch_regex", + action="callback", callback=varnarg, + help="The regex for unmatching filename."), + make_option("--plan-list", dest="show_plan_folder", + action="callback", callback=print_planfolder, + help="List all existed plan in the Plan folder. \ + The plan folder is defined in the configure '/opt/tct/shell/CONF'"), + make_option("-a", "--all-suites", dest="show_suites", + action="callback", callback=show_available_suites, + help="Show all available test-suites"), + make_option("-c", "--command", dest="command", + action="callback", callback=varnarg, + help="The command which will be used to start a widget. \ + Possible value: WRTLauncher, firefox, and/or other browser with appropriate parameters.\ + By default, the command is 'WRTLauncher'"), + ] + # detect non-params + if len(argv) == 1: + argv.append("-h") + PARSERS = OptionParser(option_list=option_list, usage=self.USAGE) + (self.options, args) = PARSERS.parse_args() + + def get_repository_folder(self): + if (self.options.repository_folder is not None) and (self.options.repository_folder[0] is not None): + return self.options.repository_folder[0] + else: + return Constants.TEST_SUITE_DIR + + def get_match_regex(self): + if (self.options.match_regex is not None) and (self.options.match_regex[0] is not None): + return self.options.match_regex[0] + else: + return Constants.DEFAULT_MATCH_REGEX + + def get_command(self): + if (self.options.command is not None) and (self.options.command[0] is not None): + return self.options.command[0] + else: + return Constants.WRT_LAUNCHR_CMD + + def get_plan_name(self): + if (self.options.testplan_file is not None) and (self.options.testplan_file[0] is not None): + name, ext = os.path.splitext(self.options.testplan_file[0]) + return name.split("/")[-1] + else: + return Constants.DEFAULT_PLAN_NAME + + def get_execute_type(self): + if (self.options.execute_type is not None) and (self.options.execute_type[0] is not None): + return self.options.execute_type[0] + else: + return Constants.DEFAULT_EXECUTE_TYPE + + def get_output(self): + if (self.options.testplan_file is not None) and (self.options.testplan_file[0] is not None): + d = os.path.abspath(os.path.dirname(self.options.testplan_file[0])) + if not os.path.exists(d): + os.makedirs(d) + return os.path.abspath(self.options.testplan_file[0]) + else: + return Constants.TCT_PLAN_FOLDER + "generated_plan.xml" + + def get_unmatch_regex(self): + if (self.options.unmatch_regex is not None) and (self.options.unmatch_regex[0] is not None): + return self.options.unmatch_regex[0] + else: + return Constants.DEFAULT_UNMATCH_REGEX diff --git a/tctshell/plan_runner.py b/tctshell/plan_runner.py new file mode 100644 index 0000000..d7a01e6 --- /dev/null +++ b/tctshell/plan_runner.py @@ -0,0 +1,135 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + +from constants import Constants +from xml.etree import ElementTree +import xml.etree.ElementTree as etree +import os,os.path +import zipfile +import glob +import re + +class SuitePackage: + def __init__(self, file_name, command,auto_count,manual_count): + self.file = file_name + m = re.split(Constants.REGEX_FOR_SPLIT_PKG_NAME, file_name) + self.name = m[0] + self.auto_count = auto_count + self.manual_count = manual_count + self.total_count = auto_count + manual_count + self.command = command + " %s" % self.name + + def to_xml(self): + suite = ElementTree.Element('suite') + suite.set('name', self.name) + suite.set('launcher', Constants.WRT_LAUNCHR_CMD) + auto = ElementTree.Element('auto_tcn') + auto.text = str(self.auto_count) + suite.append(auto) + manual = ElementTree.Element('manual_tcn') + manual.text = str(self.manual_count) + suite.append(manual) + total = ElementTree.Element('total_tcn') + total.text = str(self.total_count) + suite.append(total) + rpm_name = ElementTree.Element('pkg_name') + rpm_name.text = self.file + suite.append(rpm_name) + return suite + +class PlanRunner: + + def __init__(self): + self.suites = {} + self.plan_name = Constants.DEFAULT_PLAN_NAME + self.execute_type = Constants.DEFAULT_EXECUTE_TYPE + + def load_local_repo(self, path, match, command, unmatch,plan_name,execute_type): + try: + self.plan_name = plan_name + self.execute_type = execute_type + os.chdir(path) + print "match: %s" % match + print "unmatch: %s" % unmatch + for files in glob.glob(match): + print "File name: %s" % str(files) + if (unmatch is not None) and (re.search(unmatch, str(files))): + print "File %s is skipped with unmatch regex %s" % (str(files), unmatch) + else: + auto,manual = self.get_count(files,path + str(files)) + suite = SuitePackage(files, command,auto,manual) + self.suites[suite.name] = suite + Constants.clean_unzipf_ile(); + + except Exception, e: + print "[ Error happen when reading the local repository, error: %s ]\n" % e + + def get_count(self,filesname,filepath): + Constants.unzip_package(filepath) + m = re.split(Constants.REGEX_FOR_SPLIT_PKG_NAME, filesname) + name = m[0] + test_file = os.path.expanduser("~") + "/"+Constants.DEFAULT_PLAN_FILE+"/opt/" + name + "/tests.xml" + if not os.path.exists(test_file): + print("Can't find the test file...") + return 0,0 + return self.__read_test_xml(test_file) + + + def __read_test_xml(self,test_file): + autocnt = manualcnt = 0 + test_xml_temp = etree.parse(test_file) + + for test_xml_temp_suite in test_xml_temp.getiterator('suite'): + suite_name = test_xml_temp_suite.get('name') + autocnt = manualcnt = 0 + for tset in test_xml_temp_suite.getiterator('set'): + set_autocnt, set_manualcnt = self.__get_set_casecnt(tset) + autocnt += set_autocnt + manualcnt += set_manualcnt + return autocnt, manualcnt + + + def __get_set_casecnt(self,tset): + set_autocnt = set_manualcnt = 0 + for tcase in tset.getiterator('testcase'): + exetype = tcase.get('execution_type') + if exetype == "auto": + set_autocnt += 1 + elif exetype == "manual": + set_manualcnt += 1 + return set_autocnt, set_manualcnt + + def to_xml(self, xml_name): + print "generating plan to %s" % xml_name + root = ElementTree.Element('ns3:testplan') + root.set('name', self.plan_name) + root.set('xmlns','') + root.set('xmlns:ns3', 'http://www.example.org/plan/') + element = ElementTree.Element('execute_type') + element.text = self.execute_type + root.append(element) + for suite_name in self.suites: + suite = self.suites[suite_name] + root.append(suite.to_xml()) + Constants.indent(root) + tree = ElementTree.ElementTree() + tree._setroot(root) + tree.write(xml_name, encoding="utf-8") diff --git a/tctshell/shellrunner.py b/tctshell/shellrunner.py new file mode 100644 index 0000000..e9cbde3 --- /dev/null +++ b/tctshell/shellrunner.py @@ -0,0 +1,908 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + +from xml.etree import ElementTree +import os +from constants import Constants +import glob +import datetime +import string +import sys +import ConfigParser +import subprocess +import time +from commodule.killall import killall +from shutil import copy + +class PlanSuite: + def __init__(self, name, files): + self.name = name + self.cmd = "" + self.file_name = files + if self.file_name is None: + print "Error! the package %s is not found!" % name + def get_name(self): + return self.name + + def get_pkg_name(self): + if self.file_name is None: + return None + return self.file_name[0: -4] + + def get_command(self): + return self.cmd + + def to_str(self): + print "Suite: %s" % self.name + #print "Command: %s\n" % self.cmd + + def install_suite(self, wrapper): + push_command = Constants.SDB_PUSH % (wrapper.get_sdb_device_id_param()) + " " + (Constants.RELEASED_SUITE_LOCATION % self.file_name) + " " + Constants.DEVICE_ZIP_PATH + "/" + print "Command: " , push_command + Constants.execute_sdb_command(push_command) + unzip_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + "unzip -o " + Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + " -d " + Constants.DEVICE_ZIP_PATH + print "Unzip command: ", unzip_command + Constants.execute_sdb_command(unzip_command) + install_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + print "Command: " , install_command + Constants.execute_sdb_command(install_command) + + def uninstall_suite(self, wrapper): + uninstall_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + " -u" + print "Command: " , uninstall_command + Constants.execute_sdb_command(uninstall_command) + remove_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " rm -rf "+ Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + print "Command: " , remove_command + Constants.execute_sdb_command(remove_command) + + def get_f(self): + f = Constants.XMLFILE_PREFF + "\"" + Constants.DEVICE_TESTS_FILE % self.get_name() + return Constants.add_right_enbrace(f) + + def get_e(self): + #e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + " %s" % self.get_name() + e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + return Constants.add_right_enbrace(e) + +class PackageSuite: + def __init__(self, name): + self.name = name + self.file_name = None + os.chdir(Constants.TEST_SUITE_DIR) + for files in glob.glob("*.zip"): + if files.startswith(name): + self.file_name = files + break + if self.file_name is None: + print "Error! the package %s is not found!" % name + + def get_name(self): + return self.name + + def get_pkg_name(self): + if self.file_name is None: + return None + return self.file_name[0: -4] + + def to_str(self): + print "Suite: %s" % self.name + print "Package File: %s\n" % self.file_name + + def install_suite(self, wrapper): + push_command = Constants.SDB_PUSH % (wrapper.get_sdb_device_id_param()) + " " + (Constants.RELEASED_SUITE_LOCATION % self.file_name) + " " + Constants.DEVICE_ZIP_PATH + "/" + print "Command: " , push_command + Constants.execute_sdb_command(push_command) + unzip_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + "unzip -o " + Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + " -d " + Constants.DEVICE_ZIP_PATH + print "Unzip command: ", unzip_command + Constants.execute_sdb_command(unzip_command) + install_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + print "Command: " , install_command + Constants.execute_sdb_command(install_command) + + def uninstall_suite(self, wrapper): + uninstall_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + " -u" + print "Command: " , uninstall_command + Constants.execute_sdb_command(uninstall_command) + remove_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " rm -rf "+ Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + print "Command: " , remove_command + Constants.execute_sdb_command(remove_command) + + def get_f(self): + f = Constants.XMLFILE_PREFF + "\"" + Constants.DEVICE_TESTS_FILE % self.get_name() + return Constants.add_right_enbrace(f) + + def get_e(self): + #e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + " %s" % self.get_name() + e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + return Constants.add_right_enbrace(e) + +class RerunFailSuite: + def __init__(self, suite): + self.name = suite.get("name") + self.file_name = None + os.chdir(Constants.TEST_SUITE_DIR) + for files in glob.glob("*.zip"): + if files.startswith(self.name): + self.file_name = files + break + if self.file_name is None: + print "Error! the package %s is not found!" % self.name + + self.suite = suite + print "parsing the suite: " + self.name + i = 0 + if (self.suite is not None) and (self.suite.findall('set/testcase') is not None): + print "Case number: " + str(len(self.suite.findall('set/testcase'))) + for testset in self.suite.findall("set"): + for testcase in testset.findall('testcase'): + i = i+1 + result = testcase.get("result") + if (result is not None) and (cmp(result, "PASS") == 0): + #print "Removing testcase: " + testcase.get("purpose") + testset.remove(testcase) + else: + testcase.remove(testcase.find('result_info')) + testcase.set('result', "") + print "Unpassed Case number: " + str(len(self.suite.findall('set/testcase'))) + + def is_empty(self): + return (self.suite.findall('set/testcase') is None) + + def to_str(self): + print "Suite: %s" % self.name + print "Package File: %s\n" % self.file_name + + def to_xml(self): + xml_name = self.name + ".xml" + print "generating XML "+ xml_name + root = ElementTree.Element('test_definition') + root.append(self.suite) + tree = ElementTree.ElementTree() + tree._setroot(root) + tree.write(xml_name, encoding="utf-8") + + def get_rerunxml_name(self): + folder_name = Constants.TEMP_UNPASSED_XML_FOLDER + if not folder_name.endswith('/'): + folder_name = folder_name + "/" + return folder_name + self.name + ".xml" + + def get_name(self): + return self.name + + def get_pkg_name(self): + if self.file_name is None: + return None + return self.file_name[0: -4] + + def install_suite(self, wrapper): + push_command = Constants.SDB_PUSH % (wrapper.get_sdb_device_id_param()) + " " + (Constants.RELEASED_SUITE_LOCATION % self.file_name) + " " + Constants.DEVICE_ZIP_PATH + "/" + print "Command: " , push_command + Constants.execute_sdb_command(push_command) + unzip_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + "unzip -o " + Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + " -d " + Constants.DEVICE_ZIP_PATH + print "Unzip command: ", unzip_command + Constants.execute_sdb_command(unzip_command) + install_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + print "Command: " , install_command + Constants.execute_sdb_command(install_command) + + def uninstall_suite(self, wrapper): + uninstall_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " " + Constants.DEVICE_ZIP_PATH + "/opt/%s/" % self.name + Constants.INSTALL_ZIP + " -u" + print "Command: " , uninstall_command + Constants.execute_sdb_command(uninstall_command) + remove_command = Constants.SDB_SHELL % (wrapper.get_sdb_device_id_param()) + " rm -rf "+ Constants.DEVICE_ZIP_PATH + "/%s" % self.file_name + print "Command: " , remove_command + Constants.execute_sdb_command(remove_command) + + def get_f(self): + f = "-f \"" + Constants.TEMP_UNPASSED_XML_FOLDER + self.name + ".xml" + return Constants.add_right_enbrace(f) + + def get_e(self): + #e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + " %s" % self.get_name() + e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + return Constants.add_right_enbrace(e) + +class TotalSummary: + def __init__(self, plan_name): + self.plan_name = plan_name + self.suite_summary_array = [] + self.environment_elm = None + self.summary_elm = None + self.capabilities = None + + def set_summary(self, summary): + self.summary_elm = summary + + def set_environment(self, environment): + self.environment_elm = environment + + def add_suite_elm(self, suite_elm): + self.suite_summary_array.append(SuiteSummary(suite_elm)) + + def set_capabilities(self, capabilities): + self.capabilities = capabilities + + def to_xml(self): + result_summary = ElementTree.Element('result_summary') + result_summary.set('plan_name', self.plan_name) + if self.environment_elm is not None: + #ElementTree.dump(self.environment_elm) + result_summary.append(self.environment_elm) + if self.summary_elm is not None: + #ElementTree.dump(self.summary_elm) + result_summary.append(self.summary_elm) + if self.capabilities is not None: + #ElementTree.dump(self.capabilities) + result_summary.append(self.capabilities) + for suite in self.suite_summary_array: + result_summary.append(suite.to_xml()) + return result_summary + +class SuiteSummary: + def __init__(self, xml_suite): + self.name = xml_suite.get('name') + self.tc_total = len(xml_suite.findall('set/testcase')) + self.tc_pass = len(xml_suite.findall('set/testcase[@result="PASS"]')) + self.tc_fail = len(xml_suite.findall('set/testcase[@result="FAIL"]')) + self.tc_block = len(xml_suite.findall('set/testcase[@result="BLOCK"]')) + self.tc_na = len(xml_suite.findall('set/testcase[@result="N/A"]')) + if self.tc_total > 0: + self.pass_rate = '%.2f'%(string.atof(self.tc_pass)*100/self.tc_total) + self.fail_rate = '%.2f'%(string.atof(self.tc_fail)*100/self.tc_total) + self.block_rate = '%.2f'%(string.atof(self.tc_block)*100/self.tc_total) + self.na_rate = '%.2f'%(string.atof(self.tc_na)*100/self.tc_total) + else: + self.pass_rate = 0 + self.fail_rate = 0 + self.block_rate = 0 + self.na_rate = 0 + + def to_str(self): + print " Suite: %s" % self.name + print " |---total case number: %d " % self.tc_total + print " | |---Pass rate: %d" % self.pass_rate + print " | |---PASS case number: %d " % self.tc_pass + print " | |---FAIL rate: %d" % self.fail_rate + print " | |---FAIL case number: %d " % self.tc_fail + print " | |---BLOCK rate: %d" % self.block_rate + print " | |---BLOCK case number: %d " % self.tc_block + print " | |---N/A rate: %d" % self.na_rate + print " | |---N/A case number: %d " % self.tc_na + + def to_xml(self): + suite_elm = ElementTree.Element('suite') + suite_elm.set('name', self.name) + + total_case = ElementTree.SubElement(suite_elm, 'total_case') + total_case.text = str(self.tc_total) + + pass_case = ElementTree.SubElement(suite_elm, 'pass_case') + pass_case.text = str(self.tc_pass) + pass_rate = ElementTree.SubElement(suite_elm, 'pass_rate') + pass_rate.text = str(self.pass_rate) + + fail_case = ElementTree.SubElement(suite_elm, 'fail_case') + fail_case.text = str(self.tc_fail) + fail_rate = ElementTree.SubElement(suite_elm, 'fail_rate') + fail_rate.text = str(self.fail_rate) + + block_case = ElementTree.SubElement(suite_elm, 'block_case') + block_case.text = str(self.tc_block) + block_rate = ElementTree.SubElement(suite_elm, 'block_rate') + block_rate.text = str(self.block_rate) + + na_case = ElementTree.SubElement(suite_elm, 'na_case') + na_case.text = str(self.tc_na) + na_rate = ElementTree.SubElement(suite_elm, 'na_rate') + na_rate.text = str(self.na_rate) + + return suite_elm + +class WrapperRunner: + + def __init__(self): + self.suites = {} + self.running_mode = None + self.latest_result_folder = Constants.TEMP_RESULT_XML_FOLDER + "%s/" % datetime.datetime.now().strftime('%Y-%m-%d_%H:%M:%S') + self.summary = None + + def create_result_folder(self): + d = os.path.dirname(Constants.TEMP_RESULT_XML_FOLDER) + if not os.path.exists(d): + os.makedirs(d) + d = os.path.dirname(self.latest_result_folder) + os.makedirs(d) + + + def check_capability_suite(self): + is_include = False + for suite_name in self.suites: + if Constants.CAPABILITY_SUITE_NAME in suite_name: + is_include = True + break + return is_include + + def exit_without_capability(self, wrapper): + print "Checking capability" + if not wrapper.is_cap_param_available(): + if self.check_capability_suite(): + print "The capability related suite is in the plan, but no capability XML is available. Please refer to the file %scapability.xml to edit and input capability XML file before running test" % Constants.TCT_PLAN_FOLDER + sys.exit(1) + + def create_latest_folder(self): + os.chdir(Constants.TEMP_RESULT_XML_FOLDER) + os.system("rm latest") + link = "ln -s '%s' '%s'" % (self.latest_result_folder, "latest") + os.system(link) + + def copy_latest_dlog(self): + command = "cp %s/latest/*.dlog " % Constants.LITE_HOME + command += "'%s'" % self.latest_result_folder + print command + os.system(command) + + def clean_latest_result_folder(self): + import shutil + print "clean latest folder..." + os.chdir(Constants.TEMP_RESULT_XML_FOLDER) + shutil.rmtree(self.latest_result_folder) + + def call_testconfig_script(self,pkg): + print "call_testconfig_script" + command = "sdb shell 'tct-testconfig.sh -f %s -p %s; echo rtncode=$?' " % (Constants.DEVICE_CONFIGURE_FILE_PATH , pkg) + print "call testconfig script: " , command + code, result = Constants.execute_sdb_command(command) + print "code :" ,code + if result: + lines = result.readlines() + if lines: + print "result: " ,lines[-1] + + def print_dlogctrl_property(self,deviceid): + exitcode, result = Constants.execute_sdb_command(Constants.SDB_GET_DLOG_PRO % deviceid) + pro = result.readlines()[0].strip() + print "get dlogctrl property: ", pro + if pro != "1": + print "please use the command of 'sdb shell dlogctrl set platformlog 1' to enable dlog, then reboot the devices" + + def execute_suite_in_loop(self, wrapper,guider): + import signal + self.create_result_folder() + if wrapper.is_fail_rerun_mode(): + self.rebuild_fail_xml() + if wrapper.is_single_mode(): + self.print_dlogctrl_property(wrapper.get_sdb_device_id_param()) + for suite_name in self.suites: + suite = self.suites[suite_name] + if wrapper.is_auto_install(): + print "installing suite: %s" % suite_name + suite.install_suite(wrapper) + #if guider.is_update_config(suite_name): + # self.call_testconfig_script(suite_name) + f = suite.get_f() + e = suite.get_e() + command = "testkit-lite %s " % (f + " " + e) + command += "%s " % wrapper.get_deviceid_param() + command += "%s " % wrapper.get_capability_param() + command += "%s " % wrapper.get_auto_case_param() + command += "%s " % wrapper.get_manual_case_param() + command += "%s " % wrapper.get_testcase_id() + command += " -o \"%s.xml\"" % (self.latest_result_folder + suite_name) + if wrapper.is_fail_rerun_mode(): + command += " --rerun" + print command + value = os.system(command) + print "execute suite in loop return value: " , value + if value == signal.SIGINT: + print "the value is 2, so exit tct-shell tool" + self.clean_latest_result_folder() + sys.exit(1) + + self.create_latest_folder() + self.copy_latest_dlog() + + if wrapper.is_auto_install(): + print "uninstalling suite: %s" % suite_name + suite.uninstall_suite(wrapper) + + def select_start_at(self, start_at, this_start_at): + if start_at is None: + return this_start_at + elif cmp(start_at, this_start_at) < 0: + return start_at + else: + return this_start_at + + def select_end_at(self, end_at, this_end_at): + if end_at is None: + return this_end_at + elif cmp(end_at, this_end_at) > 0: + return end_at + else: + return this_end_at + + def copy_result_xml(self, result_xml): + print "Copying the merged result XML '%s'" % result_xml + "to %s" % self.latest_result_folder + os.system("cp " + result_xml + " '" + self.latest_result_folder + "'") + + def copy_preconfigure_file(self): + print "Copying the preconfigure file to result" + os.system("cp " + Constants.PRE_CONFIGURE_FILE_PATH + " '" + self.latest_result_folder + "'") + + def _merge_build_info(self, env_elm): + root = ElementTree.parse(Constants.BUILD_INFO_PATH).getroot() + for element in root.findall("buildinfo"): + if element is not None: + name = ElementTree.Element.get(element, "name", None) + child = ElementTree.Element.getchildren(element) + value = "" + if child and child[0].text: + value = child[0].text + print name, value + env_elm.set(name, value) + + def merge_suite_result(self, result_xml, plan_name): + os.chdir(self.latest_result_folder) + root = ElementTree.Element('test_definition') + first = True + start_at = None + end_at = None + summary_xml = TotalSummary(plan_name) + + for suite_name in self.suites: + result_name = "%s.xml" % suite_name + if os.path.isfile(result_name): + print "Reading the result of suite : %s" % suite_name + if True: + xml_tree = ElementTree.parse(result_name) + xml_root = xml_tree.getroot() + if first: + first = False + env_elm = xml_root.find('environment') + self._merge_build_info(env_elm) + root.append(env_elm) + summary_xml.set_environment(env_elm) + root.append(xml_root.find('summary')) + summary = xml_root.find('summary') + this_start = summary.find('start_at') + this_end = summary.find('end_at') + + start_at = self.select_start_at(start_at, this_start.text) + end_at = self.select_end_at(end_at, this_end.text) + + for this_suite in xml_root.findall('suite'): + root.append(this_suite) + summary_xml.add_suite_elm(this_suite) + else: + print "[Error] the expecting result of the suite %s is not found! " % suite_name + s_tag = root.find('summary/start_at') + if s_tag is not None: + s_tag.text = start_at + e_tag = root.find('summary/end_at') + if e_tag is not None: + e_tag.text = end_at + + sum_elm = root.find('summary') + summary_xml.set_summary(sum_elm) + + capability_root = self.parse_capablities() + summary_xml.set_capabilities(capability_root) + + tree = ElementTree.ElementTree() + tree._setroot(root) + + d = os.path.abspath(os.path.dirname(result_xml)) + + if not os.path.exists(d): + os.makedirs(d) + + tree.write(result_xml, encoding="utf-8") + print "Generating the merged result XML '%s'" % result_xml + + sum_tree = ElementTree.ElementTree() + sum_root = summary_xml.to_xml() + Constants.indent(sum_root) + sum_tree._setroot(sum_root) + print "Generating the summary XML in %s" % (self.latest_result_folder + "summary.xml") + outFile = open(self.latest_result_folder + "summary.xml", 'w') + outFile.write("\n") + outFile.write("\n") + + sum_tree.write(outFile, encoding="utf-8") + outFile.close() + + def pull_device_capabilities(self, wrapper): + rm_command = 'rm -rf ' + Constants.CAPABILITY_PATH + print "Command: " + rm_command + os.system(rm_command) + + pull_command = Constants.SDB_PULL % wrapper.get_sdb_device_id_param() + " " + Constants.DEVICE_CAPABILITY_PATH + " " + Constants.CAPABILITY_PATH + print "Command: " + pull_command + os.system(pull_command) + + def pull_build_info(self, wrapper): + rm_command = 'rm -rf ' + Constants.BUILD_INFO_PATH + print "Command: " + rm_command + os.system(rm_command) + + pull_command = Constants.SDB_PULL % wrapper.get_sdb_device_id_param() + " " + Constants.DEVICE_BUILD_INFO_PATH + " " + Constants.BUILD_INFO_PATH + print "Command: " + pull_command + os.system(pull_command) + + def pull_preconfigure_json(self,wrapper): + pull_command = Constants.SDB_PULL % wrapper.get_sdb_device_id_param() + " " + Constants.DEVICE_DEFAULT_JSON_CONF + " " + Constants.PRE_CONFIGURE_ISON_FILE_PATH + print "Command: " + pull_command + os.system(pull_command) + + def push_preconfigure(self,device_id): + push_command = Constants.SDB_PUSH % device_id + " " + Constants.PRE_CONFIGURE_ISON_FILE_PATH + " " + Constants.DEVICE_DEFAULT_JSON_CONF + print "push_preconfigure Command: " + push_command + os.system(push_command) + + def pull_default_conf(self,device_id): + pull_command = Constants.SDB_PULL % device_id + " " + Constants.DEVICE_DEFAULT_CONF + " " + Constants.PRE_CONFIGURE_FILE_PATH + print "Command: " + pull_command + os.system(pull_command) + + def parse_capablities(self): + root = ElementTree.Element('capabilities') + try: + capa_root = None + capa_tree = ElementTree.parse(Constants.CAPABILITY_PATH) + capa_root = capa_tree.getroot() + + for capa in capa_root.findall('capability'): + root.append(capa) + except Exception, e: + print "[ Error: reading capability XML fail, error: %s ]\n" % e + root = None + return root + + def open_report(self,wrapper): + Constants.copy_style_in_result_folder(self.latest_result_folder,wrapper) + + def prepare_testplan(self, testplan_xml, running_mode, f_suites, result_xml): + print "preparing test plan" + self.running_mode = running_mode + if running_mode == Constants.RUNNING_MODE_PLAN: + return self.load_testplan_xml(testplan_xml) + elif running_mode == Constants.RUNNING_MODE_RESULT: + print "reading result file..." + return self.read_result_xml(result_xml) + elif running_mode == Constants.RUNNING_MODE_SUITES: + return self.prepare_pkg(f_suites) + elif running_mode == None: + print "\ntct-shell: Command requires a running mode" + os.system("tct-shell -h") + return False + + def load_testplan_xml(self, testplan_xml): + try: + xml_tree = ElementTree.parse(testplan_xml) + xml_root = xml_tree.getroot() + # all suites + for xml_suite in xml_root.findall('suite'): + suite = PlanSuite(xml_suite.get("name"), xml_suite.find("pkg_name").text) + if suite.get_pkg_name is None: + return False + self.suites[suite.get_name()] = suite + suite.to_str() + except Exception, e: + print "[ Error: reading testplan XML fail, error: %s ]\n" % e + sys.exit(1) + return True + + def prepare_pkg(self, f_suites): + if f_suites: + for suite_name in f_suites: + suite = PackageSuite(suite_name) + if suite.get_pkg_name() is None: + return False + self.suites[suite.get_name()] = suite + suite.to_str() + return True + + def generate_params(self): + f = Constants.XMLFILE_PREFF + "\"" + tmp = "" + for suite in self.suites.keys(): + suite_obj = self.suites[suite] + p_name = suite[0: suite.find("tests")] + "tests" + f += Constants.DEVICE_TESTS_FILE % p_name + if suite_obj.get_command().startswith(Constants.WRT_LAUNCHR_CMD): + tmp += "%s " % suite_obj.get_command()[(len(Constants.WRT_LAUNCHR_CMD) + 1):] + f = Constants.add_right_enbrace(f) + e = "" + if not tmp.isspace(): + e = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD +" " + tmp + e = Constants.add_right_enbrace(e) + return f + " " + e + + def generate_rerun_params(self): + f = "-f \"" + param = Constants.EXECUTE_PREFF + "\"" + Constants.WRT_LAUNCHR_CMD + for suite in self.suites.keys(): + suite_obj = self.suites[suite] + f += "%s " % suite_obj.get_rerunxml_name() + param += " %s" % suite + f = Constants.add_right_enbrace(f) + param = Constants.add_right_enbrace(param) + return f + param + Constants.RERUN_OUTPUT + + def read_result_xml(self, result_xml): + try: + xml_tree = ElementTree.parse(result_xml) + xml_root = xml_tree.getroot() + # all suites + for xml_suite in xml_root.findall('suite'): + launcher = xml_suite.get("launcher") + print "Launcher: %s" % launcher + if launcher is not None: + launcher = launcher.strip() + if launcher.endswith("-r") or launcher.endswith("-a"): + print "The suite: %s is a UIFW suite, skipped" % xml_suite.get("name") + continue + suite = RerunFailSuite(xml_suite) + if suite.get_pkg_name() is None: + return False + if suite.is_empty(): + print "No unpassed test cases is found in the suite %s; skip it!" % suite.get_name() + else: + self.suites[suite.get_name()] = suite + except Exception, e: + print "[ Error: reading original Result XML fail, error: %s ]\n" % e + sys.exit(1) + return True + + def rebuild_fail_xml(self): + folder_name = Constants.TEMP_UNPASSED_XML_FOLDER + if not folder_name.endswith('/'): + folder_name = folder_name + "/" + d = os.path.dirname(Constants.TEMP_UNPASSED_XML_FOLDER) + if not os.path.exists(d): + print "Creating" + os.makedirs(d) + os.chdir(Constants.TEMP_UNPASSED_XML_FOLDER) + + for suite_name in self.suites.keys(): + suite = self.suites[suite_name] + suite.to_xml() + + def merge_result_xml(self, origin_xml, result_xml): + try: + origin_tree = ElementTree.parse(origin_xml) + origin_root = origin_tree.getroot() + + new_tree = ElementTree.parse(result_xml) + new_root = new_tree.getroot() + + for new_tc in new_root.findall('suite/set/testcase'): + condition = './testcase[@purpose="%s"]' % new_tc.get("purpose") + for origin_set in origin_root.findall('suite/set'): + target = origin_set.find(condition) + if target is not None: + origin_set.remove(target) + origin_set.append(new_tc) + break + origin_tree.write(origin_xml, encoding="utf-8") + print "The new results are merged with the original one %s ." % origin_xml + except Exception, e: + print "[ Error: reading suite Result XML fail, error: %s ]\n" % e + +class Guider: + _conf_prompts = [] + + def __init__(self,grunner,id): +# self.config_file_path = os.path.join(os.curdir, "config_file") + self.guidrunner = grunner + self.suits = grunner.suites + self.deviceid = id + self.manual_items = {} + self.conf_items = {} + self.conf_file = Constants.PRE_CONFIGURE_FILE_PATH + + def init_items(self): + self._fill_items() + self._read_conf() + self.read(self.conf_file) + + def read(self,filename): + read_ok = [] + try: + fp = open(filename) + except IOError: + print "open file fail..." + self._read(fp, filename) + fp.close() + + def _read(self,fp, fpname): + lineno = 0 + comment = None; + prompts = [] + while True: + line = fp.readline() + if not line: + break + lineno = lineno + 1 + # comment or blank line? + if line.strip() == '': + continue + if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": + # no leading whitespace + continue + if line[0] in '#;': + comment = line; + continue + if line[0] in '[': + comment = None; + continue + if line.find('='): + linelist = line.split('=') + prompts.append((linelist[0], comment)) + self._conf_prompts = prompts + + + def set_conf_path(self,path): + if path: + file = path + os.path.sep + "pre_configure.conf" + print "set config path:" , file + if os.path.exists(file): + copy(file, self.conf_file) + self._read_conf() + + def _read_conf(self): + parser = ConfigParser.ConfigParser() + if not os.path.exists(self.conf_file): + print("Can't find the tct config file...") + self.guidrunner.pull_default_conf(self.deviceid) + parser.read(self.conf_file) + for section in parser.sections(): + if not section: + continue + opts = {} + for n, v in parser.items(section): + if n: + opts[n] = v + self.conf_items[section] = opts + + def _fill_items(self): + command = Constants.SDB_SHELL % self.deviceid + Constants.SDB_CAT_CONFIG % Constants.TCT_TESTCONFIG_PATH + print "fill items command : " , command + code, result = Constants.execute_sdb_command(command) + if code is None or code != 0: + print("Obtain tct_testconfig content error...") + self._exit(0) + xml_tree = ElementTree.fromstring(result.read()) + for set in ElementTree.Element.findall(xml_tree, "suite/set"): + module_name = ElementTree.Element.get(set, "name", None) + if module_name: + descs = [] + for desc in ElementTree.Element.findall(set, "testcase/description/steps/step/step_desc"): + if desc is not None: + descs.append(desc.text) + self.manual_items[module_name] = descs + + + def is_update_config(self, pkg_name): + if self.conf_items.has_key(pkg_name): + return True + return False + + def get_confs(self, pkg_name): + return self.conf_items.get(pkg_name) + + def setup_env(self): + self._show_manual_setup() + self._show_overall_infos() + self.update_json() + self.guidrunner.push_preconfigure(self.deviceid) + + def _filter_suits(self, items): + keys = set() + if self.suits and items: + suit_keys = set(self.suits.keys()) + item_keys = set(items.keys()) + keys = keys|(suit_keys & item_keys) + return list(keys) + + def _show_manual_setup(self): + keys = self._filter_suits(self.manual_items) + if not keys: + print("No any pre-configure to be configured.") + return + + print("\nHi,guys, before the start, there are some pre-configures must be provided," + "and you just should input (Y)es or simply press Enter if your test environment " + "has met the request, otherwise, please input (N)o and the program will exit.") + + for module in keys: + descs = self.manual_items.get(module) + if descs: + print("") + print("Module: %s" % module) + print("") + i = 1 + for desc in descs: + l = list() + print('(%r) %s' % (descs.index(desc) + 1, desc)) + i += i + result = True + while result: + answer = raw_input("\nDo you have set the device as descriped above? [Yes/No]: ") + if answer and (answer.lower() == "no" or answer.lower() == "n"): + print "\nYour environment can't meet the test's request. Exit..." + self._exit(0) + if answer and (answer.lower() == "yes" or answer.lower() == "y"): + result = False + print("") + + def _show_overall_infos(self): + keys = self._filter_suits(self.conf_items) + if not keys: + print("No conf params are needed here.") + return + + print("Overall needed configure is as below, please confirm:\n") + index = 1 + for k,p in self._conf_prompts: + for pkg in keys: + v = self._get_value(pkg, k) + if v is not None: + print ('{i}. {p}'.format(i = index, p = p)) + print('{0: <30}{v}'.format(k + ":", v = v)) + index += 1 + print ("\nIf needed, please update %s. " % Constants.PRE_CONFIGURE_FILE_PATH) + result = True + while result: + next_step = raw_input("Continue to run test? [Yes/No]: ") + if next_step and (next_step.lower() == "n" or next_step.lower() == "no"): + self._exit(0) + if next_step and (next_step.lower() == "yes" or next_step.lower() == "y"): + result = False + print("OK! All is well. Let's go ahead...") + + def _exit(self, code=None): + print ("Bye-bye ^_^") + if code is None: + code = 0 + sys.exit(code) + + def _get_value(self, section, opt): + if self.conf_items.has_key(section): + vs = self.conf_items.get(section) + if vs and vs.has_key(opt.lower()): + return vs.get(opt.lower()) + return None + + def update_json(self): + import json; + self._read_conf(); + list = [] + dic = {} + for value in self.conf_items.values(): + for k,v in value.items(): + dic[string.upper(k)] = v + list.append(dic) + fp = open(Constants.PRE_CONFIGURE_ISON_FILE_PATH,"w") + json.dump(list, fp,indent = 4) + + diff --git a/tctshell/shellwrapper.py b/tctshell/shellwrapper.py new file mode 100644 index 0000000..f361381 --- /dev/null +++ b/tctshell/shellwrapper.py @@ -0,0 +1,433 @@ +#!/usr/bin/python +# +# Copyright (C) 2012 Intel Corporation +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Authors: +# Tang, Shaofeng + + +import os +import re +import sys, traceback +import time +import subprocess +import platform +import ctypes +import glob +import ConfigParser +import xml.etree.ElementTree as etree +from optparse import * +from shutil import copyfile +from tempfile import mktemp +from datetime import datetime +from constants import Constants +import os.path + +#show all available suites +def show_available_suites(option, opt_str, value, parser): + if platform.system() != "Linux": + Constants.TEST_SUITE_DIR = "../suites" + print "Test Suites:...\n" + os.chdir(Constants.TEST_SUITE_DIR) + rpms = sorted(glob.glob("*.zip")) + for files in rpms: + #without ".rpm" + print files[:-4] + sys.exit(1) + +# version option +def print_version(option, opt_str, value, parser): + try: + CONFIG = ConfigParser.ConfigParser() + if platform.system() == "Linux": + CONFIG.read('/opt/tct/shell/VERSION') + else: + VERSION_FILE = os.path.join(sys.path[0], 'VERSION') + CONFIG.read(VERSION_FILE) + VERSION = CONFIG.get('public_version', 'version') + print "V%s" % VERSION + except Exception, e: + print "[ Error: fail to parse version info, error: %s ]\n" % e + sys.exit(1) + +def print_planfolder(option, opt_str, value, parser): + print "Plan folder: %s" % Constants.TCT_PLAN_FOLDER + os.system("tree %s" % Constants.TCT_PLAN_FOLDER) + sys.exit(1) + +def print_resultfolder(option, opt_str, value, parser): + print "Result folder: %s" % Constants.TEMP_RESULT_XML_FOLDER + os.system("tree %s" % Constants.TEMP_RESULT_XML_FOLDER) + sys.exit(1) + +def invoke_sdb_devices(option, opt_str, value, parser): + os.system(Constants.SDB_DEVICES) + sys.exit(1) + +def varnarg(option, opt_str, value, parser): + """ parser srg""" + value = [] + import re + for arg in parser.rargs: + if re.search('^--.+', arg) or re.search('^-[\D]', arg): + break + value.append(arg) + + del parser.rargs[:len(value)] + setattr(parser.values, option.dest, value) + +class TestKitShellWrapper: + + def __init__(self): + print "" + self._j = os.path.join + self._e = os.path.exists + self._d = os.path.dirname + self._b = os.path.basename + self._abspath = os.path.abspath + self.testkit_dir = "/opt/testkit/lite" + self.LOG_DIR = self.testkit_dir + self.PID_FILE = self._j(self.LOG_DIR , "pid.log") + if not platform.system() == "Linux": + self.testkit_dir = self._d(self._abspath(__file__)) + sys.path += [self._j(self.testkit_dir)] + self.testkit_dir = self._j(self.testkit_dir , "results") + #self.clean_context() + self.options = None + self.options = None + self.running_mode = None # plan suite result + self.USAGE = "tct-shell [options] --testplan \n\ +examples: \n\ + tct-shell --test package1 package2 ... packageN\n\ + tct-shell --rerun-fail '/test-result.xml' \n\ + tct-shell --testplan /testplan.xml\n\ +\n\ + run a test plan: \n\ + tct-shell --testplan /testplan.xml --output /tmp/wekit-tests-result.xml ...\n\ + run some test packages: \n\ + tct-shell --test package1 package2 ... packageN --output /tmp/wekit-tests-result.xml ...\n\ + rerun all unpassed test: \n\ + tct-shell --rerun-fail '/test-result.xml' ...\n\ + show all existed testplan which is in the folder (configured in /opt/tct/shell/CONFIG): \n\ + tct-shell --plan-list\n\ + show all history result which is in the folder (configured in /opt/tct/shell/CONFIG): \n\ + tct-shell --result-list\n\ + show all connected devices: \n\ + tct-shell --device-list\n\ +\n\ +Note: \n\ + 1) Proxy settings should be disabled when execute webapi packages\n\ + 2) run command 'tct-shell', it might not be able to locate related module, run command 'export PYTHONPATH=/usr/lib/python2.7/site-packages' to resolve this issue" + + + def print_usage(self): + print self.USAGE + + def parse_options(self, argv): + option_list = [ + make_option("--testplan", "-p", dest="testplan_file", + action="callback", callback=varnarg, + help="Specify the testplan.xml."), + make_option("--output", "-o", dest="resultfile", + help="Specify output file for result xml. If more than one testxml provided, results will be merged together to this output file"), + make_option("--version", dest="version_info", action="callback", callback=print_version, + help="Show version information"), + make_option("--skip-iu", dest="skip_install", action="store_true", help="Do not install and uninstall suite packages during a test.Install and uninstall them manually."), + make_option("--all-suites", dest="show_suites", action="callback", callback=show_available_suites, + help="Show all available test-suites in the local repository, the local repository is defined in the configure '/opt/tct/shell/CONFIG'"), + make_option("--test", "-t", dest="suites", action="callback", callback=varnarg, + help="Specify testing suites. If more than one suites are provided, just list them all and separate with whitespace"), + make_option("--rerun-fail", dest="fail_result_xml", action="callback", callback=varnarg, + help="Rerun all fail testcase according to the specified result XML."), + make_option("--deviceid", "-d", dest="deviceid", action="callback", callback=varnarg, + help="set sdb device serial information."), + make_option("--plan-list", dest="show_plan_folder", action="callback", callback=print_planfolder, + help="List all existed plan in the Plan folder. The plan folder is defined in the configure '/opt/tct/shell/CONFIG'"), + make_option("--result-list", dest="show_all_result", action="callback", callback=print_resultfolder, + help="List all history results in the result folder. The result folder is defined in the configure '/opt/tct/shell/CONFIG'"), + make_option("--device-list", dest="show_all_device", action="callback", callback=invoke_sdb_devices, + help="List all connected devices. just same with 'sdb devices'"), + make_option("--all", "-A", dest="all_tc", action="store_true", help="Both manual and auto test cases will be executed, without this option, only auto testcase will be executed."), + make_option("--manual", "-M", dest="only_manual", action="store_true", help="Only manual test cases will be executed"), + make_option("--id", dest="testcase_id", action="callback", callback=varnarg, + help="Specify to run a test case by id."), + make_option("--disable", dest="disable_preconfigure",action="store_false",default=True,help="disable the function of pre_configure"), + ] + # detect non-params + if len(argv) == 1: + argv.append("--help") + PARSERS = OptionParser(option_list=option_list, usage=self.USAGE) + (self.options, args) = PARSERS.parse_args() + if self.is_testplan_mode(): + + conflicts = ["--testplan"] + if self.options.fail_result_xml is not None: + conflicts.append("--rerun-fail") + if self.options.suites is not None: + conflicts.append("--test") + self.conflict_exit(conflicts) + + self.running_mode = Constants.RUNNING_MODE_PLAN + elif self.options.fail_result_xml is not None: + + conflicts = ["--rerun-fail"] + if self.options.suites is not None: + conflicts.append("--test") + self.conflict_exit(conflicts) + + self.running_mode = Constants.RUNNING_MODE_RESULT + elif self.options.suites is not None: + self.running_mode = Constants.RUNNING_MODE_SUITES + + if self.options.all_tc and self.options.only_manual: + conflicts = ["--all", "--manual"] + self.conflict_exit(conflicts) + + self.check_args_number() + + def check_args_number(self): + opt = "" + if self.running_mode == Constants.RUNNING_MODE_PLAN and len(self.options.testplan_file) < 1: + opt = "--testplan" + elif self.running_mode == Constants.RUNNING_MODE_RESULT and len(self.options.fail_result_xml) < 1: + opt = "--rerun-fail" + elif self.running_mode == Constants.RUNNING_MODE_SUITES and len(self.options.suites) < 1: + opt = "--test" + elif self.options.deviceid is not None and len(self.options.deviceid) < 1: + opt = "--deviceid" + elif self.options.testcase_id is not None and len(self.options.testcase_id) < 1: + opt = "--id" + + if len(opt) > 0: + os.system("tct-shell -h") + print "\ntct-shell: error: \"%s\" option requires an argument" % opt + sys.exit(1) + + + + def conflict_exit(self, conflicts): + if conflicts == None or len(conflicts) <= 1: + return + + os.system("tct-shell -h") + print "\ntct-shell: Conflicted options: %s" % conflicts + sys.exit(1) + + def get_suite_param(self): + param = Constants.XMLFILE_PREFF + "\"" + + for suite_name in self.options.suites: + param += "%s " % suite_name + param = Constants.add_right_enbrace(param) + + param += Constants.EXECUTE_PREFF + ("\" %s " % Constants.WRT_LAUNCHR_CMD) + for e_name in self.options.suites: + param += "%s " % e_name + param = Constants.add_right_enbrace(param) + return param + + def is_testplan_mode(self): + return self.options.testplan_file is not None + + def is_single_mode(self): + return self.options.testcase_id is not None + + def is_enable_preconf_func(self): + return self.options.disable_preconfigure + + def is_suite_mode(self): + return self.options.suites is not None + + def is_fail_rerun_mode(self): + return self.options.fail_result_xml is not None + + def get_plan_name(self): + plan_name = "" + if self.is_testplan_mode(): + plan_name = "Plan: %s" % self.options.testplan_file[0] + elif self.is_suite_mode(): + plan_name = "Temporary plan to execute the suites: %s " % str(self.get_suites()) + elif self.is_fail_rerun_mode(): + plan_name = "Temporary plan to re-run all un-Passed testcases in the result_XML '%s' " % self.options.fail_result_xml[0] + return plan_name + + def is_auto_install(self): + return not self.options.skip_install + + def get_output_param(self): + return "-o %s" % self.get_output_file_name() + + def get_output_file_name(self): + output = "" + if self.options.resultfile is not None: + output = os.path.abspath(self.options.resultfile) + else: + output = Constants.TEMP_UNPASSED_XML_FOLDER + "result.xml" + return output + + def get_deviceid_param(self): + deviceid = "" + if self.options.deviceid is not None: + deviceid = " --deviceid %s" % self.options.deviceid[0] + return deviceid + + def get_deviceid(self): + deviceid = "" + if self.options.deviceid is not None: + deviceid = "deviceid=%s" % self.options.deviceid[0] + return deviceid + + def get_sdb_device_id_param(self): + deviceid = "" + if self.options.deviceid is not None: + deviceid = " -s %s" % self.options.deviceid[0] + return deviceid + + def get_capability_param(self): + capability_file = "" + if os.path.isfile(Constants.CAPABILITY_PATH): + capability_file = " --capability %s" % Constants.CAPABILITY_PATH + return capability_file + + def is_cap_param_available(self): + return os.path.isfile(Constants.CAPABILITY_PATH) + + def get_auto_case_param(self): + auto = Constants.ONLY_AUTO_CASES + if self.options.all_tc or self.options.only_manual: + auto = "" + return auto + + def get_manual_case_param(self): + manual = "" + if self.options.only_manual: + manual = Constants.ONLY_MANUAL_CASES + return manual + + def is_manual_or_all_param(self): + if self.options.only_manual: + return " --manual" + if self.options.all_tc: + return " --all" + else: + return "" + + def get_testcase_id(self): + tc_id = "" + if self.options.testcase_id is not None: + tc_id = " --id %s" % self.options.testcase_id[0] + " --debug" + return tc_id + + def print_result_summary(self, result_file): + try: + tree = etree.parse(result_file) + root = tree.getroot() + suites = root.findall('suite') + + total_cases = len(root.findall('suite/set/testcase')) + print "[Result: execute %d suites]" % len(suites) + print "[ total case number: %d ]" % total_cases + if total_cases > 0: + print "[ Pass Rate: %.2f" % (float(len(root.findall('suite/set/testcase[@result="PASS"]'))) * 100 /total_cases) + "% ]" + else: + print "[ Pass Rate: 0% ]" + print "[ PASS case number: %d ]" % len(root.findall('suite/set/testcase[@result="PASS"]')) + print "[ FAIL case number: %d ]" % len(root.findall('suite/set/testcase[@result="FAIL"]')) + print "[ BLOCK case number: %d ]" % len(root.findall('suite/set/testcase[@result="BLOCK"]')) + print "[ N/A case number: %d ]" % len(root.findall('suite/set/testcase[@result="N/A"]')) + for suite in suites: + print " Suite: %s" % suite.get("name") + suite_cases_number = len(suite.findall('set/testcase')) + print " |---total case number: %d " % suite_cases_number + if suite_cases_number > 0 : + print " | |---Pass rate: %.2f" % (float(len(suite.findall('set/testcase[@result="PASS"]'))) * 100 /len(suite.findall('set/testcase'))) + "%" + else: + print " | |---Pass rate: 0%" + print " | |---PASS case number: %d " % len(suite.findall('set/testcase[@result="PASS"]')) + print " | |---FAIL case number: %d " % len(suite.findall('set/testcase[@result="FAIL"]')) + print " | |---BLOCK case number: %d " % len(suite.findall('set/testcase[@result="BLOCK"]')) + print " | |---N/A case number: %d " % len(suite.findall('set/testcase[@result="N/A"]')) + except Exception, e: + print "[ Error: When calculating the summary of Result XML, error: %s ]\n" % e + + def get_suites(self): + suites = [] + if self.options.suites: + for suite_str in self.options.suites: + suite_str_array = suite_str.split(" ") + for suite_name in suite_str_array: + suites.append(suite_name) + return suites + + def check_sdb_devices(self): + sdb_ok = False + import subprocess + + proc = subprocess.Popen(["sdb start-server"], stdout=None, shell=True) + time.sleep(3) + proc = subprocess.Popen(["sdb devices"], stdout=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + print "sdb devices: %s" % out + i = 0 + for line in out: + if line == '\n': + i = i + 1 + if self.options.deviceid is not None and self.options.deviceid[0] is not None: + if self.options.deviceid[0] in out: + sdb_ok = True + print "The specified device %s is connected. Going to start testing" % self.options.deviceid[0] + else: + print "The specified device %s is not connected. exiting" % self.options.deviceid[0] + elif i > 1: + sdb_ok = True + print "No device is specified, use the only connected USB device." + else: + print "No device is connected. exiting" + + return sdb_ok + + def read_health_check_ini(self): + parser = ConfigParser.ConfigParser() + if not os.path.exists(Constants.HEALTH_CHECK_FILE_PATH): + print("Can't find the healthcheck.ini file...") + return + parser.read(Constants.HEALTH_CHECK_FILE_PATH) + for section in parser.sections(): + if not section: + continue + values = [] + for n, v in parser.items(section): + values.append(v.split(",")) + return values + + def check_sdb_devices_env(self): + """shell communication for quick return in sync mode""" + list = self.read_health_check_ini() + if not list: + return False + for l in list: + command = Constants.DEVICE_HEALTH_CHECK_CMD + " " + self.get_deviceid() + " proc=" + l[1].strip() + exit_code, result = Constants.execute_sdb_command(command) + if exit_code != 0: + for l in result: + print l + print "The device configuration is not right." + return False + else: + print l[0].strip() + ": pass" + print "Finished the env check. OK" + return True -- 2.7.4