From bbb0ffb2116840f507ab0a474ee22e3de8a42d1a Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 17 2020 16:43:30 +0000 Subject: beakerlib-1.17 base --- diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..875df46 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +beaker.spec export-subst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0bddd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +*.pyc +*.swp +*.tmp +dist +rpm-build +*.egg-info +*.bz2 +Server/build +Server/devdata.sqlite +Server/setup.py +Common/beaker/__init__.py +Harness +*.diff +publish-repo.sh +*.tar.gz +repo +make-release.sh +.project +src/test/.*.old +.pydevproject +.performance-history.dat +performance.svg +*.orig +*.patch +src/dictionary.vim +src/docs +.idea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..add7251 --- /dev/null +++ b/LICENSE @@ -0,0 +1,344 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, 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 Street, 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/MAINTENANCE b/MAINTENANCE new file mode 100644 index 0000000..c971a24 --- /dev/null +++ b/MAINTENANCE @@ -0,0 +1,23 @@ +--------------------- +Releasing new version +--------------------- + +Before release, it is usually worth the time to browse the Bugzilla +for patches and easy fixes. + +1) Apply (merge) all changes you want to be present in the new version + to the master branch. + +2) Increment the version in the VERSION file (do not forget to commit) + +3) run 'make upstream-release', which will + - check if the version in VERSION does not exist already + - create a tarball + - upload the tarball to fedorahosted.org + - tag the current commit with the version tag + - push tags upstream + +Note: we track bugs in Fedora / RH QA Process bugzilla for appropriate + package, so fixing bugs in upstream release does not constitute + an event for any bug status flipping. +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2e755ae --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +# License: GPL v2 +# Copyright Red Hat Inc. 2009 + +export PKGNAME := beakerlib +export PKGVERSION := $(shell cat VERSION ) +export TAG := ${PKGNAME}-${PKGVERSION} +ifndef DD + DD:=/ +endif + +ifndef PKGDOCDIR + export PKGDOCDIR := /usr/share/doc/$(PKGNAME)/ +endif + +export DESTDIR := $(shell readlink -f -n $(DD)) + +SUBDIRS := src + +build: + for i in $(SUBDIRS); do $(MAKE) -C $$i; done + +install: + mkdir -p $(DESTDIR)$(PKGDOCDIR) + install -m 644 -p LICENSE $(DESTDIR)$(PKGDOCDIR) + install -m 644 -p README $(DESTDIR)$(PKGDOCDIR) + install -m 644 -p MAINTENANCE $(DESTDIR)$(PKGDOCDIR) + install -m 644 -p VERSION $(DESTDIR)$(PKGDOCDIR) + + for i in $(SUBDIRS); do $(MAKE) -C $$i install; done + +clean: + rm -f *.tar.gz + for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done + +upstream-release: + sh upstream-release.sh ${TAG} + +testing-release: + sh upstream-release.sh ${TAG} testing + +experimental-release: + sh upstream-release.sh ${TAG} experimental + +check: + sh check-tempfiles.sh + make -C src check + make -C src test AREA=$(AREA) diff --git a/README b/README new file mode 100644 index 0000000..f140cc3 --- /dev/null +++ b/README @@ -0,0 +1,14 @@ +============ +INSTALLATION +============ + +To install the library to the /, use: + +$ make +$ make install + +If you need to install to a different directory, use +$ make +$ make DD=/path/to/directory install + +Running against non-installed tree (except for the in-tree testsuite) is not supported. diff --git a/README.md b/README.md new file mode 100644 index 0000000..33bc92f --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +## Installation + +To install the library to the root directory, use: + +``` +$ make +$ make install +``` + +If you need to install to a different directory, use +``` +$ make +$ make DD=/path/to/directory install +``` +Running against non-installed tree (except for the in-tree testsuite) is not supported. diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..b48f322 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.17 diff --git a/check-tempfiles.sh b/check-tempfiles.sh new file mode 100755 index 0000000..e7bf3b9 --- /dev/null +++ b/check-tempfiles.sh @@ -0,0 +1,24 @@ +#!/usr/bin/bash + +OUTPUT=$( mktemp ) # no-reboot + +find . -type f | grep -v -e runtest.sh -e check-tempfiles.sh -e '.git' -e '\.swp' -e 'src/test' -e '\.pyc' -e 'Build/' | \ + xargs grep -e mktemp -e mkstemp -e '/tmp/' | \ + grep -v -e "# no-reboot" -e "__INTERNAL_PERSISTENT_TMP" &> $OUTPUT + +RC=$? + +if [ $RC -eq 0 ] +then + echo "Several non-annotated temporary file usages found:" + echo "==================================================" + cat $OUTPUT + echo "==================================================" + echo "Please annotate intentional /tmp directory usage with # no-reboot" + echo "comment, or change the directory to \$__INTERNAL_PERSISTENT_TMP" + rm -f $OUTPUT + exit 1 +fi + + +rm -f $OUTPUT diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..a1bf07c --- /dev/null +++ b/src/Makefile @@ -0,0 +1,192 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: Makefile - part of the BeakerLib project +# Description: The Makefile +# +# Author: Petr Muller +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2013 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SUBDIRS := extradocs + +MODULES=journal.sh\ + logging.sh\ + testing.sh\ + rpms.sh\ + infrastructure.sh\ + performance.sh\ + analyze.sh\ + libraries.sh\ + storage.sh \ + synchronisation.sh\ + virtualX.sh + +FILES=$(MODULES) beakerlib.sh +DEFDOCS=dictionary.vim docsman + +.PHONY: install clean test + +build: $(FILES) $(DEFDOCS) + @for i in $(SUBDIRS); do $(MAKE) -C $$i $(MAKECMDGOALS); done + +install: + mkdir -p $(DESTDIR)/usr/share/beakerlib + mkdir -p $(DESTDIR)/usr/share/beakerlib/xslt-templates + mkdir -p $(DESTDIR)/usr/share/man/man1 + mkdir -p $(DESTDIR)/usr/bin + mkdir -p $(DESTDIR)/usr/share/vim/vimfiles/after/ftdetect + mkdir -p $(DESTDIR)/usr/share/vim/vimfiles/after/syntax + + install -p -m 644 $(FILES) $(DESTDIR)/usr/share/beakerlib + install -p -m 644 dictionary.vim $(DESTDIR)/usr/share/beakerlib + + install -p -m 644 xslt-templates/* $(DESTDIR)/usr/share/beakerlib/xslt-templates + + install -p -m 644 vim/ftdetect/beakerlib.vim $(DESTDIR)/usr/share/vim/vimfiles/after/ftdetect + install -p -m 644 vim/syntax/beakerlib.vim $(DESTDIR)/usr/share/vim/vimfiles/after/syntax + + install -p python/rlMemAvg.py $(DESTDIR)/usr/bin/beakerlib-rlMemAvg + install -p python/rlMemPeak.py $(DESTDIR)/usr/bin/beakerlib-rlMemPeak + install -p python/journalling.py $(DESTDIR)/usr/bin/beakerlib-journalling + install -p python/journal-compare.py $(DESTDIR)/usr/bin/beakerlib-journalcmp + install -p python/testwatcher.py $(DESTDIR)/usr/bin/beakerlib-testwatcher + install -p perl/deja-summarize $(DESTDIR)/usr/bin/beakerlib-deja-summarize + install -p lsb_release $(DESTDIR)/usr/bin/beakerlib-lsb_release + + install -p -m 644 $(MANDIR1)/* $(DESTDIR)/usr/share/man/man1 + + @for i in $(SUBDIRS); do $(MAKE) -C $$i $(MAKECMDGOALS); done + +clean: + rm -rf test/remotedir test/beakerlib-test-mount-point + rm -f *.tar.gz + rm -f test/runtests-worker.sh + rm -rf docs/{man,wiki,html,pod} + rm -f ./pod2htm* + rm -f dictionary.vim + @for i in $(SUBDIRS); do $(MAKE) -C $$i $(MAKECMDGOALS); done + +check: + BEAKERLIB=. sh beakerlib.sh + +test: + make -C test test + +benchmark: + cd test/; ./benchmark.sh + +# == DOCS GENERATION MACHINERY == # + +.PHONY: all-documentation docsman docshtml docswiki pod2wiki + +all-documentation: docsman docshtml dictionary.vim docswiki + +DOCDIR=docs +PODDIR=$(DOCDIR)/pod +MANDIR1=$(DOCDIR)/man/man1 +WIKIDIR=$(DOCDIR)/wiki +HTMLDIR=$(DOCDIR)/html + +DOCSJOIN=perl/docsjoin +PODFILE=docs/pod/beakerlib.pod + +MANPAGE=$(MANDIR1)/beakerlib.1 +HTMLPAGE=$(HTMLDIR)/beakerlib.html +WIKIPAGE=beakerLibManual +WIKIREV=00000001 + +WIKIPREFIX="Docs(2f)BeakerLib(2f)Manual(2f)" +WIKILOCATION="wikiadmin@docs.example.com:/var/www/moin/wiki/data/pages" + +$(PODDIR): + mkdir -p $(PODDIR) + +$(MANDIR1): + mkdir -p $(MANDIR1) + +$(HTMLDIR): + mkdir -p $(HTMLDIR) + +$(PODFILE): $(FILES) $(PODDIR) + $(DOCSJOIN) $(FILES) >$@ + +$(MANPAGE): $(PODFILE) $(MANDIR1) + pod2man $(PODFILE) >$@ + +$(MANDIR1)/beakerlib-%.1: %.sh $(MANDIR1) + pod2man $*.sh >$@ + +%.1.gz: %.1 + gzip --force $< + +$(HTMLPAGE): $(PODFILE) $(HTMLDIR) + pod2html $(PODFILE) >$@ + +$(HTMLDIR)/beakerlib-%.html: %.sh $(HTMLDIR) + pod2html $*.sh >$@ + +$(WIKIDIR)/$(WIKIPREFIX)/%: %.sh + mkdir -p $@ + +$(WIKIDIR)/$(WIKIPREFIX)/$(WIKIPAGE): + mkdir -p $@ + +$(WIKIDIR)/$(WIKIPREFIX)/%/current: $(WIKIDIR)/$(WIKIPREFIX)/% + echo $(WIKIREV) > $@ + +$(WIKIDIR)/$(WIKIPREFIX)/%/revisions/$(WIKIREV): $(WIKIDIR)/$(WIKIPREFIX)/% pod2wiki + mkdir -p $(WIKIDIR)/$/$(WIKIPREFIX)/$*/revisions + echo '<>' >$@; \ + pod2wiki --style moinmoin $*.sh >> $@ 2>/dev/null || pod2wiki --style moinmoin $(PODFILE) >> $@ + +MANPAGES = $(addprefix $(MANDIR1)/beakerlib-, $(MODULES:.sh=.1.gz)) $(MANPAGE).gz +HTMLPAGES = $(addprefix $(HTMLDIR)/beakerlib-, $(MODULES:.sh=.html)) $(HTMLPAGE) +WIKIREVFILES = $(addsuffix /revisions/$(WIKIREV), $(MODULES:.sh=) $(WIKIPAGE)) +WIKICURFILES = $(addsuffix /current, $(MODULES:.sh=) $(WIKIPAGE)) +WIKISUBDIRS = $(WIKIREVFILES) $(WIKICURFILES) +WIKIFILES = $(addprefix $(WIKIDIR)/$(WIKIPREFIX)/, $(WIKISUBDIRS)) + +docsman: $(MANPAGES) +docshtml: $(HTMLPAGES) +docswiki: $(WIKIFILES) + for wikidir in $(shell ls $(WIKIDIR)/$(WIKIPREFIX)); do \ + newdir=`basename $$wikidir | sed -e 's|\(.*\)|\u\1|'`; \ + mv $(WIKIDIR)/$(WIKIPREFIX)/$$wikidir $(WIKIDIR)/$(WIKIPREFIX)/$$newdir; \ + done + +dictionary.vim: $(FILES) + BEAKERLIB=. bash -c ". beakerlib.sh ; declare -f | \ + perl -e 'map { s/.*(obsolete|deprecate|^rlj).*//s; s/ .*/\n/s; print } \ + (join \"\", <>) =~ m/^rl.*?^}/msg;' > $@" + +pod2wiki: + if ! type pod2wiki &>/dev/null; then \ + echo "You need pod2wiki utility - see package 'perl-Pod-Simple-Wiki' or http://search.cpan.org/dist/Pod-Simple-Wiki/"; \ + false; \ + fi + if pod2wiki 2>&1 | grep -q "Can't locate Pod/Simple/Wiki"; then \ + echo "You need Pod::Simple::Wiki module for converting to wiki"; \ + echo "Try: yum install perl-CPAN && perl -MCPAN -e 'install \"Pod::Simple::Wiki\"'"; \ + false; \ + fi + +wiki: $(FILES) docswiki + scp -r $(WIKIDIR)/* $(WIKILOCATION) diff --git a/src/analyze.sh b/src/analyze.sh new file mode 100644 index 0000000..87f5102 --- /dev/null +++ b/src/analyze.sh @@ -0,0 +1,111 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: analyze.sh - part of the BeakerLib +# Description: Analyzing test results +# +# Author: Petr Muller +# Author: Petr Splichal +# Author: Jan Hutar +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - analyze - Analyzing test results + +=head1 DESCRIPTION + +Contains helpers for summarizing test results. + +=head1 FUNCTIONS + +=cut + +. $BEAKERLIB/logging.sh +. $BEAKERLIB/journal.sh + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlDejaSum +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Analyze + +=head3 rlDejaSum + +TODO description + + rlDejaSum par1 par2 + +=over + +=item par1 + +TODO description + +=item par2 + +TODO description + +=back + +Return 0 if... TODO + +=cut + +rlDejaSum(){ + rlLog "Summarizing files: $1 $2" + rlLogDebug "Calling beakerlib-deja-summarize routine" + beakerlib-deja-summarize $1 $2 | while read line + do + rlLog "$line" + done +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=back + +=cut diff --git a/src/beakerlib.sh b/src/beakerlib.sh new file mode 100644 index 0000000..ad57505 --- /dev/null +++ b/src/beakerlib.sh @@ -0,0 +1,359 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: beakerlib.sh - part of the BeakerLib project +# Description: The main BeakerLib script +# +# Author: Petr Muller +# Author: Ondrej Hudlicky +# Author: Jan Hutar +# Author: Petr Splichal +# Author: Ales Zelinka +# Author: Dalibor Pospisil +# Author: Martin Kyral +# Author: Jakub Prokes +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2012 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' + +=pod + +=for comment beakerlib-manual-header + +=head1 NAME + +BeakerLib - a shell-level integration testing library + +=head1 DESCRIPTION + +BeakerLib is a shell-level integration testing library, providing +convenience functions which simplify writing, running and analysis of +integration and blackbox tests. + +The essential features include: + +=over + +=item + +B - uniform logging mechanism (logs & results saved in flexible XML +format, easy to compare results & generate reports) + +=item + +B - logical grouping of test actions, clear separation of setup / test +/ cleanup (preventing false fails) + +=item + +B - common checks affecting the overall results of the individual +phases (checking for exit codes, file existence & content...) + +=item + +B - convenience functions for common operations such as managing +services, backup & restore + +=back + +The main script sets the C variable and sources other scripts where +the actual functions are defined. You should source it at the beginning of your +test with: + + . /usr/share/beakerlib/beakerlib.sh + +See the EXAMPLES section for quick start inspiration. + +See the BKRDOC section for more information about Automated documentation generator for BeakerLib tests. + +=cut + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# beakerlib-manual-include +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:<<'=cut' + +=pod + +=for comment beakerlib-manual-footer + +=head1 EXAMPLES + +=head2 Simple + +A minimal BeakerLib test can look like this: + + . /usr/share/beakerlib/beakerlib.sh + + rlJournalStart + rlPhaseStartTest + rlAssertRpm "setup" + rlAssertExists "/etc/passwd" + rlAssertGrep "root" "/etc/passwd" + rlPhaseEnd + rlJournalEnd + +=head2 Phases + +Here comes a bit more interesting example of a test which sets all +the recommended variables and makes use of the phases: + + # Include the BeakerLib environment + . /usr/share/beakerlib/beakerlib.sh + + # Set the full test name + TEST="/examples/beakerlib/Sanity/phases" + + # Package being tested + PACKAGE="coreutils" + + rlJournalStart + # Setup phase: Prepare test directory + rlPhaseStartSetup + rlAssertRpm $PACKAGE + rlRun 'TmpDir=$(mktemp -d)' 0 'Creating tmp directory' # no-reboot + rlRun "pushd $TmpDir" + rlPhaseEnd + + # Test phase: Testing touch, ls and rm commands + rlPhaseStartTest + rlRun "touch foo" 0 "Creating the foo test file" + rlAssertExists "foo" + rlRun "ls -l foo" 0 "Listing the foo test file" + rlRun "rm foo" 0 "Removing the foo test file" + rlAssertNotExists "foo" + rlRun "ls -l foo" 2 "Listing foo should now report an error" + rlPhaseEnd + + # Cleanup phase: Remove test directory + rlPhaseStartCleanup + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd + rlJournalEnd + + # Print the test report + rlJournalPrintText + +The ouput of the rlJournalPrintText command would produce an +output similar to the following: + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: TEST PROTOCOL + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ LOG ] :: Test run ID : debugging + :: [ LOG ] :: Package : coreutils + :: [ LOG ] :: Installed: : coreutils-7.6-9.fc12.i686 + :: [ LOG ] :: Test started : 2010-02-08 14:55:44 + :: [ LOG ] :: Test finished : 2010-02-08 14:55:50 + :: [ LOG ] :: Test name : /examples/beakerlib/Sanity/phases + :: [ LOG ] :: Distro: : Fedora release 12 (Constantine) + :: [ LOG ] :: Hostname : localhost + :: [ LOG ] :: Architecture : i686 + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Test description + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + PURPOSE of /examples/beakerlib/Sanity/phases + Description: Testing BeakerLib phases + Author: Petr Splichal + + This example shows how the phases work in the BeakerLib on a + trivial smoke test for the "touch", "ls" and "rm" commands from + the coreutils package. + + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Setup + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ PASS ] :: Checking for the presence of coreutils rpm + :: [ PASS ] :: Creating tmp directory + :: [ PASS ] :: Running 'pushd /tmp/tmp.IcluQu5GVS' # no-reboot + :: [ LOG ] :: Duration: 0s + :: [ LOG ] :: Assertions: 3 good, 0 bad + :: [ PASS ] :: RESULT: Setup + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Test + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ PASS ] :: Creating the foo test file + :: [ PASS ] :: File foo should exist + :: [ PASS ] :: Listing the foo test file + :: [ PASS ] :: Removing the foo test file + :: [ PASS ] :: File foo should not exist + :: [ PASS ] :: Listing foo should now report an error + :: [ LOG ] :: Duration: 1s + :: [ LOG ] :: Assertions: 6 good, 0 bad + :: [ PASS ] :: RESULT: Test + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Cleanup + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ PASS ] :: Running 'popd' + :: [ PASS ] :: Removing tmp directory + :: [ LOG ] :: Duration: 1s + :: [ LOG ] :: Assertions: 2 good, 0 bad + :: [ PASS ] :: RESULT: Cleanup + +Note that the detailed test description is read from a separate +file PURPOSE placed in the same directory as the test itself. + +A lot of useful debugging information is reported on the DEBUG level. +You can run your test with C to get them. + +Verbosity of the test logging may be altered also by setting the LOG_LEVEL +variable. Possible values are "ERROR", "WARNING", "INFO" and "DEBUG". You will +see messages like the ones below. + + :: [ WARNING ] :: rlGetArch: Update test to use rlGetPrimaryArch/rlGetSecondaryArch + :: [ DEBUG ] :: rlGetArch: This is architecture 'x86_64' + :: [ ERROR ] :: this function was dropped as its development is completely moved to the beaker library + :: [ INFO ] :: if you realy on this function and you really need to have it present in core beakerlib, file a RFE, please + +Setting LOG_LEVEL="DEBUG" is equivalent to DEBUG=1. + +=head1 BKRDOC + +=over + +=item Description + +Bkrdoc is a documentation generator from tests written using BeakerLib +library. This generator makes documentation from test code with and also +without any documentation markup. + +=item What it's good for + +For fast, brief and reliable documentation creation. It`s good for +quick start with unknown BeakerLib test. Created documentations provides +information about the documentation credibility. Also created documentations +shows environmental variables and helps reader to run test script from +which was documentation created. + +=item Bkrdoc project page + +https://github.com/rh-lab-q/bkrdoc + +=back + +=head1 LINKS + +=over + +=item Project Page + +https://github.com/beakerlib/beakerlib + +=item Manual + +https://github.com/beakerlib/beakerlib/wiki/man + +=item Issues list + +https://github.com/beakerlib/beakerlib/issues + +=item Reporting issues + +https://github.com/beakerlib/beakerlib/issues/new + +=back + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Ondrej Hudlicky + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=item * + +Ales Zelinka + +=item * + +Dalibor Pospisil + +=item * + +Martin Kyral + +=item * + +Jakub Prokes + +=item * + +Jakub Heger + +=back + +=cut + +if set -o | grep posix | grep on ; then + set +o posix + export POSIXFIXED="YES" +else + export POSIXFIXED="NO" +fi + +export __INTERNAL_PERSISTENT_TMP=/var/tmp + +# export COBBLER_SERVER for the usage in tests +test -f /etc/profile.d/cobbler.sh && . /etc/profile.d/cobbler.sh + +set -e +export BEAKERLIB=${BEAKERLIB:-"/usr/share/beakerlib"} +. $BEAKERLIB/storage.sh +. $BEAKERLIB/infrastructure.sh +. $BEAKERLIB/journal.sh +. $BEAKERLIB/libraries.sh +. $BEAKERLIB/logging.sh +. $BEAKERLIB/rpms.sh +. $BEAKERLIB/testing.sh +. $BEAKERLIB/analyze.sh +. $BEAKERLIB/performance.sh +. $BEAKERLIB/virtualX.sh +. $BEAKERLIB/synchronisation.sh +if [ -d $BEAKERLIB/plugins/ ] ; then + for __INTERNAL_beakerlib_source_plugin in $BEAKERLIB/plugins/*.sh ; do + . $__INTERNAL_beakerlib_source_plugin + done +fi +set +e diff --git a/src/extradocs/Makefile b/src/extradocs/Makefile new file mode 100644 index 0000000..03b58ba --- /dev/null +++ b/src/extradocs/Makefile @@ -0,0 +1,37 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2015 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.PHONY: install clean + +build: + +install: + @# usage examples + mkdir -p $(DESTDIR)/$(PKGDOCDIR)/examples/ + mkdir -p $(DESTDIR)/$(PKGDOCDIR)/examples/simple + mkdir -p $(DESTDIR)/$(PKGDOCDIR)/examples/phases + mkdir -p $(DESTDIR)/$(PKGDOCDIR)/examples/apache + install -p -m 644 examples/simple/* $(DESTDIR)/$(PKGDOCDIR)/examples/simple + install -p -m 644 examples/phases/* $(DESTDIR)/$(PKGDOCDIR)/examples/phases + install -p -m 644 examples/apache/* $(DESTDIR)/$(PKGDOCDIR)/examples/apache + @# testwatcher design docs + install -p -m 644 testwatcher.txt $(DESTDIR)/$(PKGDOCDIR) + +clean: diff --git a/src/extradocs/examples/apache/PURPOSE b/src/extradocs/examples/apache/PURPOSE new file mode 100644 index 0000000..1e7d1a3 --- /dev/null +++ b/src/extradocs/examples/apache/PURPOSE @@ -0,0 +1,9 @@ +PURPOSE of /examples/beakerlib/Sanity/apache +Description: Apache example test +Author: Petr Splichal + +This is an example BeakerLib test. It presents the most essential +BeakerLib functions and concepts on a basic Apache sanity test. + +Have a look at the code to get an initial overview and inspiration +before you start writing your first BeakerLib test ;-) diff --git a/src/extradocs/examples/apache/runtest.sh b/src/extradocs/examples/apache/runtest.sh new file mode 100755 index 0000000..ff21b9a --- /dev/null +++ b/src/extradocs/examples/apache/runtest.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# runtest.sh of /examples/beakerlib/Sanity/apache +# Description: Apache example test +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2009 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Include the BeakerLib environment +. /usr/share/beakerlib/beakerlib.sh + +# Set the full test name +TEST="/examples/beakerlib/Sanity/apache" + +# Package being tested +PACKAGE="httpd" + +HttpdPages="/var/www/html" +HttpdLogs="/var/log/httpd" + +rlJournalStart + rlPhaseStartSetup "Setup" + # Make sure the httpd package is installed + rlAssertRpm "httpd" + + # Use rlRun to check the exit status of a command (0 expected here) + rlRun 'TmpDir=$(mktemp -d)' 0 + pushd $TmpDir + + # Add a comment to make the final report more readable + rlRun "rlFileBackup --clean $HttpdPages $HttpdLogs" 0 "Backing up" + rlRun "echo 'Welcome to Test Page!' > $HttpdPages/index.html" 0 \ + "Creating a simple welcome page" + + # Both comment & and exit status can be omitted (expecting success) + rlRun "rm -f $HttpdLogs/*" + + # Make sure the httpd service is running with fresh configuration + # (restarts if necessary, remembers the original state) + rlRun "rlServiceStart httpd" + rlPhaseEnd # Sums up phase asserts and reports the overall phase result + + rlPhaseStartTest "Test Existing Page" + # Get the page + rlRun "wget http://localhost/" 0 "Fetching the welcome page" + # Check whether the page has been successfully fetched + rlAssertExists "index.html" + # Log page content + rlLog "index.html contains: $(stderr" 1,8 \ + "Trying to access a nonexistent page" + # The file should not be created + rlAssertNotExists "missing.html" + # An error message should be reported to stderr + rlAssertGrep "Not Found" "stderr" + # The access log should contain a 404 record + rlAssertGrep "GET /missing.html HTTP.*404" "$HttpdLogs/access_log" + # And the error log should describe the problem + rlAssertGrep "does not exist.*missing.html" "$HttpdLogs/error_log" + rlPhaseEnd + + rlPhaseStartCleanup "Cleanup" + popd + # Let's clean up all the mess we've made + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + + # Restore the www and log directories to their original state + rlRun "rlFileRestore" + + # Restore httpd service to it's original state + rlRun "rlServiceRestore httpd" + rlPhaseEnd +rlJournalEnd + +# Print the test report +rlJournalPrintText diff --git a/src/extradocs/examples/phases/PURPOSE b/src/extradocs/examples/phases/PURPOSE new file mode 100644 index 0000000..a3f0268 --- /dev/null +++ b/src/extradocs/examples/phases/PURPOSE @@ -0,0 +1,7 @@ +PURPOSE of /examples/beakerlib/Sanity/phases +Description: Testing BeakerLib phases +Author: Petr Splichal + +This example shows how the phases work in the BeakerLib on a +trivial smoke test for the "touch", "ls" and "rm" commands from +the coreutils package. diff --git a/src/extradocs/examples/phases/runtest.sh b/src/extradocs/examples/phases/runtest.sh new file mode 100755 index 0000000..6c1e147 --- /dev/null +++ b/src/extradocs/examples/phases/runtest.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# runtest.sh of /examples/beakerlib/Sanity/phases +# Description: Testing BeakerLib phases +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2009 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Include the BeakerLib environment +. /usr/share/beakerlib/beakerlib.sh + +# Set the full test name +TEST="/examples/beakerlib/Sanity/phases" + +# Package being tested +PACKAGE="coreutils" + +rlJournalStart + # Setup phase: Prepare test directory + rlPhaseStartSetup + rlAssertRpm $PACKAGE + rlRun 'TmpDir=$(mktemp -d)' 0 "Creating tmp directory" + rlRun "pushd $TmpDir" + rlPhaseEnd + + # Test phase: Testing touch, ls and rm commands + rlPhaseStartTest + rlRun "touch foo" 0 "Creating the foo test file" + rlAssertExists "foo" + rlRun "ls -l foo" 0 "Listing the foo test file" + rlRun "rm foo" 0 "Removing the foo test file" + rlAssertNotExists "foo" + rlRun "ls -l foo" 2 "Listing foo should now report an error" + rlPhaseEnd + + # Cleanup phase: Remove test directory + rlPhaseStartCleanup + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd +rlJournalEnd + +# Print the test report +rlJournalPrintText diff --git a/src/extradocs/examples/simple/PURPOSE b/src/extradocs/examples/simple/PURPOSE new file mode 100644 index 0000000..f5acc56 --- /dev/null +++ b/src/extradocs/examples/simple/PURPOSE @@ -0,0 +1,7 @@ +PURPOSE of /examples/beakerlib/Sanity/simple +Description: Minimal BeakerLib sanity test +Author: Petr Splichal + +This is a minimal sanity test for BeakerLib. It contains a single +phase with a couple of asserts. We Just check that the "setup" +package is installed and that there is a sane /etc/passwd file. diff --git a/src/extradocs/examples/simple/runtest.sh b/src/extradocs/examples/simple/runtest.sh new file mode 100755 index 0000000..0379ddb --- /dev/null +++ b/src/extradocs/examples/simple/runtest.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# runtest.sh of /examples/beakerlib/Sanity/simple +# Description: Minimal BeakerLib sanity test +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2009 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Include the BeakerLib environment +. /usr/share/beakerlib/beakerlib.sh + +# Set the full test name +TEST="/examples/beakerlib/Sanity/simple" + +# Package being tested +PACKAGE="setup" + +rlJournalStart + rlPhaseStartTest + rlAssertRpm "setup" + rlAssertExists "/etc/passwd" + rlAssertGrep "root" "/etc/passwd" + rlPhaseEnd +rlJournalEnd + +# Print the test report +rlJournalPrintText diff --git a/src/extradocs/testwatcher.txt b/src/extradocs/testwatcher.txt new file mode 100644 index 0000000..fa6a077 --- /dev/null +++ b/src/extradocs/testwatcher.txt @@ -0,0 +1,170 @@ += TESTWATCHER = + +== The watcher == +The testwatcher itself is a python-based Beaker-aware "watchdog" for a command. +The basic syntax is: + + testwatcher.py [arg1] [arg2] ... + +which starts cmd with the specified args in a new process group. When the cmd +finishes, the watcher exits cleanly with it. If the watcher is somehow +interrupted (SIGINT/SIGHUP), it SIGKILLs the process group. +That's about all the basic functionality. + +== Cleanup support == +The testwatcher exports an environment variable called 'TESTWATCHER_CLPATH' +to the cmd. This variable points to a zero-length non-executable file +by default. When the cmd finishes cleanly or is interrupted, the watcher +tries to read a cleanup script path from the file. If the path points to +an existing executable and the watcher is allowed to execute it, it does so. + +Ie. +$ echo "$TESTWATCHER_CLPATH" +/var/tmp/testwatcher-gf9Kadf3 +$ cat "$TESTWATCHER_CLPATH" +/tmp/beakerlib-GX3B2Ef/cleanup.sh + +The cmd can therefore keep (atomically) replacing the cleanup script on the +specified path with newer versions of the executable it wants to execute upon +finish/interrupt, as a "cleanup". + +== Beaker awareness == +The testwatcher can be used from a command line (without Beaker) in which case +it doesn't measure any time limits and can be interrupted only by SIGINT. + +When it however detects Beaker harness environment (TASKID env var), it sets up +a special hook in beah that sends back SIGHUP when the LWD expires. In addition, +it is aware of external watchdog (EWD) expiration after LWD expires. + +This creates several possible scenarios (here, "test" is "cmd" from above and +EWD is assumed to be 30 minutes): + + - both test and cleanup finish successfully in time + - nothing special needed + + - LWD is received during test + - test is interrupted, cleanup is executed and given 25min to complete + before also being interrupted + + - test finished, LWD is received during cleanup + - the watcher realizes that testtime expired now (even though test already + finished) and gives cleanup as much time as it can (additional 25min) + before interrupting it + +All scenarios hopefully avoid EWD being triggered - even if cleanup is +unfortunately interrupted, it doesn't have to be fatal for other tests. + +== Reboot handling == +The testwatcher itself doesn't have any reboot-aware mechanisms, when it +receives SIGTERM, it simply exits without killing anything. It presumes +that SIGTERM is realistically received only on system reboot (if not SIGKILL) +when all other processes (incl. the test or cleanup) are also being killed. +Therefore there's little it can do to guarantee cleanup execution. + +The TESTWATCHER_CLPATH will also point elsewhere on each watcher execution. +Any reboot-aware logic therefore needs to be implemented by the cmd/test. + + += BEAKERLIB CLEANUP MANAGEMENT = + +The bulk of this functionality, user-wise, is documented as "Cleanup management" +in the beakerlib manpage. This section describes only interaction with the +testwatcher. + +== Atomic cleanups == +The bash-based beakerlib cleanup implementation maintains a "buffer", which is +essentially a shell script, to which it prepends/appends commands. Upon each +prepend/append, a final version of the script is generated in a temporary +location, made executable, and then atomically moved to where the final cleanup +script path is (path written to the file at TESTWATCHER_CLPATH). +This guarantees that either the old or the new version of the cleanup script +is executed on interrupt, not a mix of both (or incomplete file). + +== Hooking runtest.sh into testwatcher == +For the test to run under testwatcher, it needs to be executed using the syntax +specified above ("The watcher"). When using one of the standard beaker-wizard +layouts, this is most easily done in the Makefile, simply replace + +run: $(FILES) build + ./runtest.sh + +with + +run: $(FILES) build + beakerlib-testwatcher ./runtest.sh + +The beakerlib cleanup management then detects and uses TESTWATCHER_CLPATH. + +== System reboot == +All cleanup-related info, incl. the "buffer", is stored in BEAKERLIB_DIR. +When running outside Beaker harness, BEAKERLIB_DIR points to a volatile tmpdir, +which we can't predict and therefore any existing buffer is lost on reboot. +However when running under beah, BEAKERLIB_DIR is based on TESTID and already +detected and set by rlJournalStart. Any existing cleanup buffer is therefore +re-used from previous "session" automatically, by design. + +Upon beakerlib initialization, rlJournalStart detects existing cleanup script +and re-hooks it into the testwatcher, cleanup persistence is achieved. + +== Example == +Due to how the cleanup buffer works, it's not recommended to have a second +cleanup in rlPhaseStartCleanup - it would run as part of the test process +with no guarantees from the watcher. + +Instead, declare cleanup incrementally, anywhere in the test body: + +rlJournalStart + rlPhaseStartSetup + # create tmpdir + rlRun 'TmpDir=$(mktemp -d)' 0 "Creating tmp directory" + rlPrependCleanup "rm -rf \"$TmpDir\"" + rlRun "pushd $TmpDir" + + # create big file and setup it on loopback + rlRun "fallocate -l 100M bigfile" || rlDie + rlPrependCleanup "losetup -d /dev/loop0" + rlRun "losetup /dev/loop0 bigfile" || rlDie + + # create filesystem on it and mount it + rlRun "mkfs.ext3 /dev/loop0" || rlDie + rlRun "mkdir mntpoint" + rlPrependCleanup "umount -f $PWD/mntpoint" + rlRun "mount /dev/loop0 mntpoint" || rlDie + rlPhaseEnd + + rlPhaseStartTest + rlRun "ln -s / mntpoint/linktest" 0 "symlink can be created on ext3" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd + +== Less extreme example == +Since giving up the classical cleanup phase is not for everybody, you can still +use it along with testwatcher - the watcher simply sees it as part of the test +and as long as you specify TestTime long enough, it works just like before. + +IOW you can simply use the watcher only for "critical" things like restoring +system-wide files (think of /etc/hosts). + +rlJournalStart + rlPhaseStartSetup + rlFileBackup /etc/hosts + rlAppendCleanup "rlFileRestore" + rlRun 'TmpDir=$(mktemp -d)' 0 "Creating tmp directory" + rlRun "pushd $TmpDir" + rlPhaseEnd + + rlPhaseStartTest + rlRun "echo \"127.0.0.1 example\" >> /etc/hosts" + rlRun "nc -l 1234 &" + ncpid=$! + rlRun "nc example 1234 <<<\"test text\"" + rlPhaseEnd + + rlPhaseStartCleanup + rlRun "kill $ncpid" + rlRun "popd" + rlRun "rm -r $TmpDir" 0 "Removing tmp directory" + rlPhaseEnd +rlJournalPrintText +rlJournalEnd diff --git a/src/infrastructure.sh b/src/infrastructure.sh new file mode 100644 index 0000000..750be8e --- /dev/null +++ b/src/infrastructure.sh @@ -0,0 +1,1943 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: infrastructure.sh - part of the BeakerLib project +# Description: Mounting, backup & service helpers +# +# Author: Petr Muller +# Author: Jan Hutar +# Author: Petr Splichal +# Author: Ales Zelinka +# Author: Karel Srot +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2013 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - infrastructure - mounting, backup and service helpers + +=head1 DESCRIPTION + +BeakerLib functions providing checking and mounting NFS shares, backing up and +restoring files and controlling running services. + +=head1 FUNCTIONS + +=cut + +. "$BEAKERLIB"/logging.sh +. "$BEAKERLIB"/testing.sh +. "$BEAKERLIB"/storage.sh + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__INTERNAL_rlRunsInBeaker() { + if [ -n "$JOBID" ] && [ -n "$TESTID" ] + then + return 0 + else + return 1 + fi +} + +__INTERNAL_CheckMount(){ + local MNTPATH="$1" + local MNTHOST="$2" + local OPTIONS="$3" + local RETCODE=0 + + # if a host was specified, the semantics is "is PATH mounted on HOST?" + # if a host is not specified, the semantics is "is PATH mounted somewhere?" + if [ -z "$MNTHOST" ] + then + mount | grep "on ${MNTPATH%/} type" + RETCODE=$? + else + mount | grep "on ${MNTPATH%/} type" | grep -E "^$MNTHOST[ :/]" + RETCODE=$? + fi + + # check if the mountpoint is mounted with given options + if [ $RETCODE -eq 0 ] && [ -n "$OPTIONS" ] + then + IFS=',' read -ra OPTS <<< "$OPTIONS" + for option in "${OPTS[@]}"; do + mount | grep "on ${MNTPATH%/} type" | grep "[(,]$option[),]" + [ $? -eq 0 ] || RETCODE=2 + done + fi + + return $RETCODE +} + +__INTERNAL_Mount(){ + local SERVER=$1 + local MNTPATH=$2 + local WHO=$3 + local OPTIONS=$4 + + if __INTERNAL_CheckMount "$MNTPATH" + then + if [[ -z "$OPTIONS" ]]; then + rlLogInfo "$WHO already mounted: success" + return 0 + else + [[ "$OPTIONS" =~ remount ]] || OPTIONS="remount,$OPTIONS" + fi + elif [ ! -d "$MNTPATH" ] + then + rlLogInfo "$WHO creating directory $MNTPATH" + mkdir -p "$MNTPATH" + fi + if [ -z "$OPTIONS" ] ; then + rlLogInfo "$WHO mounting $SERVER on $MNTPATH with default mount options" + mount "$SERVER" "$MNTPATH" + else + rlLogInfo "$WHO mounting $SERVER on $MNTPATH with custom mount options: $OPTIONS" + mount -o "$OPTIONS" "$SERVER" "$MNTPATH" + fi + if [ $? -eq 0 ] + then + rlLogInfo "$WHO success" + return 0 + else + rlLogWarning "$WHO failure" + return 1 + fi +} + +__INTERNAL_rlCleanupGenFinal() +{ + local __varname= + local __newfinal="$__INTERNAL_CLEANUP_FINAL".tmp + if [ -e "$__newfinal" ]; then + rm -f "$__newfinal" || return 1 + fi + touch "$__newfinal" || return 1 + + # head + cat > "$__newfinal" </dev/null; then + echo "$__varname" "2>/dev/null" >> "$__newfinal" + else + echo "$__varname" >> "$__newfinal" + fi + done + # - functions + declare -f >> "$__newfinal" + + # journal/phase start + cat >> "$__newfinal" <> "$__newfinal" + + # tail + cat >> "$__newfinal" < for asserting the result but keep in mind meaning of exit codes, +especialy exit code 8, if using without --clean option. + +=over + +=item --clean + +If this option is provided (have to be first option of the command), +then file/dir backuped using this command (provided in next +options) will be (recursively) removed before we will restore it. +This option implies --missing-ok, this can be overridden by --no-missing-ok. + +=item --namespace name + +Specifies the namespace to use. +Namespaces can be used to separate backups and their restoration. + +=item --missing-ok + +Do not raise an error in case of missing file to backup. + +=item --no-missing-ok + +Do raise an error in case of missing file to backup. +This is useful with --clean. This behaviour can be globally +set by global variable BEAKERLIB_FILEBACKUP_MISSING_OK=false. + +=item file + +Files and/or directories to be backed up. + +=back + +Returns 0 if the backup was successful. +Returns 1 if parsing of parameters was not successful. +Returns 2 if no files specification was provided. +Returns 3 if BEAKERLIB_DIR variable is not set, e.g. rlJournalStart was not executed. +Returns 4 if creating of main backup destination directories was not successful. +Returns 5 if creating of file specific backup destination directories was not successful. +Returns 6 if the copy of backed up files was not successful. +Returns 7 if attributes of backedup files were not successfuly copied. +Returns 8 if backed up files does not exist. This can be suppressed based on other options. + + +=head4 Example with --clean: + + touch cleandir/aaa + rlRun "rlFileBackup --clean cleandir/" + touch cleandir/bbb + ls cleandir/ + aaa bbb + rlRun "rlFileRestore" + ls cleandir/ + aaa + +=cut + +__INTERNAL_FILEBACKUP_NAMESPACE="rlFileBackupNamespace" + +__INTERNAL_FILEBACKUP_SET_PATH_CLEAN() { + local path="$1" + local path_encoded="$( rlHash -a hex "$path" )" + + local namespace="_$2" + local namespace_encoded="$( rlHash -a hex "$namespace" )" + + rlLogDebug "rlFileBackup: Setting up the cleaning lists" + rlLogDebug "rlFileBackup: Path [$path] Encoded [$path_encoded]" + rlLogDebug "rlFileBackup: Namespace [$namespace] Encoded [$namespace_encoded]" + + local CURRENT="$( __INTERNAL_ST_GET --namespace="$__INTERNAL_FILEBACKUP_NAMESPACE" $namespace_encoded )" + if [ -z "$CURRENT" ] + then + __INTERNAL_ST_PUT --namespace="$__INTERNAL_FILEBACKUP_NAMESPACE" $namespace_encoded $path_encoded + else + __INTERNAL_ST_PUT --namespace="$__INTERNAL_FILEBACKUP_NAMESPACE" $namespace_encoded "$CURRENT $path_encoded" + fi +} + +__INTERNAL_FILEBACKUP_CLEAN_PATHS() { + local namespace="_$1" + local namespace_encoded="$( rlHash -a hex "$namespace" )" + local res=0 + + rlLogDebug "rlFileRestore: Fetching clean-up lists for namespace: [$namespace] (encoded as [$namespace_encoded])" + + local PATHS="$( __INTERNAL_ST_GET --namespace="$__INTERNAL_FILEBACKUP_NAMESPACE" $namespace_encoded )" + + rlLogDebug "rlFileRestore: Fetched: [$PATHS]" + + local path + for path in $PATHS + do + local path_decoded="$( rlUnhash -a hex "$path" )" + echo "$path_decoded" + if rm -rf "$path_decoded"; + then + rlLogDebug "rlFileRestore: Cleaning $path_decoded successful" + else + rlLogError "rlFileRestore: Failed to clean $path_decoded" + let res++ + fi + done + return $res +} + +rlFileBackup() { + local backup status file path dir failed selinux acl missing_ok="$BEAKERLIB_FILEBACKUP_MISSING_OK" + + local OPTS clean="" namespace="" + + # getopt will cut off first long opt when no short are defined + OPTS=$(getopt -o "." -l "clean,namespace:,no-missing-ok,missing-ok" -- "$@") + [ $? -ne 0 ] && return 1 + + eval set -- "$OPTS" + while true; do + case "$1" in + '--clean') clean=1; missing_ok="${missing_ok:-true}"; ;; + '--missing-ok') missing_ok="true"; ;; + '--no-missing-ok') missing_ok="false"; ;; + '--namespace') shift; namespace="$1"; ;; + --) shift; break ;; + esac + shift + done; + missing_ok="${missing_ok:-false}" + + # check parameter sanity + if [ -z "$1" ]; then + rlLogError "rlFileBackup: Nothing to backup... supply a file or dir" + return 2 + fi + + # check if we have '--clean' option and save items if we have + if [ "$clean" ]; then + local file + for file in "$@"; do + rlLogDebug "rlFileBackup: Adding '$file' to the clean list" + __INTERNAL_FILEBACKUP_SET_PATH_CLEAN "$file" "$namespace" + done + fi + + # set the backup dir + if [ -z "$BEAKERLIB_DIR" ]; then + rlLogError "rlFileBackup: BEAKERLIB_DIR not set, run rlJournalStart first" + return 3 + fi + + # backup dir to use, append namespace if defined + backup="$BEAKERLIB_DIR/backup${namespace:+-$namespace}" + rlLogInfo "using '$backup' as backup destination" + + # create backup dir (unless it already exists) + if [ -d "$backup" ]; then + rlLogDebug "rlFileBackup: Backup dir ready: $backup" + else + if mkdir "$backup"; then + rlLogDebug "rlFileBackup: Backup dir created: $backup" + else + rlLogError "rlFileBackup: Creating backup dir failed" + return 4 + fi + fi + + # do the actual backup + status=0 + # detect selinux & acl support + if selinuxenabled + then + selinux=true + else + selinux=false + fi + + if setfacl -m u:root:rwx "$BEAKERLIB_DIR" &>/dev/null + then + acl=true + else + acl=false + fi + + local file + for file in "$@"; do + # convert relative path to absolute, remove trailing slash + file="$(echo "$file" | sed "s|^\([^/]\)|$PWD/\1|" | sed 's|/$||')" + # follow symlinks in parent dir + path="$(dirname "$file")" + path="$(readlink -n -m "$path")" + file="$path/$(basename "$file")" + + # bail out if the file does not exist + if ! [ -e "$file" ]; then + $missing_ok || { + rlLogError "rlFileBackup: File $file does not exist." + status=8 + } + continue + fi + + if [ -h "$file" ]; then + rlLogWarning "rlFileBackup: Backing up symlink (not its target): $file" + fi + + # create path + if ! mkdir -p "${backup}${path}"; then + rlLogError "rlFileBackup: Cannot create ${backup}${path} directory." + status=5 + continue + fi + + # copy files + if ! cp -fa "$file" "${backup}${path}"; then + rlLogError "rlFileBackup: Failed to copy $file to ${backup}${path}." + status=6 + continue + fi + + # preserve path attributes + dir="$path" + failed=false + while true; do + $acl && { getfacl --absolute-names "$dir" | setfacl --set-file=- "${backup}${dir}" || failed=true; } + $selinux && { chcon --reference "$dir" "${backup}${dir}" || failed=true; } + chown --reference "$dir" "${backup}${dir}" || failed=true + chmod --reference "$dir" "${backup}${dir}" || failed=true + touch --reference "$dir" "${backup}${dir}" || failed=true + [ "$dir" == "/" ] && break + dir=$(dirname "$dir") + done + if $failed; then + rlLogError "rlFileBackup: Failed to preserve all attributes for backup path ${backup}${path}." + status=7 + continue + fi + + # everything ok + rlLogDebug "rlFileBackup: $file successfully backed up to $backup" + done + + return $status +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlFileRestore +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlFileRestore + +Restore backed up files to their original location. +C does not remove new files appearing after backup +has been made. If you don't want to leave anything behind just +remove the whole original tree before running C, +or see C<--clean> option of C. + + rlFileRestore [--namespace name] + +You can use C for asserting the result. + +=over + +=item --namespace name + +Specifies the namespace to use. +Namespaces can be used to separate backups and their restoration. + +=back + +Returns 0 if backup dir is found and files are restored successfully. + +Return code bits meaning XXXXX + ||||| + ||||\_ error parsing parameters + |||\__ could not find backup directory + ||\___ files cleanup failed + |\____ files restore failed + \_____ no files were restored nor cleaned + +=cut +#' + +rlFileRestore() { + local OPTS namespace="" backup res=0 + + # getopt will cut off first long opt when no short are defined + OPTS=$(getopt -o "n:" -l "namespace:" -- "$@") + [ $? -ne 0 ] && return 1 + + eval set -- "$OPTS" + while true; do + case "$1" in + '--namespace') shift; namespace="$1"; shift ;; + --) shift; break ;; + esac + done; + + # check for backup dir first, append namespace if defined + backup="$BEAKERLIB_DIR/backup${namespace:+-$namespace}" + if [ -d "$backup" ]; then + rlLogDebug "rlFileRestore: Backup dir ready: $backup" + else + rlLogError "rlFileRestore: Cannot find backup in $backup" + return 2 + fi + + # clean up if required + local cleaned_files + cleaned_files=$(__INTERNAL_FILEBACKUP_CLEAN_PATHS "$namespace") || (( res |= 4 )) + + # if destination is a symlink, remove the file first + local filecheck + for filecheck in $( find "$backup" | cut --complement -b 1-"${#backup}") + do + if [ -L "/$filecheck" ] + then + rm -f "/$filecheck" + fi + done + + # restore the files + if [[ -n "$(ls -A "$backup")" ]]; then + if cp -fa "$backup"/* / + then + rlLogDebug "rlFileRestore: restoring files from $backup successful" + else + rlLogError "rlFileRestore: failed to restore files from $backup" + (( res |= 8 )) + fi + else + if [[ -n "$cleaned_files" && $(( res & 4)) -eq 0 ]]; then + rlLogDebug "rlFileRestore: there were just some files cleaned up" + else + rlLogError "rlFileRestore: no files were actually restored as there were no files backed up to $backup" + (( res |= 16 )) + fi + fi + + return $res +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlServiceStart +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Services + +Following routines implement comfortable way how to start/stop +system services with the possibility to restore them to their +original state after testing. + +=head3 rlServiceStart + +Make sure the given C is running with fresh +configuration. (Start it if it is stopped and restart if it is +already running.) In addition, when called for the first time, the +current state is saved so that the C can be restored to +its original state when testing is finished, see +C. + + rlServiceStart service [service...] + +=over + +=item service + +Name of the service(s) to start. + +=back + +Returns number of services which failed to start/restart; thus +zero is returned when everything is OK. + +=cut + +__INTERNAL_SERVICE_NS="rlService" +__INTERNAL_SERVICE_STATE_SECTION="savedStates" +__INTERNAL_SERVICE_STATE_SECTION_PERSISTENCE="savedPersistentStates" + +__INTERNAL_SERVICE_STATE_SAVE(){ + local serviceId=$1 + local serviceState=$2 + local persistence=${3:-"false"} + + if [[ "$persistence" == "false" ]]; then + local section=$__INTERNAL_SERVICE_STATE_SECTION + else + local section=$__INTERNAL_SERVICE_STATE_SECTION_PERSISTENCE + fi + + __INTERNAL_ST_PUT --namespace="$__INTERNAL_SERVICE_NS" --section="$section" $serviceId $serviceState +} + +__INTERNAL_SERVICE_STATE_LOAD(){ + local serviceId=$1 + local persistence=${2:-"false"} + + if [[ "$persistence" == "false" ]]; then + local section=$__INTERNAL_SERVICE_STATE_SECTION + else + local section=$__INTERNAL_SERVICE_STATE_SECTION_PERSISTENCE + fi + __INTERNAL_ST_GET --namespace="$__INTERNAL_SERVICE_NS" --section="$section" $serviceId +} + +__INTERNAL_SERVICE_initial_state_save() { + local service=$1 + local status=$2 + local persistence=${3:-"false"} + + # if the original state hasn't been saved yet, do it now! + local serviceId="$(echo $service | sed 's/[^a-zA-Z0-9]//g')" + + local wasRunning="$( __INTERNAL_SERVICE_STATE_LOAD $serviceId "$persistence")" + rlLogDebug "Previous state of service $service: $wasRunning" + if [ -z "$wasRunning" ]; then + # was running + if [ $status == 0 ]; then + rlLogDebug "rlServiceStart: Original state of $service saved (running)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId true "$persistence" + # was stopped + elif [ $status == 3 ]; then + rlLogDebug "rlServiceStart: Original state of $service saved (stopped)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false "$persistence" + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlServiceStart: service $service status returned $status" + rlLogWarning "rlServiceStart: Guessing that original state of $service is stopped" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false "$persistence" + fi + fi +} + +# __INTERNAL_SERVICE operation service.. +# returns last failure +__INTERNAL_SERVICE() { + local res=0 + local op=$1 + shift + while [[ -n "$1" ]]; do + PAGER= service $1 $op || res=$? + shift + done + return $res +} + +# __INTERNAL_SERVICE_systemctl operation systemctl.. +# returns last failure +__INTERNAL_SYSTEMCTL() { + systemctl --no-pager "$@" +} + +__INTERNAL_SERVICES_LIST="$BEAKERLIB_DIR/services_list" + +rlServiceStart() { + # at least one service has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlServiceStart: You have to supply at least one service name" + return 99 + fi + + local failed=0 + + # create file to store list of services, if it doesn't already exist + touch $__INTERNAL_SERVICES_LIST + + local service + for service in "$@"; do + __INTERNAL_SERVICE status "$service" + local status=$? + + # if the original state hasn't been saved yet, do it now! + local serviceId="$(echo $service | sed 's/[^a-zA-Z0-9]//g')" + local wasRunning="$( __INTERNAL_SERVICE_STATE_LOAD $serviceId)" + if [ -z "$wasRunning" ]; then + echo "$service" >> $__INTERNAL_SERVICES_LIST + # was running + if [ $status == 0 ]; then + rlLogDebug "rlServiceStart: Original state of $service saved (running)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId true + # was stopped + elif [ $status == 3 ]; then + rlLogDebug "rlServiceStart: Original state of $service saved (stopped)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlServiceStart: service $service status returned $status" + rlLogWarning "rlServiceStart: Guessing that original state of $service is stopped" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false + fi + fi + + # if the service is running, stop it first + if [ $status == 0 ]; then + rlLog "rlServiceStart: Service $service already running, stopping first." + if ! __INTERNAL_SERVICE stop "$service"; then + # if service stop failed, inform the user and provide info about service status + rlLogWarning "rlServiceStart: Stopping service $service failed." + rlLogWarning "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + fi + + # finally let's start the service! + if __INTERNAL_SERVICE start "$service"; then + rlLog "rlServiceStart: Service $service started successfully" + else + # if service start failed, inform the user and provide info about service status + rlLogError "rlServiceStart: Starting service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlServiceStop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlServiceStop + +Make sure the given C is stopped. Stop it if it is +running and do nothing when it is already stopped. In addition, +when called for the first time, the current state is saved so that +the C can be restored to its original state when testing +is finished, see C. + + rlServiceStop service [service...] + +=over + +=item service + +Name of the service(s) to stop. + +=back + +Returns number of services which failed to become stopped; thus +zero is returned when everything is OK. + +=cut + +rlServiceStop() { + # at least one service has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlServiceStop: You have to supply at least one service name" + return 99 + fi + + local failed=0 + + # create file to store list of services, if it doesn't already exist + touch $__INTERNAL_SERVICES_LIST + + local service + for service in "$@"; do + __INTERNAL_SERVICE status "$service" + local status=$? + + # if the original state hasn't been saved yet, do it now! + + local serviceId="$(echo $service | sed 's/[^a-zA-Z0-9]//g')" + local wasRunning="$(__INTERNAL_SERVICE_STATE_LOAD $serviceId)" + if [ -z "$wasRunning" ]; then + echo "$service" >> $__INTERNAL_SERVICES_LIST + # was running + if [ $status == 0 ]; then + rlLogDebug "rlServiceStop: Original state of $service saved (running)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId true + # was stopped + elif [ $status == 3 ]; then + rlLogDebug "rlServiceStop: Original state of $service saved (stopped)" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlServiceStop: service $service status returned $status" + rlLogWarning "rlServiceStop: Guessing that original state of $service is stopped" + __INTERNAL_SERVICE_STATE_SAVE $serviceId false + fi + fi + + # if the service is stopped, do nothing + if [ $status == 3 ]; then + rlLogDebug "rlServiceStop: Service $service already stopped, doing nothing." + continue + fi + + # finally let's stop the service! + if __INTERNAL_SERVICE stop "$service"; then + rlLogDebug "rlServiceStop: Service $service stopped successfully" + else + # if service stop failed, inform the user and provide info about service status + rlLogError "rlServiceStop: Stopping service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlServiceRestore +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlServiceRestore + +Restore given C into its original state (before the first +C or C was called). + + rlServiceRestore [service...] + +=over + +=item service + +Name of the service(s) to restore to original state. +All services will be restored in reverse order if no service name is given. + +=back + +Returns number of services which failed to get back to their +original state; thus zero is returned when everything is OK. + +=cut + +rlServiceRestore() { + # create file to store list of services, if it doesn't already exist + touch $__INTERNAL_SERVICES_LIST + + if [ $# -lt 1 ]; then + local services=`tac $__INTERNAL_SERVICES_LIST` + if [ -z "$services" ]; then + rlLogWarning "rlServiceRestore: There are no services to restore" + else + rlLogDebug "rlServiceRestore: No service supplied, restoring all" + fi + else + local services=$@ + fi + + local failed=0 + + local service + for service in $services; do + # if the original state hasn't been saved, then something's wrong + local serviceId="$(echo $service | sed 's/[^a-zA-Z0-9]//g')" + local wasRunning="$( __INTERNAL_SERVICE_STATE_LOAD $serviceId )" + local wasEnabled="$( __INTERNAL_SERVICE_STATE_LOAD ${serviceId} "true")" + if [ -z "$wasRunning" ] && [ -z "$wasEnabled" ]; then + rlLogError "rlServiceRestore: Original state of $service was not saved, nothing to do" + ((failed++)) + continue + fi + + #### + # part handling start/stop actions + if [ -n "$wasRunning" ]; then + + $wasRunning && wasStopped=false || wasStopped=true + rlLogDebug "rlServiceRestore: Restoring $service to original state ($( + $wasStopped && echo "stopped" || echo "running"))" + + # find out current state + __INTERNAL_SERVICE status "$service" + local status=$? + if [ $status == 0 ]; then + isStopped=false + elif [ $status == 3 ]; then + isStopped=true + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlServiceRestore: service $service status returned $status" + rlLogWarning "rlServiceRestore: Guessing that current state of $service is stopped" + isStopped=true + fi + + # if stopped and was stopped -> nothing to do + if $isStopped; then + if $wasStopped; then + rlLogDebug "rlServiceRestore: Service $service is already stopped, doing nothing" + continue + fi + # if running, we have to stop regardless original state + else + if __INTERNAL_SERVICE stop "$service"; then + rlLogDebug "rlServiceRestore: Service $service stopped successfully" + else + # if service stop failed, inform the user and provide info about service status + rlLogError "rlServiceRestore: Stopping service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + continue + fi + fi + + # if was running then start again + if ! $wasStopped; then + if __INTERNAL_SERVICE start "$service"; then + rlLogDebug "rlServiceRestore: Service $service started successfully" + else + # if service start failed, inform the user and provide info about service status + rlLogError "rlServiceRestore: Starting service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + continue + fi + fi + fi + + #### + # part handling enable/disable + if [ -n "$wasEnabled" ]; then + local error="false" + # as enablement and disablement of service does not change actual + # state, we can use simpler approach than in start/stop case + if [ "$wasEnabled" == "true" ];then + if rlServiceEnable ${service}; then + rlLogDebug "rlServiceRestore: Enablement of service $service successful." + else + rlLogError "rlServiceRestore: Enablement of service $service failed." + error="true" + fi + else + if rlServiceDisable ${service};then + rlLogDebug "rlServiceRestore: Disablement of service $service successful." + else + rlLogError "rlServiceRestore: Disablement of service $service failed." + error="true" + fi + fi + if [ "$error" == "true" ]; then + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + continue + fi + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlServiceEnable +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlServiceEnable + +Enables selected services if needed, or does nothing if already enabled. +In addition, when called for the first time, the current state is saved +so that the C can be restored to its original state when testing +is finished, see C. + + rlServiceEnable service [service...] + +=over + +=item service + +Name of the service(s) to enable. + +=back + +Returns number of services which failed enablement; thus +zero is returned when everything is OK. + +=cut + + +rlServiceEnable() { + # at least one service has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlServiceEnable: You have to supply at least one service name" + return 99 + fi + + local failed=0 + + local service + for service in "$@"; do + chkconfig $service > /dev/null + local status=$? + + # if the original state hasn't been saved yet, do it now! + __INTERNAL_SERVICE_initial_state_save $service $status "true" + + # if the service is enabled, nothing more is needed + if [ $status == 0 ]; then + rlLog "rlServiceEnable: Service $service is already enabled." + continue + fi + + # enable service + if chkconfig "$service" on; then + rlLog "rlServiceEnable: Service $service enabled successfully" + else + # if service start failed, inform the user and provide info about service status + rlLogError "rlServiceEnable: Enablement of service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlServiceDisable +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlServiceDisable + +Disables selected services if needed, or does nothing if already disabled. +In addition, when called for the first time, the current state is saved +so that the C can be restored to its original state when testing +is finished, see C. + + rlServiceDisable service [service...] + +=over + +=item service + +Name of the service(s) to disable. + +=back + +Returns number of services which failed disablement; thus +zero is returned when everything is OK. + +=cut + +rlServiceDisable() { + # at least one service has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlServiceDisable: You have to supply at least one service name" + return 99 + fi + + local failed=0 + + local service + for service in "$@"; do + chkconfig $service > /dev/null + local status=$? + + # if the original state hasn't been saved yet, do it now! + __INTERNAL_SERVICE_initial_state_save $service $status "true" + + # if the service is disabled, do nothing + if [ $status == 1 ]; then + rlLogDebug "rlServiceDisable: Service $service already disabled, doing nothing." + continue + fi + + # disable it + if chkconfig "$service" off; then + rlLogDebug "rlServiceDisable: Service $service disabled successfully" + else + # if disable failed, inform the user and provide info about service status + rlLogError "rlServiceDisable: Disablement service $service failed" + rlLogError "Status of the failed service:" + __INTERNAL_SERVICE status "$service" 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSocketStart +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Sockets + +Following routines implement comfortable way how to start/stop +system sockets with the possibility to restore them to their +original state after testing. + +=head3 rlSocketStart + +Make sure the given C is running. (Start it if stopped, leave +it if already running.) In addition, when called for the first time, the +current state is saved so that the C can be restored to +its original state when testing is finished, see +C. + + rlSocketStart socket [socket...] + +=over + +=item socket + +Name of the socket(s) to start. + +=back + +Returns number of sockets which failed to start/restart; thus +zero is returned when everything is OK. + +=cut + +__INTERNAL_SOCKET_NS="rlSocket" +__INTERNAL_SOCKET_STATE_SECTION="savedStates" + +__INTERNAL_SOCKET_STATE_SAVE(){ + local socketId=$1 + local socketState=$2 + + __INTERNAL_ST_PUT --namespace="$__INTERNAL_SOCKET_NS" --section="$__INTERNAL_SOCKET_STATE_SECTION" $socketId $socketState +} + +__INTERNAL_SOCKET_STATE_LOAD(){ + local socketId=$1 + + __INTERNAL_ST_GET --namespace="$__INTERNAL_SOCKET_NS" --section="$__INTERNAL_SOCKET_STATE_SECTION" $socketId +} + +__INTERNAL_SOCKET_get_handler() { + local socketName="$1" + local handler="xinetd" + + # detection whether it is xinetd service, or systemd socket + if ! rlIsRHEL "<7"; then + # need to check if socket exists, in case it does not, it is xinetd + __INTERNAL_SYSTEMCTL list-sockets --all | grep -q "${socketName}.socket" && handler="systemd" + fi + # return correct handler: + [ "$handler" == "systemd" ] && return 0 + [ "$handler" == "xinetd" ] && return 1 +} + +__INTERNAL_SOCKET_service() { + local serviceName="$1" + local serviceTask="$2" + __INTERNAL_SOCKET_get_handler ${serviceName}; local handler=$? + + if [[ "$handler" -eq "0" ]];then + ######## systemd ######### + rlLogDebug "rlSocket: Handling $serviceName socket via systemd" + case $serviceTask in + "start") + __INTERNAL_SYSTEMCTL start ${serviceName}.socket + return + ;; + "stop") + __INTERNAL_SYSTEMCTL stop ${serviceName}.socket + return + ;; + "status") + local outcome + outcome=$(__INTERNAL_SYSTEMCTL is-active ${serviceName}.socket) + local outcomeExit=$? + rlLogDebug "rlSocket: status of ${serviceName} is \"${outcome}\", exit code: ${outcomeExit}." + return $outcomeExit + ;; + esac + + else + ######## legacy (xinetd) ######## + # also needs to handle (non)existence of the socket + rlLogDebug "rlSocket: Handling $serviceName via xinetd" + case $serviceTask in + "start") + rlServiceStart xinetd && chkconfig ${serviceName} on + outcome=$? + if [[ $outcome == "0" ]]; then + return 0 + else + chkconfig ${serviceName} > /dev/null; local outcome=$? + __INTERNAL_SERVICE status xinetd 2>&1 > /dev/null; local outcomeXinetd=$? + rlLogDebug "xinetd status code: $outcomeXinetd" + rlLogDebug "socket $serviceName status: $outcome" + return 1 + fi + ;; + "stop") + chkconfig ${serviceName} off + return + ;; + "status") + chkconfig ${serviceName} > /dev/null; local outcome=$? + __INTERNAL_SERVICE status xinetd 2>&1 > /dev/null; local outcomeXinetd=$? + + if [[ "$outcome" == 0 && "$outcomeXinetd" == 0 ]]; then + rlLogDebug "rlSocket: Socket $serviceName is started" + return 0 + else + rlLogDebug "xinetd status code: $outcomeXinetd" + rlLogDebug "socket $serviceName status: $outcome" + return 3 + fi + ;; + esac + + fi +} + +__INTERNAL_SOCKET_initial_state_save() { + local socket=$1 + __INTERNAL_SOCKET_service "$socket" status + local status=$? + + # if the original state hasn't been saved yet, do it now! + + local socketId="$(echo $socket | sed 's/[^a-zA-Z0-9]//g')" + local wasRunning="$( __INTERNAL_SOCKET_STATE_LOAD $socketId)" + if [ -z "$wasRunning" ]; then + # was running + if [ $status == 0 ]; then + rlLogDebug "rlSocketStart: Original state of $socket saved (running)" + __INTERNAL_SOCKET_STATE_SAVE $socketId true + # was stopped + elif [ $status == 3 ]; then + rlLogDebug "rlSocketStart: Original state of $socket saved (stopped)" + __INTERNAL_SOCKET_STATE_SAVE $socketId false + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlSocketStart: socket $socket status returned $status" + rlLogWarning "rlSocketStart: Guessing that original state of $socket is stopped" + __INTERNAL_SOCKET_STATE_SAVE $socketId false + fi + fi +} + + +rlSocketStart() { + # at least one socket has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlSocketStart: You have to supply at least one socket name" + return 99 + fi + + local failed=0 + + local socket + for socket in "$@"; do + __INTERNAL_SOCKET_service "$socket" status + local status=$? + + # if the original state hasn't been saved yet, do it now! + __INTERNAL_SOCKET_initial_state_save $socket + + if [ $status == 0 ]; then + rlLog "rlSocketStart: Socket $socket already running, doing nothing." + continue + fi + + # finally let's start the socket! + if __INTERNAL_SOCKET_service "$socket" start; then + rlLog "rlSocketStart: Socket $socket started successfully" + else + # if socket start failed, inform the user and provide info about socket status + rlLogError "rlSocketStart: Starting socket $socket failed" + rlLogError "Status of the failed socket:" + __INTERNAL_SOCKET_service "$socket" status 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSocketStop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlSocketStop + +Make sure the given C is stopped. Stop it if it is +running and do nothing when it is already stopped. In addition, +when called for the first time, the current state is saved so that +the C can be restored to its original state when testing +is finished, see C. + + rlSocketStop socket [socket...] + +=over + +=item socket + +Name of the socket(s) to stop. + +=back + +Returns number of sockets which failed to become stopped; thus +zero is returned when everything is OK. + +=cut + +rlSocketStop() { + # at least one socket has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlSocketStop: You have to supply at least one socket name" + return 99 + fi + + local failed=0 + + local socket + for socket in "$@"; do + __INTERNAL_SOCKET_service "$socket" status + local status=$? + # if the original state hasn't been saved yet, do it now! + __INTERNAL_SOCKET_initial_state_save $socket + + # if the socket is stopped, do nothing + if [ $status != 0 ]; then + rlLogDebug "rlSocketStop: Socket $socket already stopped, doing nothing." + continue + fi + + # finally let's stop the socket! + if __INTERNAL_SOCKET_service "$socket" stop; then + rlLogDebug "rlSocketStop: Socket $socket stopped successfully" + else + # if socket stop failed, inform the user and provide info about socket status + rlLogError "rlSocketStop: Stopping socket $socket failed" + rlLogError "Status of the failed socket:" + __INTERNAL_SOCKET_service "$socket" status 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + fi + done + + return $failed +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSocketRestore +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlSocketRestore + +Restore given C into its original state (before the first +C or C was called). + +Warning !!! +Xinetd process [even though it might have been used] is NOT restored. It is +recommended to call rlServiceRestore xinetd as well. + + rlSocketRestore socket [socket...] + +=over + +=item socket + +Name of the socket(s) to restore to original state. + +=back + +Returns number of sockets which failed to get back to their +original state; thus zero is returned when everything is OK. + +=cut + +rlSocketRestore() { + # at least one socket has to be supplied + if [ $# -lt 1 ]; then + rlLogError "rlSocketRestore: You have to supply at least one socket name" + return 99 + fi + + local failed=0 + + local socket + for socket in "$@"; do + # if the original state hasn't been saved, then something's wrong + local socketId="$(echo $socket | sed 's/[^a-zA-Z0-9]//g')" + local wasRunning="$( __INTERNAL_SOCKET_STATE_LOAD $socketId )" + if [ -z "$wasRunning" ]; then + rlLogError "rlSocketRestore: Original state of $socket was not saved, nothing to do" + ((failed++)) + continue + fi + + $wasRunning && wasStopped=false || wasStopped=true + rlLogDebug "rlSocketRestore: Restoring $socket to original state ($( + $wasStopped && echo "stopped" || echo "running"))" + + # find out current state + __INTERNAL_SOCKET_service "$socket" status + local status=$? + if [ $status == 0 ]; then + isStopped=false + elif [ $status == 3 ]; then + isStopped=true + # weird exit status (warn and suppose stopped) + else + rlLogWarning "rlSocketRestore: socket $socket status returned $status" + rlLogWarning "rlSocketRestore: Guessing that current state of $socket is stopped" + isStopped=true + fi + + if [[ $isStopped == $wasStopped ]]; then + rlLogDebug "rlSocketRestore: Socket $socket is already in original state, doing nothing." + continue + fi + # we actually have to do something + local action=$($wasStopped && echo "stop" || echo "start") + if __INTERNAL_SOCKET_service "$socket" $action; then + rlLogDebug "rlSocketRestore: Socket $socket ${action}ed successfully" + else + rlLogError "rlSocketRestore: Socket $socket failed to ${action}" + rlLogError "Status of the failed socket:" + __INTERNAL_SOCKET_service "$socket" status 2>&1 | while read line; do rlLog " $line"; done + ((failed++)) + continue + fi + done + + return $failed +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSEBooleanOn +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rlSEBooleanOn() { + rlLogError "this function was dropped as its development is completely moved to the beaker library" + rlLogInfo "if you realy on this function and you really need to have it present in core beakerlib, file a RFE, please" + return 1 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSEBooleanOff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +rlSEBooleanOff() { + rlLogError "this function was dropped as its development is completely moved to the beaker library" + rlLogInfo "if you realy on this function and you really need to have it present in core beakerlib, file a RFE, please" + return 1 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlSEBooleanRestore +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +rlSEBooleanRestore() { + rlLogError "this function was dropped as its development is completely moved to the beaker library" + rlLogInfo "if you realy on this function and you really need to have it present in core beakerlib, file a RFE, please" + return 1 +} + +: <<'=cut' +=pod + +=head2 Cleanup management + +Cleanup management works with a so-called cleanup buffer, which is a temporary +representation of what should be run at cleanup time, and a final cleanup +script (executable), which is generated from this buffer and wraps it using +BeakerLib essentials (journal initialization, cleanup phase, ...). +The cleanup script must always be updated on an atomic basis (filesystem-wise) +to allow an asynchronous execution by a third party (ie. test watcher). + +The test watcher usage is mandatory for the cleanup management system to work +properly as it is the test watcher that executes the actual cleanup script. +Limited, catastrophe-avoiding mechanism is in place even when the test is not +run in test watcher, but that should be seen as a backup and such situation +is to be avoided whenever possible. + +The cleanup script shares all environment (variables, exported or not, and +functions) with the test itself - the cleanup append/prepend functions "sample" +or "snapshot" the environment at the time of their call, IOW any changes to the +test environment are synchronized to the cleanup script only upon calling +append/prepend. +When the append/prepend functions are called within a function which has local +variables, these will appear as global in the cleanup. + +While the cleanup script receives $PWD from the test, its working dir is set +to the initial test execution dir even if $PWD contains something else. It is +impossible to use relative paths inside cleanup reliably - certain parts of +the cleanup might have been added under different current directories (CWDs). +Therefore always use absolute paths in append/prepend cleanup or make sure +you never 'cd' elsewhere (ie. to a TmpDir). +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCleanupAppend +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlCleanupAppend + +Appends a string to the cleanup buffer and recreates the cleanup script. + + rlCleanupAppend string + +=over + +=back + +Returns 0 if the operation was successful, 1 otherwise. + +=cut + +rlCleanupAppend() { + if [ ! -f "$__INTERNAL_CLEANUP_BUFF" ]; then + rlLogError "rlCleanupAppend: cleanup metadata not initialized" + return 1 + elif [ $# -lt 1 ]; then + rlLogError "rlCleanupAppend: not enough arguments" + return 1 + elif [ -z "$__INTERNAL_TESTWATCHER_ACTIVE" ]; then + rlLogWarning "rlCleanupAppend: Running outside of the test watcher" + rlLogWarning "rlCleanupAppend: Check your 'run' target in the test Makefile" + rlLogWarning "rlCleanupAppend: Cleanup will be executed only if rlJournalEnd is called properly" + fi + + echo "$1" >> "$__INTERNAL_CLEANUP_BUFF" || return 1 + + __INTERNAL_rlCleanupGenFinal || return 1 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCleanupPrepend +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlCleanupPrepend + +Prepends a string to the cleanup buffer and recreates the cleanup script. + + rlCleanupPrepend string + +=over + +=back + +Returns 0 if the operation was successful, 1 otherwise. + +=cut + +rlCleanupPrepend() { + if [ ! -f "$__INTERNAL_CLEANUP_BUFF" ]; then + rlLogError "rlCleanupPrepend: cleanup metadata not initialized" + return 1 + elif [ $# -lt 1 ]; then + rlLogError "rlCleanupPrepend: not enough arguments" + return 1 + elif [ -z "$__INTERNAL_TESTWATCHER_ACTIVE" ]; then + rlLogWarning "rlCleanupPrepend: Running outside of the test watcher" + rlLogWarning "rlCleanupPrepend: Check your 'run' target in the test Makefile" + rlLogWarning "rlCleanupPrepend: Cleanup will be executed only if rlJournalEnd is called properly" + fi + + local tmpbuff="$__INTERNAL_CLEANUP_BUFF".tmp + echo "$1" > "$tmpbuff" || return 1 + cat "$__INTERNAL_CLEANUP_BUFF" >> "$tmpbuff" || return 1 + mv -f "$tmpbuff" "$__INTERNAL_CLEANUP_BUFF" || return 1 + + __INTERNAL_rlCleanupGenFinal || return 1 +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=item * + +Ales Zelinka + +=item * + +Karel Srot + +=back + +=cut diff --git a/src/journal.sh b/src/journal.sh new file mode 100644 index 0000000..03d4fad --- /dev/null +++ b/src/journal.sh @@ -0,0 +1,955 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: journal.sh - part of the BeakerLib project +# Description: Journalling functionality +# +# Author: Petr Muller +# Author: Jan Hutar +# Author: Ales Zelinka +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - journal - journalling functionality + +=head1 DESCRIPTION + +Routines for initializing the journalling features and pretty +printing journal contents. + +=head1 FUNCTIONS + +=cut + +__INTERNAL_JOURNALIST=beakerlib-journalling +__INTERNAL_TIMEFORMAT_TIME="%H:%M:%S" +__INTERNAL_TIMEFORMAT_DATE_TIME="%Y-%m-%d %H:%M:%S %Z" +__INTERNAL_TIMEFORMAT_SHORT="$__INTERNAL_TIMEFORMAT_TIME" +__INTERNAL_TIMEFORMAT_LONG="$__INTERNAL_TIMEFORMAT_DATE_TIME" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlJournalStart +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Journalling + +=head3 rlJournalStart + +Initialize the journal file. + + rlJournalStart + +Run on the very beginning of your script to initialize journalling +functionality. + +=cut + +rlJournalStart(){ + __INTERNAL_SET_TIMESTAMP + export __INTERNAL_STARTTIME="$__INTERNAL_TIMESTAMP" + export __INTERNAL_ENDTIME="" + # test-specific temporary directory for journal/metadata + if [ -n "$BEAKERLIB_DIR" ]; then + # try user-provided temporary directory first + true + elif [ -n "$TESTID" ]; then + # if available, use TESTID for the temporary directory + # - this is useful for preserving metadata through a system reboot + export BEAKERLIB_DIR="$__INTERNAL_PERSISTENT_TMP/beakerlib-$TESTID" + else + # else generate a random temporary directory + export BEAKERLIB_DIR=$(mktemp -d $__INTERNAL_PERSISTENT_TMP/beakerlib-XXXXXXX) + fi + + [ -d "$BEAKERLIB_DIR" ] || mkdir -p "$BEAKERLIB_DIR" || { + __INTERNAL_LogText "could not create BEAKERLIB_DIR $BEAKERLIB_DIR" FATAL + exit 1 + } + + # set global internal BeakerLib journal and metafile variables + export __INTERNAL_BEAKERLIB_JOURNAL="$BEAKERLIB_DIR/journal.xml" + export __INTERNAL_BEAKERLIB_METAFILE="$BEAKERLIB_DIR/journal.meta" + export __INTERNAL_BEAKERLIB_JOURNAL_TXT="$BEAKERLIB_DIR/journal.txt" + export __INTERNAL_BEAKERLIB_JOURNAL_COLORED="$BEAKERLIB_DIR/journal_colored.txt" + + # make sure the directory is ready, otherwise we cannot continue + if [ ! -d "$BEAKERLIB_DIR" ] ; then + echo "rlJournalStart: Failed to create $BEAKERLIB_DIR directory." + echo "rlJournalStart: Cannot continue, exiting..." + exit 1 + fi + + # creating queue file + touch $__INTERNAL_BEAKERLIB_METAFILE || { + __INTERNAL_LogText "could not write to BEAKERLIB_DIR $BEAKERLIB_DIR" FATAL + exit 1 + } + + # Initialization of variables holding current state of the test + export __INTERNAL_METAFILE_INDENT_LEVEL=0 + __INTERNAL_PHASE_TYPE=() + __INTERNAL_PHASE_NAME=() + export __INTERNAL_PRESISTENT_DATA="$BEAKERLIB_DIR/PersistentData" + export __INTERNAL_JOURNAL_OPEN='' + __INTERNAL_PersistentDataLoad + export __INTERNAL_PHASES_FAILED=0 + export __INTERNAL_PHASES_PASSED=0 + export __INTERNAL_PHASES_SKIPED=0 + export __INTERNAL_PHASES_WORST_RESULT='PASS' + export __INTERNAL_TEST_STATE=0 + __INTERNAL_PHASE_TXTLOG_START=() + __INTERNAL_PHASE_FAILED=() + __INTERNAL_PHASE_PASSED=() + __INTERNAL_PHASE_STARTTIME=() + __INTERNAL_PHASE_METRICS=() + export __INTERNAL_PHASE_OPEN=0 + + if [[ -z "$__INTERNAL_JOURNAL_OPEN" ]]; then + # Create Header for XML journal + __INTERNAL_CreateHeader + # Create log element for XML journal + __INTERNAL_WriteToMetafile log + fi + __INTERNAL_JOURNAL_OPEN=1 + # Increase level of indent + __INTERNAL_METAFILE_INDENT_LEVEL=1 + + # display a warning message if run in POSIX mode + if [ $POSIXFIXED == "YES" ] ; then + rlLogWarning "POSIX mode detected and switched off" + rlLogWarning "Please fix your test to have /bin/bash shebang" + fi + + # Check BEAKERLIB_JOURNAL parameter + [ -n "$BEAKERLIB_JOURNAL" ] && __INTERNAL_JournalParamCheck + + # final cleanup file (atomic updates) + export __INTERNAL_CLEANUP_FINAL="$BEAKERLIB_DIR/cleanup.sh" + # cleanup "buffer" used for append/prepend + export __INTERNAL_CLEANUP_BUFF="$BEAKERLIB_DIR/clbuff" + + if touch "$__INTERNAL_CLEANUP_FINAL" "$__INTERNAL_CLEANUP_BUFF"; then + rlLogDebug "rlJournalStart: Basic cleanup infrastructure successfully initialized" + + if [ -n "$TESTWATCHER_CLPATH" ] && \ + echo "$__INTERNAL_CLEANUP_FINAL" > "$TESTWATCHER_CLPATH"; then + rlLogDebug "rlJournalStart: Running in test watcher and setup was successful" + export __INTERNAL_TESTWATCHER_ACTIVE=true + else + rlLogDebug "rlJournalStart: Not running in test watcher or setup failed." + fi + else + rlLogError "rlJournalStart: Failed to set up cleanup infrastructure" + fi + __INTERNAL_PersistentDataSave +} + +# backward compatibility +rlStartJournal() { + rlJournalStart + rlLogWarning "rlStartJournal is obsoleted by rlJournalStart" +} + +# Check if XML journal is to be created and if so +# whether it should be xsl transformed and how. +# Sets BEAKERLIB_JOURNAL and __INTERNAL_XSLT vars. +__INTERNAL_JournalParamCheck(){ + __INTERNAL_XSLT='' + if [[ "$BEAKERLIB_JOURNAL" != "0" ]]; then + if [[ -r "$BEAKERLIB/xslt-templates/$BEAKERLIB_JOURNAL" ]]; then + __INTERNAL_XSLT="--xslt $BEAKERLIB/xslt-templates/$BEAKERLIB_JOURNAL" + elif [[ -r "$BEAKERLIB_JOURNAL" ]]; then + __INTERNAL_XSLT="--xslt $BEAKERLIB_JOURNAL" + else + rlLogError "xslt file '$BEAKERLIB_JOURNAL' is not readable" + BEAKERLIB_JOURNAL="0" + fi + else + rlLogInfo "skipping xml journal creation" + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlJournalEnd +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlJournalEnd + +Summarize the test run and upload the journal file. + + rlJournalEnd + +Run on the very end of your script to print summary of the whole test run, +generate OUTPUTFILE and include journal in Beaker logs. + +=cut + +rlJournalEnd(){ + if [ -z "$__INTERNAL_TESTWATCHER_ACTIVE" ] && [ -s "$__INTERNAL_CLEANUP_FINAL" ] && \ + [ -z "$__INTERNAL_CLEANUP_FROM_JOURNALEND" ] + then + rlLogWarning "rlJournalEnd: Not running in test watcher and rlCleanup* functions were used" + rlLogWarning "rlJournalEnd: Executing prepared cleanup" + rlLogWarning "rlJournalEnd: Please fix the test to use test watcher" + + # The executed cleanup will always run rlJournalEnd, so we need to prevent + # infinite recursion. rlJournalEnd runs the cleanup only when + # __INTERNAL_CLEANUP_FROM_JOURNALEND is not set (see above). + __INTERNAL_CLEANUP_FROM_JOURNALEND=1 "$__INTERNAL_CLEANUP_FINAL" + + # Return, because the rest of the rlJournalEnd was already run inside the cleanup + return $? + fi + + if [ -z "$BEAKERLIB_COMMAND_SUBMIT_LOG" ] + then + local BEAKERLIB_COMMAND_SUBMIT_LOG="$__INTERNAL_DEFAULT_SUBMIT_LOG" + fi + + __INTERNAL_SET_TIMESTAMP + __INTERNAL_ENDTIME=$__INTERNAL_TIMESTAMP + __INTERNAL_update_journal_txt + + if [ -n "$TESTID" ] ; then + __INTERNAL_JournalXMLCreate + $BEAKERLIB_COMMAND_SUBMIT_LOG -T $TESTID -l $__INTERNAL_BEAKERLIB_JOURNAL \ + || rlLogError "rlJournalEnd: Submit wasn't successful" + else + [[ "$BEAKERLIB_JOURNAL" == "0" ]] || rlLog "JOURNAL XML: $__INTERNAL_BEAKERLIB_JOURNAL" + rlLog "JOURNAL TXT: $__INTERNAL_BEAKERLIB_JOURNAL_TXT" + fi + + echo "#End of metafile" >> $__INTERNAL_BEAKERLIB_METAFILE + __INTERNAL_JournalXMLCreate +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# __INTERNAL_JournalXMLCreate +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +#: <<'=cut' +#=pod +# +#=head3 __INTERNAL_JournalXMLCreate +# +#Create XML version of the journal from internal structure. +# +# __INTERNAL_JournalXMLCreate +# +#=cut + +__INTERNAL_JournalXMLCreate() { + [[ "$BEAKERLIB_JOURNAL" == "0" ]] || $__INTERNAL_JOURNALIST $__INTERNAL_XSLT --metafile \ + "$__INTERNAL_BEAKERLIB_METAFILE" --journal "$__INTERNAL_BEAKERLIB_JOURNAL" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlJournalPrint +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlJournalPrint + +Print the content of the journal in pretty xml format. + + rlJournalPrint [type] + +=over + +=item type + +Can be either 'raw' or 'pretty', with the latter as a default. +Raw: xml is in raw form, no indentation etc +Pretty: xml is pretty printed, indented, with one record per line + +=back + +Example: + + + + debugging + setup + setup-2.8.9-1.fc12.noarch + 2010-02-08 15:17:47 + 2010-02-08 15:17:47 + /examples/beakerlib/Sanity/simple + Fedora release 12 (Constantine) + localhost + i686 + PURPOSE of /examples/beakerlib/Sanity/simple + Description: Minimal BeakerLib sanity test + Author: Petr Splichal <psplicha@redhat.com> + + This is a minimal sanity test for BeakerLib. It contains a single + phase with a couple of asserts. We Just check that the "setup" + package is installed and that there is a sane /etc/passwd file. + + + + PASS + PASS + PASS + + + + +=cut + +# cat generated text version +rlJournalPrint(){ + __INTERNAL_JournalXMLCreate + if [[ "$1" == "raw" ]]; then + cat $__INTERNAL_BEAKERLIB_JOURNAL + else + cat $__INTERNAL_BEAKERLIB_JOURNAL | xmllint --format - + fi +} + +# backward compatibility +rlPrintJournal() { + rlLogWarning "rlPrintJournal is obsoleted by rlJournalPrint" + rlJournalPrint +} + + +__INTERNAL_update_journal_txt() { + local textfile + local duration=$(($__INTERNAL_TIMESTAMP - $__INTERNAL_STARTTIME)) + local endtime + printf -v endtime "%($__INTERNAL_TIMEFORMAT_LONG)T %s" $__INTERNAL_TIMESTAMP "(still running)" + [[ -n "$__INTERNAL_ENDTIME" ]] && printf -v endtime "%($__INTERNAL_TIMEFORMAT_LONG)T" $__INTERNAL_ENDTIME + local sed_patterns="0,/ Test finished : /s/^( Test finished : ).*\$/\1$endtime/;0,/ Test duration : /s/^( Test duration : ).*\$/\1$duration seconds/" + for textfile in "$__INTERNAL_BEAKERLIB_JOURNAL_COLORED" "$__INTERNAL_BEAKERLIB_JOURNAL_TXT"; do + sed -r -i "$sed_patterns" "$textfile" + done + +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlJournalPrintText +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlJournalPrintText + +Print the content of the journal in pretty text format. + + rlJournalPrintText [--full-journal] + +=over + +=item --full-journal + +The options is now deprecated, has no effect and will be removed in one +of future versions. + +=back + +Example: + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: TEST PROTOCOL + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ LOG ] :: Test run ID : debugging + :: [ LOG ] :: Package : debugging + :: [ LOG ] :: Test started : 2010-02-08 14:45:57 + :: [ LOG ] :: Test finished : 2010-02-08 14:45:58 + :: [ LOG ] :: Test name : + :: [ LOG ] :: Distro: : Fedora release 12 (Constantine) + :: [ LOG ] :: Hostname : localhost + :: [ LOG ] :: Architecture : i686 + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Test description + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + PURPOSE of /examples/beakerlib/Sanity/simple + Description: Minimal BeakerLib sanity test + Author: Petr Splichal + + This is a minimal sanity test for BeakerLib. It contains a single + phase with a couple of asserts. We Just check that the "setup" + package is installed and that there is a sane /etc/passwd file. + + + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + :: [ LOG ] :: Test + :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + + :: [ PASS ] :: Checking for the presence of setup rpm + :: [ PASS ] :: File /etc/passwd should exist + :: [ PASS ] :: File '/etc/passwd' should contain 'root' + :: [ LOG ] :: Duration: 1s + :: [ LOG ] :: Assertions: 3 good, 0 bad + :: [ PASS ] :: RESULT: Test + +=cut +# call rlJournalPrint +rlJournalPrintText(){ + __INTERNAL_PersistentDataLoad + __INTERNAL_update_journal_txt + + echo -e "\n\n\n\n" + local textfile + [[ -t 1 ]] && textfile="$__INTERNAL_BEAKERLIB_JOURNAL_COLORED" || textfile="$__INTERNAL_BEAKERLIB_JOURNAL_TXT" + cat "$textfile" + + local tmp="$__INTERNAL_LogText_no_file" + __INTERNAL_LogText_no_file=1 + __INTERNAL_PrintHeadLog "${TEST}" 2>&1 + __INTERNAL_LogText "Phases: $__INTERNAL_PHASES_PASSED good, $__INTERNAL_PHASES_FAILED bad" LOG 2>&1 + __INTERNAL_LogText "RESULT: $TEST" $__INTERNAL_PHASES_WORST_RESULT 2>&1 + __INTERNAL_LogText_no_file=$tmp + + return 0 +} + +# backward compatibility +rlCreateLogFromJournal(){ + rlLogWarning "rlCreateLogFromJournal is obsoleted by rlJournalPrintText" + rlJournalPrintText +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetTestState +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlGetTestState + +Returns number of failed asserts in so far, 255 if there are more then 255 failures. +The precise number is set to ECODE variable. + + rlGetTestState +=cut + +rlGetTestState(){ + __INTERNAL_PersistentDataLoad + ECODE=$__INTERNAL_TEST_STATE + rlLogDebug "rlGetTestState: $ECODE failed assert(s) in test" + [[ $ECODE -gt 255 ]] && return 255 || return $ECODE +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetPhaseState +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlGetPhaseState + +Returns number of failed asserts in current phase so far, 255 if there are more then 255 failures. +The precise number is set to ECODE variable. + + rlGetPhaseState +=cut + +rlGetPhaseState(){ + __INTERNAL_PersistentDataLoad + ECODE=$__INTERNAL_PHASE_FAILED + rlLogDebug "rlGetPhaseState: $ECODE failed assert(s) in phase" + [[ $ECODE -gt 255 ]] && return 255 || return $ECODE +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rljAddPhase(){ + __INTERNAL_PersistentDataLoad + local MSG=${2:-"Phase of $1 type"} + local TXTLOG_START=$(wc -l $__INTERNAL_BEAKERLIB_JOURNAL_TXT) + rlLogDebug "rljAddPhase: Phase $MSG started" + __INTERNAL_WriteToMetafile phase --name "$MSG" --type "$1" >&2 + # Printing + __INTERNAL_PrintHeadLog "$MSG" + + if [[ -z "$BEAKERLIB_NESTED_PHASES" ]]; then + __INTERNAL_METAFILE_INDENT_LEVEL=2 + __INTERNAL_PHASE_TYPE=( "$1" ) + __INTERNAL_PHASE_NAME=( "$MSG" ) + __INTERNAL_PHASE_FAILED=( 0 ) + __INTERNAL_PHASE_PASSED=( 0 ) + __INTERNAL_PHASE_STARTTIME=( $__INTERNAL_TIMESTAMP ) + __INTERNAL_PHASE_TXTLOG_START=( $(wc -l $__INTERNAL_BEAKERLIB_JOURNAL_TXT) ) + __INTERNAL_PHASE_OPEN=${#__INTERNAL_PHASE_NAME[@]} + __INTERNAL_PHASE_METRICS=( "" ) + else + let __INTERNAL_METAFILE_INDENT_LEVEL++ + __INTERNAL_PHASE_TYPE=( "$1" "${__INTERNAL_PHASE_TYPE[@]}" ) + __INTERNAL_PHASE_NAME=( "$MSG" "${__INTERNAL_PHASE_NAME[@]}" ) + __INTERNAL_PHASE_FAILED=( 0 "${__INTERNAL_PHASE_FAILED[@]}" ) + __INTERNAL_PHASE_PASSED=( 0 "${__INTERNAL_PHASE_PASSED[@]}" ) + __INTERNAL_PHASE_STARTTIME=( $__INTERNAL_TIMESTAMP "${__INTERNAL_PHASE_STARTTIME[@]}" ) + __INTERNAL_PHASE_TXTLOG_START=( $TXTLOG_START "${__INTERNAL_PHASE_TXTLOG_START[@]}" ) + __INTERNAL_PHASE_OPEN=${#__INTERNAL_PHASE_NAME[@]} + __INTERNAL_PHASE_METRICS=( "" "${__INTERNAL_PHASE_METRICS[@]}" ) + fi + __INTERNAL_PersistentDataSave +} + +__INTERNAL_SET_WORST_PHASE_RESULT() { + local results='PASS WARN FAIL' + [[ "$results" =~ $(echo "$__INTERNAL_PHASES_WORST_RESULT(.*)") ]] && { + local possible_results="${BASH_REMATCH[1]}" + rlLogDebug "$FUNCNAME(): possible worse results are now $possible_results, current result is $1" + [[ "$possible_results" =~ $1 ]] && { + rlLogDebug "$FUNCNAME(): changing worst phase result from $__INTERNAL_PHASES_WORST_RESULT to $1" + __INTERNAL_PHASES_WORST_RESULT="$1" + } + } +} + +rljClosePhase(){ + __INTERNAL_PersistentDataLoad + [[ $__INTERNAL_PHASE_OPEN -eq 0 ]] && { + rlLogError "nothing to close - no open phase" + return 1 + } + local result + local logfile="$BEAKERLIB_DIR/journal.txt" + + local score=$__INTERNAL_PHASE_FAILED + # Result + if [ $score -eq 0 ]; then + result="PASS" + let __INTERNAL_PHASES_PASSED++ + else + result="$__INTERNAL_PHASE_TYPE" + let __INTERNAL_PHASES_FAILED++ + fi + + __INTERNAL_SET_WORST_PHASE_RESULT "$result" + + local name="$__INTERNAL_PHASE_NAME" + + rlLogDebug "rljClosePhase: Phase $name closed" + __INTERNAL_SET_TIMESTAMP + local endtime="$__INTERNAL_TIMESTAMP" + __INTERNAL_LogText "________________________________________________________________________________" + __INTERNAL_LogText "Duration: $((endtime - __INTERNAL_PHASE_STARTTIME))s" LOG + __INTERNAL_LogText "Assertions: $__INTERNAL_PHASE_PASSED good, $__INTERNAL_PHASE_FAILED bad" LOG + __INTERNAL_LogText "RESULT: $name" $result + __INTERNAL_LogText '' + local logfile="$(mktemp)" + tail -n +$((__INTERNAL_PHASE_TXTLOG_START+1)) $__INTERNAL_BEAKERLIB_JOURNAL_TXT > $logfile + rlReport "$(echo "$name" | sed 's/[^[:alnum:]]\+/-/g')" "$result" "$score" "$logfile" + rm -f $logfile + + # Reset of state variables + if [[ -z "$BEAKERLIB_NESTED_PHASES" ]]; then + __INTERNAL_METAFILE_INDENT_LEVEL=1 + __INTERNAL_PHASE_TYPE=() + __INTERNAL_PHASE_NAME=() + __INTERNAL_PHASE_FAILED=() + __INTERNAL_PHASE_PASSED=() + __INTERNAL_PHASE_STARTTIME=() + __INTERNAL_PHASE_TXTLOG_START=() + __INTERNAL_PHASE_METRICS=() + else + let __INTERNAL_METAFILE_INDENT_LEVEL-- + unset __INTERNAL_PHASE_TYPE[0]; __INTERNAL_PHASE_TYPE=( "${__INTERNAL_PHASE_TYPE[@]}" ) + unset __INTERNAL_PHASE_NAME[0]; __INTERNAL_PHASE_NAME=( "${__INTERNAL_PHASE_NAME[@]}" ) + [[ ${#__INTERNAL_PHASE_FAILED[@]} -gt 1 ]] && let __INTERNAL_PHASE_FAILED[1]+=__INTERNAL_PHASE_FAILED[0] + unset __INTERNAL_PHASE_FAILED[0]; __INTERNAL_PHASE_FAILED=( "${__INTERNAL_PHASE_FAILED[@]}" ) + [[ ${#__INTERNAL_PHASE_PASSED[@]} -gt 1 ]] && let __INTERNAL_PHASE_PASSED[1]+=__INTERNAL_PHASE_PASSED[0] + unset __INTERNAL_PHASE_PASSED[0]; __INTERNAL_PHASE_PASSED=( "${__INTERNAL_PHASE_PASSED[@]}" ) + unset __INTERNAL_PHASE_STARTTIME[0]; __INTERNAL_PHASE_STARTTIME=( "${__INTERNAL_PHASE_STARTTIME[@]}" ) + unset __INTERNAL_PHASE_TXTLOG_START[0]; __INTERNAL_PHASE_TXTLOG_START=( "${__INTERNAL_PHASE_TXTLOG_START[@]}" ) + unset __INTERNAL_PHASE_METRICS[0]; __INTERNAL_PHASE_METRICS=( "${__INTERNAL_PHASE_METRICS[@]}" ) + fi + __INTERNAL_PHASE_OPEN=${#__INTERNAL_PHASE_NAME[@]} + # Updating phase element + __INTERNAL_WriteToMetafile --result "$result" --score "$score" + __INTERNAL_PersistentDataSave +} + +# $1 message +# $2 result +# $3 command +rljAddTest(){ + __INTERNAL_PersistentDataLoad + if [ $__INTERNAL_PHASE_OPEN -eq 0 ]; then + rlPhaseStart "FAIL" "Asserts collected outside of a phase" + rlFail "TEST BUG: Assertion not in phase" + rljAddTest "$@" + rlPhaseEnd + else + __INTERNAL_LogText "$1" "$2" + __INTERNAL_WriteToMetafile test --message "$1" ${3:+--command "$3"} -- "$2" >&2 + if [ "$2" == "PASS" ]; then + let __INTERNAL_PHASE_PASSED++ + else + let __INTERNAL_TEST_STATE++ + let __INTERNAL_PHASE_FAILED++ + fi + fi + __INTERNAL_PersistentDataSave +} + +rljAddMetric(){ + __INTERNAL_PersistentDataLoad + local MID="$2" + local VALUE="$3" + local TOLERANCE=${4:-"0.2"} + local res=0 + if [ "$MID" == "" ] || [ "$VALUE" == "" ] + then + rlLogError "TEST BUG: Bad call of rlLogMetric" + return 1 + fi + if [[ "$__INTERNAL_PHASE_METRICS" =~ \ $MID\ ]]; then + rlLogError "$FUNCNAME: Metric name not unique!" + let res++ + else + rlLogDebug "rljAddMetric: Storing metric $MID with value $VALUE and tolerance $TOLERANCE" + __INTERNAL_PHASE_METRICS="$__INTERNAL_PHASE_METRICS $MID " + __INTERNAL_WriteToMetafile metric --type "$1" --name "$MID" \ + --value "$VALUE" --tolerance "$TOLERANCE" >&2 || let res++ + __INTERNAL_PersistentDataSave + fi + return $? +} + +rljAddMessage(){ + __INTERNAL_WriteToMetafile message --severity "$2" -- "$1" >&2 +} + +__INTERNAL_GetPackageDetails() { + rpm -q "$1" --qf "%{name}-%{version}-%{release}.%{arch} %{sourcerpm}" +} + +rljRpmLog(){ + local package_details + if package_details=( $(__INTERNAL_GetPackageDetails "$1") ); then + __INTERNAL_WriteToMetafile pkgdetails --sourcerpm "${package_details[1]}" -- "${package_details[0]}" + else + __INTERNAL_WriteToMetafile pkgnotinstalled -- "$1" + fi +} + + +# determine SUT package +__INTERNAL_DeterminePackage(){ + local package="$PACKAGE" + if [ "$PACKAGE" == "" ]; then + if [ "$TEST" == "" ]; then + package="unknown" + else + local arrPac=(${TEST//// }) + package=${arrPac[1]} + fi + fi + echo "$package" + return 0 +} + +# Creates header +__INTERNAL_CreateHeader(){ + + __INTERNAL_PrintHeadLog "TEST PROTOCOL" 2> /dev/null + + [[ -n "$TESTID" ]] && { + __INTERNAL_WriteToMetafile test_id -- "$TESTID" + __INTERNAL_LogText " Test run ID : $TESTID" 2> /dev/null + } + + # Determine package which is tested + local package=$(__INTERNAL_DeterminePackage) + __INTERNAL_WriteToMetafile package -- "$package" + __INTERNAL_LogText " Package : $package" 2> /dev/null + + # Write package details (rpm, srcrpm) into metafile + rljRpmLog "$package" + package=( $(__INTERNAL_GetPackageDetails "$package") ) && \ + __INTERNAL_LogText " Installed : ${package[0]}" 2> /dev/null + + # RPM version of beakerlib + package=( $(__INTERNAL_GetPackageDetails "beakerlib") ) && { + __INTERNAL_WriteToMetafile beakerlib_rpm -- "${package[0]}" + __INTERNAL_LogText " beakerlib RPM : ${package[0]}" 2> /dev/null + } + + # RPM version of beakerlib-redhat + package=( $(__INTERNAL_GetPackageDetails "beakerlib-redhat") ) && { + __INTERNAL_WriteToMetafile beakerlib_redhat_rpm -- "${package[0]}" + __INTERNAL_LogText " bl-redhat RPM : ${package[0]}" 2> /dev/null + } + + local test_version="${testversion:-$TESTVERSION}" + + [[ -n "$test_version" ]] && { + __INTERNAL_WriteToMetafile testversion -- "$test_version" + __INTERNAL_LogText " Test version : $test_version" 2> /dev/null + } + + package="${packagename:-$test_version}" + local test_built + [[ -n "$package" ]] && test_built=$(rpm -q --qf '%{BUILDTIME}\n' $package) && { + test_built="$(ehco "$test_built" | head -n 1 )" + printf -v test_built "%($__INTERNAL_TIMEFORMAT_LONG)T" "$test_built" + __INTERNAL_WriteToMetafile testversion -- "$test_built" + __INTERNAL_LogText " Test built : $test_built" 2> /dev/null + } + + + # Starttime and endtime + __INTERNAL_WriteToMetafile starttime + __INTERNAL_WriteToMetafile endtime + __INTERNAL_LogText " Test started : $(printf "%($__INTERNAL_TIMEFORMAT_LONG)T" $__INTERNAL_STARTTIME)" 2> /dev/null + __INTERNAL_LogText " Test finished : " 2> /dev/null + __INTERNAL_LogText " Test duration : " 2> /dev/null + + # Test name + TEST="${TEST:-unknown}" + __INTERNAL_WriteToMetafile testname -- "${TEST}" + __INTERNAL_LogText " Test name : ${TEST}" 2> /dev/null + + # OS release + local release=$(cat /etc/redhat-release) + [[ -n "$release" ]] && { + __INTERNAL_WriteToMetafile release -- "$release" + __INTERNAL_LogText " Distro : ${release}" 2> /dev/null + } + + # Hostname + local hostname="" + # Try hostname command or /etc/hostname if both fail skip it + if which hostname &> /dev/null; then + hostname=$(hostname --fqdn) + elif [[ -f "/etc/hostname" ]]; then + hostname=$(cat /etc/hostname) + fi + + [[ -n "$hostname" ]] && { + __INTERNAL_WriteToMetafile hostname -- "$hostname" + __INTERNAL_LogText " Hostname : ${hostname}" 2> /dev/null + } + + # Architecture + local arch=$(uname -i 2>/dev/null || uname -m) + [[ -n "$arch" ]] && { + __INTERNAL_WriteToMetafile arch -- "$arch" + __INTERNAL_LogText " Architecture : ${arch}" 2> /dev/null + } + + local line size + # CPU info + if [ -f "/proc/cpuinfo" ]; then + local count=0 + local type="unknown" + local cpu_regex="^model\sname.*: (.*)$" + while read line; do + if [[ "$line" =~ $cpu_regex ]]; then + type="${BASH_REMATCH[1]}" + let count++ + fi + done < "/proc/cpuinfo" + __INTERNAL_WriteToMetafile hw_cpu -- "$count x $type" + __INTERNAL_LogText " CPUs : $count x $type" 2> /dev/null + fi + + # RAM size + if [[ -f "/proc/meminfo" ]]; then + size=0 + local ram_regex="^MemTotal: *(.*) kB$" + while read line; do + if [[ "$line" =~ $ram_regex ]]; then + size=`expr ${BASH_REMATCH[1]} / 1024` + break + fi + done < "/proc/meminfo" + __INTERNAL_WriteToMetafile hw_ram -- "$size MB" + __INTERNAL_LogText " RAM size : ${size} MB" 2> /dev/null + fi + + # HDD size + size=0 + local hdd_regex="^(/[^ ]+) +([0-9]+) +[0-9]+ +[0-9]+ +[0-9]+% +[^ ]+$" + while read -r line ; do + if [[ "$line" =~ $hdd_regex ]]; then + let size+=BASH_REMATCH[2] + fi + done < <(df -k -P --local --exclude-type=tmpfs) + [[ -n "$size" ]] && { + size="$(echo "$((size*100/1024/1024))" | sed -r 's/..$/.\0/') GB" + __INTERNAL_WriteToMetafile hw_hdd -- "$size" + __INTERNAL_LogText " HDD size : ${size}" 2> /dev/null + } + + # Purpose + [[ -f 'PURPOSE' ]] && { + local purpose tmp + mapfile -t tmp < PURPOSE + printf -v purpose "%s\n" "${tmp[@]}" + __INTERNAL_WriteToMetafile purpose -- "$purpose" + __INTERNAL_PrintHeadLog "Test description" 2> /dev/null + __INTERNAL_LogText "$purpose" 2> /dev/null + } + + return 0 +} + + +__INTERNAL_SET_TIMESTAMP() { + printf -v __INTERNAL_TIMESTAMP '%(%s)T' -1 +} + + +# Encode arguments' values into base64 +# Adds --timestamp argument and indent +# writes it into metafile +# takes [element] --attribute1 value1 --attribute2 value2 .. [-- "content"] +__INTERNAL_WriteToMetafile(){ + __INTERNAL_SET_TIMESTAMP + local indent + local line="" + local lineraw='' + local ARGS=("$@") + local element='' + + [[ "${1:0:2}" != "--" ]] && { + local element="$1" + shift + } + local arg + while [[ $# -gt 0 ]]; do + case $1 in + --) + line+=" -- \"$(echo -n "$2" | base64 -w 0)\"" + printf -v lineraw "%s -- %q" "$lineraw" "$2" + shift 2 + break + ;; + --*) + line+=" $1=\"$(echo -n "$2" | base64 -w 0)\"" + printf -v lineraw "%s %s=%q" "$lineraw" "$1" "$2" + shift + ;; + *) + __INTERNAL_LogText "unexpected meta input format" + set | grep ^ARGS= + exit 124 + ;; + esac + shift + done + [[ $# -gt 0 ]] && { + __INTERNAL_LogText "unexpected meta input format" + set | grep ^ARGS= + exit 125 + } + + printf -v indent '%*s' $__INTERNAL_METAFILE_INDENT_LEVEL + + line="$indent${element:+$element }--timestamp=\"${__INTERNAL_TIMESTAMP}\"$line" + lineraw="$indent${element:+$element }--timestamp=\"${__INTERNAL_TIMESTAMP}\"$lineraw" + echo "#${lineraw:1}" >> $__INTERNAL_BEAKERLIB_METAFILE + echo "$line" >> $__INTERNAL_BEAKERLIB_METAFILE +} + +__INTERNAL_PrintHeadLog() { + __INTERNAL_LogText "\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + __INTERNAL_LogText ":: $1" + __INTERNAL_LogText "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n" +} + + +# whenever any of the persistend variable is touched, +# functions __INTERNAL_PersistentDataLoad and __INTERNAL_PersistentDataSave +# should be called before and after that respectively. + +__INTERNAL_PersistentDataSave() { + cat > "$__INTERNAL_PRESISTENT_DATA" <> $__INTERNAL_PRESISTENT_DATA +declare -p __INTERNAL_PHASE_PASSED >> $__INTERNAL_PRESISTENT_DATA +declare -p __INTERNAL_PHASE_STARTTIME >> $__INTERNAL_PRESISTENT_DATA +declare -p __INTERNAL_PHASE_TXTLOG_START >> $__INTERNAL_PRESISTENT_DATA +declare -p __INTERNAL_PHASE_METRICS >> $__INTERNAL_PRESISTENT_DATA +} + +__INTERNAL_PersistentDataLoad() { + [[ -r "$__INTERNAL_PRESISTENT_DATA" ]] && . "$__INTERNAL_PRESISTENT_DATA" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Ales Zelinka + +=item * + +Petr Splichal + +=item * + +Dalibor Pospisil + +=item * + +Jakub Heger + +=back + +=cut diff --git a/src/libraries.sh b/src/libraries.sh new file mode 100644 index 0000000..821f6ae --- /dev/null +++ b/src/libraries.sh @@ -0,0 +1,496 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: libraries.sh - part of the BeakerLib project +# Description: Functions for importing separate libraries +# +# Author: Petr Muller +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2012 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - libraries - mechanism for loading shared test code from libraries + +=head1 DESCRIPTION + +This file contains functions for bringing external code into the test +namespace. + +=head1 FUNCTIONS + +=cut + +# Extract a list of required libraries from a Makefile +# Takes a directory where the library is placed + +__INTERNAL_extractRequires(){ + local MAKEFILE="$1/Makefile" + + if [ -f "$MAKEFILE" ] + then + # 1) extract RhtsRequires lines, where RhtsRequires is not commented out + # 2) extract test(/Foo/Bar/Library/Baz) patterns + # 3) extract Bar/Baz from the patterns + # 4) make a single line of space-separated library IDs + __INTERNAL_LIBRARY_DEPS="$(grep -E '^[^#]*RhtsRequires' $MAKEFILE \ + | grep -E -o -e 'test\(/[^/)]+/[^/)]+/Library/[^/)]+\)' -e '[Ll]ibrary\([^)]*\)' \ + | sed -e 's|test(/[^/)]*/\([^/)]*\)/Library/\([^/)]*\))|\1/\2|g' -e 's|[Ll]ibrary(\(.*\))|\1|' \ + | tr '\n' ' ')" + else + __INTERNAL_LIBRARY_DEPS="" + fi + + echo $__INTERNAL_LIBRARY_DEPS +} + +# Extract a location of an original sourcing script from $0 +__INTERNAL_extractOrigin(){ + local SOURCE + + if [ ! -e "$0" ] + then + SOURCE="$( readlink -f . )" + else + SOURCE="$( readlink -f $0 )" + fi + + local DIR="$( dirname "$SOURCE" )" + while [ -h "$SOURCE" ] + do + SOURCE="$(readlink -f "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + done + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + + echo "$DIR" +} +__INTERNAL_TraverseRoot="$(__INTERNAL_extractOrigin)" + +# Traverse directories upwards and search for the matching path +__INTERNAL_rlLibraryTraverseUpwards() { + local DIRECTORY="$1" + local COMPONENT="$2" + local LIBRARY="$3" + + while [ "$DIRECTORY" != "/" ] + do + DIRECTORY="$( dirname $DIRECTORY )" + if [ -d "$DIRECTORY/$COMPONENT" ] + then + + local CANDIDATE="$DIRECTORY/$COMPONENT/Library/$LIBRARY/lib.sh" + if [ -f "$CANDIDATE" ] + then + LIBFILE="$CANDIDATE" + break + fi + + local CANDIDATE="$( echo $DIRECTORY/*/$COMPONENT/Library/$LIBRARY/lib.sh )" + if [ -f "$CANDIDATE" ] + then + LIBFILE="$CANDIDATE" + break + fi + fi + done +} + +__INTERNAL_rlLibrarySearchInRoot(){ + local COMPONENT="$1" + local LIBRARY="$2" + local BEAKERLIB_LIBRARY_PATH="${3:-/mnt/tests}" + + rlLogDebug "rlImport: Trying root: [$BEAKERLIB_LIBRARY_PATH]" + + local CANDIDATE="$BEAKERLIB_LIBRARY_PATH/$COMPONENT/Library/$LIBRARY/lib.sh" + if [ -f "$CANDIDATE" ] + then + LIBFILE="$CANDIDATE" + return + fi + + local CANDIDATE="$( echo $BEAKERLIB_LIBRARY_PATH/*/$COMPONENT/Library/$LIBRARY/lib.sh )" + if [ -f "$CANDIDATE" ] + then + LIBFILE="$CANDIDATE" + return + fi + + rlLogDebug "rlImport: Library not found in $BEAKERLIB_LIBRARY_PATH" +} + +__INTERNAL_rlLibrarySearch() { + + local COMPONENT="$1" + local LIBRARY="$2" + + rlLogDebug "rlImport: Looking if we got BEAKERLIB_LIBRARY_PATH" + + if [ -n "$BEAKERLIB_LIBRARY_PATH" ] + then + rlLogDebug "rlImport: BEAKERLIB_LIBRARY_PATH is set: trying to search in it" + + __INTERNAL_rlLibrarySearchInRoot "$COMPONENT" "$LIBRARY" "$BEAKERLIB_LIBRARY_PATH" + if [ -n "$LIBFILE" ] + then + local VERSION="$(__INTERNAL_extractLibraryVersion "$LIBFILE" "$COMPONENT/$LIBRARY")" + VERSION=${VERSION:+", version '$VERSION'"} + rlLogInfo "rlImport: Found '$COMPONENT/$LIBRARY'$VERSION in BEAKERLIB_LIBRARY_PATH" + return + fi + else + rlLogDebug "rlImport: No BEAKERLIB_LIBRARY_PATH set: trying default" + fi + + __INTERNAL_rlLibrarySearchInRoot "$COMPONENT" "$LIBRARY" + if [ -n "$LIBFILE" ] + then + local VERSION="$(__INTERNAL_extractLibraryVersion "$LIBFILE" "$COMPONENT/$LIBRARY")" + VERSION=${VERSION:+", version '$VERSION'"} + rlLogInfo "rlImport: Found '$COMPONENT/$LIBRARY'$VERSION in /mnt/tests" + return + fi + + __INTERNAL_rlLibrarySearchInRoot "$COMPONENT" "$LIBRARY" "/usr/share/beakerlib-libraries" + if [ -n "$LIBFILE" ] + then + local VERSION="$(__INTERNAL_extractLibraryVersion "$LIBFILE" "$COMPONENT/$LIBRARY")" + VERSION=${VERSION:+", version '$VERSION'"} + rlLogInfo "rlImport: Found '$COMPONENT/$LIBRARY'$VERSION in /usr/share/beakerlib-libraries" + return + fi + + if [ -n "$__INTERNAL_TraverseRoot" ] + then + rlLogDebug "rlImport: Trying to find the library in directories above test" + rlLogDebug "rlImport: Starting search at: $__INTERNAL_TraverseRoot" + __INTERNAL_rlLibraryTraverseUpwards "$__INTERNAL_TraverseRoot" "$COMPONENT" "$LIBRARY" + + if [ -n "$LIBFILE" ] + then + local VERSION="$(__INTERNAL_extractLibraryVersion "$LIBFILE" "$COMPONENT/$LIBRARY")" + VERSION=${VERSION:+", version '$VERSION'"} + rlLogInfo "rlImport: Found '$COMPONENT/$LIBRARY'$VERSION during upwards traversal" + return + fi + fi +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlImport +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlImport + +Imports code provided by one or more libraries into the test namespace. +The library search mechanism is based on Beaker test hierarchy system, i.e.: + +/component/type/test-name/test-file + +When test-file calls rlImport with 'foo/bar' parameter, the directory path +is traversed upwards, and a check for presence of the test /foo/Library/bar/ +will be performed. This means this function needs to be called from +the test hierarchy, not e.g. the /tmp directory. + +Once library is found, it is sourced and a verifier function is called. +The verifier function is cunstructed by composing the library prefix and +LibraryLoaded. Library prefix can be defined in the library itself. +If the verifier passes the library is ready to use. Also variable +BPREFIXELibraryDir> is created and it points to the library folder. + +Usage: + + rlImport --all + rlImport LIBRARY [LIBRARY2...] + +=over + +=item --all + +Read Makefile in current/original directory, pick library requirements up and +import them all. + +=item LIBRARY + +Must have 'component/library' format. Identifies the library to import. + +=back + +Returns 0 if the import of all libraries was successful. Returns non-zero +if one or more library failed to import. + +=cut + +__INTERNAL_first(){ + echo $1 +} + +__INTERNAL_tail(){ + shift + echo $* +} + +__INTERNAL_envdebugget() { + local tmp="$(set | sed -r '/^.*\S+ \(\).$/,$d;/^(_|BASH_.*|FUNCNAME|LINENO|PWD|__INTERNAL_LIBRARY_IMPORTS_.*|__INTERNAL_envdebug.*)=/d')" + if [[ -z "$1" ]]; then + __INTERNAL_envdebugvariables="$tmp" + __INTERNAL_envdebugfunctions="$(declare -f)" + else + echo "$tmp" + fi +} + +__INTERNAL_envdebugdiff() { + rlLogDebug "rlImport: library $1 changes following environment; changed functions are marked with asterisk (*)" + diff -U0 <(echo "$__INTERNAL_envdebugvariables") <(__INTERNAL_envdebugget 1) | tail -n +3 | grep -E -v '^@@' + local line fn print='' print2 LF=" +" + while IFS= read line; do + [[ "$line" =~ ^(.)([^[:space:]]+)[[:space:]]\(\) ]] && { + [[ -n "$print" ]] && { + echo "$fn" + print='' + } + print2='' + local tmp="${BASH_REMATCH[1]}" + [[ "$tmp" == " " ]] && { + print2=1 + tmp='*' + } + fn="$tmp${BASH_REMATCH[2]}()" + continue + } + [[ "${line:0:1}" != " " ]] && print=1 + [[ "$DEBUG" =~ ^[0-9]+$ ]] && [[ -n "$print2" && $DEBUG -ge 2 || $DEBUG -ge 3 ]] && fn="$fn$LF$line" + done < <(diff -U100000 <(echo "$__INTERNAL_envdebugfunctions") <(declare -f) | tail -n +3 | grep -E -v '^@@'; echo " _ ()") + unset __INTERNAL_envdebugfunctions __INTERNAL_envdebugvariables +} + +__INTERNAL_extractLibraryVersion() { + local LIBFILE=$1 + local LIBNAME=$2 + local VERSION="" + local RESULT="" + + # Search in lib.sh + VERSION="${VERSION:+"$VERSION, "}$( grep -E '^#\s*library-version = \S*' $LIBFILE | sed 's|.*library-version = \(\S*\).*|\1|')" + + # Search lib in rpms and get version + if RESULT=( $(rpm -q --queryformat "%{NAME} %{VERSION}-%{RELEASE}\n" --whatprovides "library($LIBNAME)") ); then + # Found library-version, set $VERSION + while [[ -n "$RESULT" ]]; do + rljRpmLog $RESULT + RESULT=( "${RESULT[@]:1}" ) + VERSION="${VERSION:+"$VERSION, "}$RESULT" + RESULT=( "${RESULT[@]:1}" ) + done + fi + + echo "$VERSION" + return 0 +} #end __INTERNAL_extractLibraryVersion + +rlImport() { + local RESULT=0 + + if [ -z "$1" ] + then + rlLogError "rlImport: At least one argument needs to be provided" + return 1 + fi + + local WORKLIST="$*" + if [ "$1" == '--all' ]; then + rlLogDebug "Try to import all libraries specified in Makefile" + WORKLIST=$(__INTERNAL_extractRequires "${__INTERNAL_TraverseRoot}") + + if [ -z "$WORKLIST" ] + then + rlLogInfo "rlImport: No libraries found in Makefile" + return 0 + fi + fi + + local PROCESSING="x" + local LIBS_TO_LOAD='' + + # Process all arguments + while true + do + rlLogDebug "rlImport: WORKLIST [$WORKLIST]" + # Pick one library from the worklist + PROCESSING="$(__INTERNAL_first $WORKLIST)" + WORKLIST=$(__INTERNAL_tail $WORKLIST) + + if [ -z "$PROCESSING" ] + then + break + fi + + LIBS_TO_LOAD="$PROCESSING $LIBS_TO_LOAD" + + # Extract two identifiers from an 'component/library' argument + local COMPONENT=$( echo $PROCESSING | cut -d '/' -f 1 ) + local LIBRARY=$( echo $PROCESSING | cut -d '/' -f 2 ) + + local COMPONENT_hash=$( rlHash --algorithm hex "$COMPONENT" ) + local LIBRARY_hash=$( rlHash --algorithm hex "$LIBRARY" ) + local LOCATIONS_varname="__INTERNAL_LIBRARY_LOCATIONS_C${COMPONENT_hash}_L${LIBRARY_hash}" + local IMPORTS_varname="__INTERNAL_LIBRARY_IMPORTS_C${COMPONENT_hash}_L${LIBRARY_hash}" + + # If the lib was already processed, do nothing + if [ -n "${!IMPORTS_varname}" ] + then + continue + fi + + if [ -z "$COMPONENT" ] || [ -z "$LIBRARY" ] || [ "$COMPONENT/$LIBRARY" != "$PROCESSING" ] + then + rlLogError "rlImport: Malformed argument [$PROCESSING]" + eval $IMPORTS_varname="FAIL" + RESULT=1 + continue; + fi + + rlLogDebug "rlImport: Searching for library $COMPONENT/$LIBRARY" + + # LIBFILE is set inside __INTERNAL_rlLibrarySearch if a suitable path is found + local LIBFILE="" + __INTERNAL_rlLibrarySearch $COMPONENT $LIBRARY + + if [ -z "$LIBFILE" ] + then + rlLogError "rlImport: Could not find library $PROCESSING" + eval $IMPORTS_varname="FAIL" + RESULT=1 + continue; + else + rlLogInfo "rlImport: Will try to import $COMPONENT/$LIBRARY from $LIBFILE" + fi + + rlLogDebug "rlImport: Collecting dependencies for library $COMPONENT/$LIBRARY" + local LIBDIR="$(dirname $LIBFILE)" + if ! eval $LOCATIONS_varname='$LIBDIR' + then + rlLogError "rlImport: Error processing: $LOCATIONS_varname='$LIBDIR'" + RESULT=1 + continue + fi + WORKLIST="$WORKLIST $(__INTERNAL_extractRequires $LIBDIR )" + if ! eval $IMPORTS_varname="LOC" + then + rlLogError "rlImport: Error processing: $IMPORTS_varname='LOC'" + RESULT=1 + continue + fi + done + + rlLogDebug "rlImport: LIBS_TO_LOAD='$LIBS_TO_LOAD'" + local library + for library in $LIBS_TO_LOAD + do + local COMPONENT=$( echo $library | cut -d '/' -f 1 ) + local LIBRARY=$( echo $library | cut -d '/' -f 2 ) + local COMPONENT_hash=$( rlHash --algorithm hex "$COMPONENT" ) + local LIBRARY_hash=$( rlHash --algorithm hex "$LIBRARY" ) + local LOCATIONS_varname="__INTERNAL_LIBRARY_LOCATIONS_C${COMPONENT_hash}_L${LIBRARY_hash}" + local IMPORTS_varname="__INTERNAL_LIBRARY_IMPORTS_C${COMPONENT_hash}_L${LIBRARY_hash}" + [ "${!IMPORTS_varname}" != "LOC" ] && { + rlLogDebug "rlImport: skipping $library as it is already processed" + continue + } + local LIBFILE="${!LOCATIONS_varname}/lib.sh" + + # Try to extract a prefix comment from the file found + # Prefix comment looks like this: + # library-prefix = wee + local PREFIX="$( grep -E "library-prefix = [a-zA-Z_][a-zA-Z0-9_]*.*" $LIBFILE | sed 's|.*library-prefix = \([a-zA-Z_][a-zA-Z0-9_]*\).*|\1|')" + if [ -z "$PREFIX" ] + then + rlLogError "rlImport: Could not extract prefix from library $library" + RESULT=1 + continue; + fi + + # Construct the validating function + # Its supposed to be called 'prefixLibraryLoaded' + local VERIFIER="${PREFIX}LibraryLoaded" + rlLogDebug "rlImport: Constructed verifier function: $VERIFIER" + + local SOURCEDEBUG='' + # Try to source the library + bash -n $LIBFILE && { + [[ -n "$DEBUG" ]] && { + SOURCEDEBUG=1 + __INTERNAL_envdebugget + } + . $LIBFILE + } + + # Call the validation callback of the function + if ! eval $VERIFIER + then + rlLogError "rlImport: Import of library $library was not successful (callback failed)" + RESULT=1 + eval $IMPORTS_varname='FAIL' + [[ -n "$SOURCEDEBUG" ]] && { + __INTERNAL_envdebugdiff "$library" + } + continue; + fi + eval ${PREFIX}LibraryDir="$(dirname $LIBFILE)" + eval $IMPORTS_varname='PASS' + [[ -n "$SOURCEDEBUG" ]] && { + __INTERNAL_envdebugdiff "$library" + } + done + + return $RESULT +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Dalibor Pospisil + +=back + +=cut diff --git a/src/logging.sh b/src/logging.sh new file mode 100644 index 0000000..680f14c --- /dev/null +++ b/src/logging.sh @@ -0,0 +1,1118 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: logging.sh - part of the BeakerLib project +# Description: Phases, logging & metrics related stuff +# +# Author: Chris Ward +# Author: Ondrej Hudlicky +# Author: Petr Muller +# Author: Jan Hutar +# Author: Ales Zelinka +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +export __INTERNAL_DEFAULT_SUBMIT_LOG=__INTERNAL_FileSubmit + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - logging - phase support, logging functions and metrics + +=head1 DESCRIPTION + +Routines for creating various types of logs inside BeakerLib tests. +Implements also phase support with automatic assert evaluation. + +=head1 FUNCTIONS + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__INTERNAL_PrintText() { + local tmp="$__INTERNAL_LogText_no_file" + __INTERNAL_LogText_no_file=1 + __INTERNAL_LogText "$@" + __INTERNAL_LogText_no_file=$tmp +} + +__INTERNAL_LogText() { + local MESSAGE="$1" + local MESSAGE_COLORED="${MESSAGE}" + local prio="$2" + local LOGFILE=${3:-$OUTPUTFILE} + local res=0 + local COLOR='' UNCOLOR='' + if [[ -t 2 ]]; then + UNCOLOR="$__INTERNAL_color_reset" + case ${prio^^} in + DEBUG*) + COLOR="$__INTERNAL_color_purple" + ;; + PASS) + COLOR="$__INTERNAL_color_green" + ;; + FAIL|FATAL) + COLOR="$__INTERNAL_color_light_red" + ;; + LOG) + COLOR="$__INTERNAL_color_cyan" + ;; + LOG|INFO|BEGIN) + COLOR="$__INTERNAL_color_blue" + ;; + WARN*|SKIP*) + COLOR="$__INTERNAL_color_yellow" + ;; + esac + fi + [[ -n "$prio" ]] && { + local left=$(( (10+${#prio})/2 )) + local prefix prefix_colored timestamp + __INTERNAL_SET_TIMESTAMP + printf -v timestamp "%($__INTERNAL_TIMEFORMAT_SHORT)T" "$__INTERNAL_TIMESTAMP" + printf -v prefix_colored ":: [ %s ] :: [%s%*s%*s%s] ::" "$timestamp" "$COLOR" "$left" "${prio}" "$(( 10-$left ))" '' "$UNCOLOR" + printf -v prefix ":: [ %s ] :: [%*s%*s] ::" "$timestamp" "$left" "${prio}" "$(( 10-$left ))" + MESSAGE="$prefix $MESSAGE" + MESSAGE_COLORED="$prefix_colored $MESSAGE_COLORED" + } + if [[ -z "$__INTERNAL_LogText_no_file" ]]; then + if [[ -n "$LOGFILE" ]]; then + echo -e "${MESSAGE}" >> $LOGFILE || let res++ + fi + echo -e "${MESSAGE}" >> "$__INTERNAL_BEAKERLIB_JOURNAL_TXT" || let res++ + echo -e "${MESSAGE_COLORED}" >> "$__INTERNAL_BEAKERLIB_JOURNAL_COLORED" || let res++ + fi + echo -e "${MESSAGE_COLORED}" >&2 || let res++ + return $res +} + +__INTERNAL_FileSubmit() { + local FILENAME="$4" + local STORENAME="$__INTERNAL_PERSISTENT_TMP/BEAKERLIB_${TESTID}_STORED_$(basename $FILENAME)" + if [ -z "$TESTID" ] + then + STORENAME="$__INTERNAL_PERSISTENT_TMP/BEAKERLIB_STORED_$(basename $FILENAME)" + fi + + rlLog "File '$FILENAME' stored here: $STORENAME" + cp -f "$FILENAME" "$STORENAME" + return $? +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlLog* +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Logging + +=head3 rlLog + +=head3 rlLogDebug + +=head3 rlLogInfo + +=head3 rlLogWarning + +=head3 rlLogError + +=head3 rlLogFatal + +Create a time/priority-labelled message in the log. There is a bunch of aliases +which can create messages formated as DEBUG/INFO/WARNING/ERROR or FATAL (but you +would probably want to use rlDie instead of the last one). + + rlLog message [logfile] [priority] [label] + +=over + +=item message + +Message you want to show (use quotes when invoking). + +=item logfile + +Log file. If not supplied, OUTPUTFILE is assumed. + +=item priority + +Priority of the log. + +=item label + +Print this text instead of time in log label. + +=back + +=cut + +__INTERNAL_color_set() { + local T="$TERM" + [[ -t 1 ]] || T="" + [[ -t 2 ]] || T="" + case $T in + xterm*|screen|linux) + __INTERNAL_color_black="\e[0;30m" + __INTERNAL_color_dark_gray="\e[1;30m" + __INTERNAL_color_red="\e[0;31m" + __INTERNAL_color_light_red="\e[1;31m" + __INTERNAL_color_green="\e[0;32m" + __INTERNAL_color_light_green="\e[1;32m" + __INTERNAL_color_yellow="\e[0;33m" + __INTERNAL_color_light_yellow="\e[1;33m" + __INTERNAL_color_blue="\e[0;34m" + __INTERNAL_color_light_blue="\e[1;34m" + __INTERNAL_color_purple="\e[0;35m" + __INTERNAL_color_light_purple="\e[1;35m" + __INTERNAL_color_cyan="\e[0;36m" + __INTERNAL_color_light_cyan="\e[1;36m" + __INTERNAL_color_light_gray="\e[0;37m" + __INTERNAL_color_white="\e[1;37m" + __INTERNAL_color_reset="\e[00m" + ;; + * ) + __INTERNAL_color_black="" + __INTERNAL_color_dark_gray="" + __INTERNAL_color_red="" + __INTERNAL_color_light_red="" + __INTERNAL_color_green="" + __INTERNAL_color_light_green="" + __INTERNAL_color_brown="" + __INTERNAL_color_yellow="" + __INTERNAL_color_blue="" + __INTERNAL_color_light_blue="" + __INTERNAL_color_purple="" + __INTERNAL_color_light_purple="" + __INTERNAL_color_cyan="" + __INTERNAL_color_light_cyan="" + __INTERNAL_color_light_gray="" + __INTERNAL_color_white="" + __INTERNAL_color_reset="" + ;; + esac +} +__INTERNAL_color_set + +__INTERNAL_CenterText() { + local text="$1" + local left=$(( ($2+${#text})/2 )) + printf "%*s%*s" $left "${text}" $(( $2-$left )) +}; # end of __INTERNAL_CenterText + + +rlLog() { + local message="$1" + local logfile="$2" + local prio="${3:-LOG}" + __INTERNAL_LogText "$message" "$prio" "$logfile" + rljAddMessage "$message" "$prio" +} + +LOG_LEVEL=${LOG_LEVEL:-""} +DEBUG=${DEBUG:-""} + +rlLogDebug() { + if [ "$DEBUG" == 'true' -o "$DEBUG" == '1' -o "$LOG_LEVEL" == "DEBUG" ]; then + rlLog "$1" "$2" "DEBUG" && rljAddMessage "$1" "DEBUG" + fi +} +rlLogInfo() { rlLog "$1" "$2" "INFO"; } +rlLogWarning() { rlLog "$1" "$2" "WARNING"; } +rlLogError() { rlLog "$1" "$2" "ERROR"; } +rlLogFatal() { rlLog "$1" "$2" "FATAL"; } + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlDie +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlDie + +Create a time-labelled message in the log, report test result, +upload logs, close unfinished phase and terminate the test. + + rlDie message [file...] + +=over + +=item message + +Message you want to show (use quotes when invoking) - this +option is mandatory. + +=item file + +Files (logs) you want to upload as well. C +will be used for it. Files which are not readable will be +excluded before calling C, so it is safe to +call even with possibly not existent logs and it will +succeed. + +=back + +=cut + +rlDie() { + # handle mandatory comment + local rlMSG="$1" + shift + # handle optional list of logs + if [ -n "$*" ]; then + local logs='' + + local log + for log in "$@"; do + [ -r "$log" ] && logs="$logs $log" + done + + [ -n "$logs" ] && rlBundleLogs rlDieLogsBundling $logs + fi + # do the work + rlLogFatal "$rlMSG" + rlAssert0 "$rlMSG" 1 + rlPhaseEnd + exit 0 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlHeadLog +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# obsoleted by phases +# : <<=cut +# =pod +# +# =head2 rlHeadLog +# +# Creates a header in the supplied log +# +# * parameter 1: message you want to show (use quotes when invoking) +# * optional parameter 2: log file. If not supplied, OUTPUTFILE is assumed +# =cut + +rlHeadLog() { + local text="$1" + local logfile=${2:-""} + __INTERNAL_LogText "\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" "$logfile" + rlLog "$text" "$logfile" + __INTERNAL_LogText "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n" "$logfile" + rlLogWarning "rlHeadLog is obsoleted, use rlPhase* instead" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlBundleLogs +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlBundleLogs + +Create a tarball of files (e.g. logs) and attach them to the test result. + + rlBundleLogs package file [file...] + +=over + +=item package + +Name of the package. Will be used as a part of the tar-ball name. + +=item file + +File(s) to be packed and submitted. + +=back + +Returns result of submiting the tarball. + +=cut + +rlBundleLogs(){ + local BASENAME="$1" + local LOGDIR="/tmp/$BASENAME" # no-reboot + + if [ -n "$JOBID" ] + then + LOGDIR="$LOGDIR-$JOBID" + fi + + if [ -n "$RECIPEID" ] + then + LOGDIR="$LOGDIR-$RECIPEID" + fi + + if [ -n "$TESTID" ] + then + LOGDIR="$LOGDIR-$TESTID" + fi + + rlLog "Bundling logs" + + rlLogDebug "rlBundleLogs: Creating directory for logs: $LOGDIR" + mkdir -p "$LOGDIR" + + local i + for i in "${@:2}"; do + local i_new="$( echo $i | sed 's|[/ ]|_|g' )" + while [ -e "$LOGDIR/$i_new" ]; do + i_new="${i_new}_next" + done + rlLogInfo "rlBundleLogs: Adding '$i' as '$i_new'" + cp -r "$i" "$LOGDIR/$i_new" + [ $? -eq 0 ] || rlLogError "rlBundleLogs: '$i' can't be packed" + done + + local TARBALL="$LOGDIR.tar.gz" + tar zcf "$TARBALL" "$LOGDIR" + if [ ! $? -eq 0 ]; then + rlLogError "rlBundleLogs: Packing was not successful" + return 1 + fi + + rlFileSubmit "$TARBALL" + SUBMITCODE=$? + + if [ ! $SUBMITCODE -eq 0 ]; then + rlLogError "rlBundleLog: Submit wasn't successful" + fi + rlLogDebug "rlBundleLogs: Removing tmp: $TARBALL" + rm -rf $TARBALL + rlLogDebug "rlBundleLogs: Removing tmp: $LOGDIR" + rm -rf $LOGDIR + + return $SUBMITCODE +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlFileSubmit +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlFileSubmit + +Resolves absolute path to the file, replaces / for - and uploads this renamed +file using rhts-submit-log. +It also allows you to specify your custom name for the uploaded file. + + rlFileSubmit [-s sep] path_to_file [required_name] + +=over + +=item -s sep + +Sets separator (i.e. the replacement of the /) to sep. + +=item path_to_file + +Either absolute or relative path to file. Relative path is converted +to absolute. + +=item required_name + +Default behavior renames file to full_path_to_file with / replaced for -, +if this does not suit your needs, you can specify the name using this +option. + +Examples: + +rlFileSubmit logfile.txt -> logfile.txt +cd /etc; rlFileSubmit ./passwd -> etc-passwd +rlFileSubmit /etc/passwd -> etc-passwd +rlFileSubmit /etc/passwd my-top-secret_file -> my-top-secret-file +rlFileSubmit -s '_' /etc/passwd -> etc_passwd + +=back + +=cut + +rlFileSubmit() { + GETOPT=$(getopt -q -o s: -- "$@") + eval set -- "$GETOPT" + + SEPARATOR='-' + while true ; do + case "$1" in + -s) + SEPARATOR=$2; + shift 2 + ;; + --) shift; break;; + *) shift;; + esac + done + + local RETVAL=255 + local FILE="$1" + local ALIAS + local TMPDIR="$(mktemp -d)" # no-reboot + if [ -f "$FILE" ]; then + if [ -n "$2" ]; then + ALIAS="$2" + else + if echo "$FILE" | egrep -q "^\.(\.)?/"; then + # ^ if the path is specified as relative ~ begins with ./ or ../ + local POM=$(dirname "$FILE") + ALIAS=$(cd "$POM"; pwd) + ALIAS="$ALIAS/$(basename $FILE)" + else + ALIAS=$1 + fi + ALIAS=$(echo $ALIAS | tr '/' "$SEPARATOR" | sed "s/^${SEPARATOR}*//") + fi + rlLogInfo "Sending $FILE as $ALIAS" + ln -s "$(readlink -f $FILE)" "$TMPDIR/$ALIAS" + + if [ -z "$BEAKERLIB_COMMAND_SUBMIT_LOG" ] + then + BEAKERLIB_COMMAND_SUBMIT_LOG="$__INTERNAL_DEFAULT_SUBMIT_LOG" + fi + + $BEAKERLIB_COMMAND_SUBMIT_LOG -T "$TESTID" -l "$TMPDIR/$ALIAS" + RETVAL=$? + fi + rm -rf $TMPDIR + return $RETVAL +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlShowPkgVersion +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Info + +=head3 rlShowPackageVersion + +Shows a message about version of packages. + + rlShowPackageVersion package [package...] + +=over + +=item package + +Name of a package(s) you want to log. + +=back + +=cut + +rlShowPackageVersion() +{ + local score=0 + if [ $# -eq 0 ]; then + rlLogWarning "rlShowPackageVersion: Too few options" + return 1 + fi + + local pkg + for pkg in "$@"; do + if rpm -q $pkg &> /dev/null; then + IFS=$'\n' + local line + for line in $(rpm -q $pkg --queryformat "$pkg RPM version: %{version}-%{release}.%{arch}\n") + do + rlLog $line + done + unset IFS + else + rlLogWarning "rlShowPackageVersion: Unable to locate package $pkg" + let score+=1 + fi + done + [ $score -eq 0 ] && return 0 || return 1 +} + +# backward compatibility +rlShowPkgVersion() { + rlLogWarning "rlShowPkgVersion is obsoleted by rlShowPackageVersion" + rlShowPackageVersion "$@"; +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetArch +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlGetArch + +This function is deprecated. Use rlGetPrimaryArch or rlGetSecondaryArch +instead, or use uname. This function will be only kept for compatibility. + +Return base arch for the current system (good when you need +base arch on a multilib system). + + rlGetArch + +On an i686 system you will get i386, on a ppc64 you will get ppc. + +=cut + + +rlGetArch() { + local archi=$( uname -i 2>/dev/null || uname -m ) + case "$archi" in + i486 | i586 | i686) + archi='i386' + ;; + ppc64) + archi='ppc' + ;; + '') + rlLogWarning "rlGetArch: Do not know what the arch is ('$(uname -a)'), guessing 'i386'" + archi='i386' + ;; + esac + rlLogWarning "rlGetArch: This function is deprecated" + rlLogWarning "rlGetArch: Update test to use rlGetPrimaryArch/rlGetSecondaryArch" + rlLogDebug "rlGetArch: This is architecture '$archi'" + echo "$archi" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetPrimaryArch +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlGetPrimaryArch + +Return primary arch for the current system (good when you need +base arch on a multilib system). + + rlGetPrimaryArch + +=cut + + +rlGetPrimaryArch() { + local res=0 + local archi=$( uname -m ) + local rhelv=$( rlGetDistroRelease ) + + local retval=$archi + + case "$archi" in + i386 | i486 | i586 | i686) + case "$rhelv" in + 4 | 5) + retval='i386' + ;; + 6) + retval='i686' + ;; + 7) + retval='' + ;; + *) + retval='' + ;; + esac + ;; + ppc64) + case "$rhelv" in + 4 | 5) + retval='ppc' + ;; + *) + retval='ppc64' + ;; + esac + ;; + x86_64) + retval='x86_64' + ;; + s390x) + retval='s390x' + ;; + s390) + case "$rhelv" in + 4) + retval='s390' + ;; + *) + retval='' + ;; + esac + ;; + ia64) + case "$rhelv" in + 4 | 5) + retval='ia64' + ;; + *) + retval='' + ;; + esac + ;; + aarch64) + retval='aarch64' + ;; + ppc64le) + retval='ppc64le' + ;; + *) + rlLogError "rlGetPrimaryArch: Do not know what the arch is ('$(uname -a)')." + retval='' + res=1 + ;; + esac + + if ! rlIsRHEL + then + rlLogInfo "rlGetPrimaryArch: Concept of primary and secondary architectures is defined on RHEL only" + fi + + rlLogDebug "rlGetPrimaryArch: The primary architecture is '$retval'" + echo "$retval" + return $res +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetSecondaryArch +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlGetSecondaryArch + +Return base arch for the current system (good when you need +base arch on a multilib system). + + rlGetSecondaryArch + +=cut + + +rlGetSecondaryArch() { + local res=0 + local archi=$( uname -m ) + local rhelv=$( rlGetDistroRelease ) + + local retval=$archi + + case "$archi" in + i386 | i486 | i586 | i686) + retval='' + ;; + ppc64) + case "$rhelv" in + 4 | 5) + retval='ppc64' + ;; + *) + retval='ppc' + ;; + esac + ;; + x86_64) + case "$rhelv" in + 4 | 5) + retval='i386' + ;; + *) + retval='i686' + ;; + esac + ;; + s390x) + retval='s390' + ;; + s390) + retval='' + ;; + ia64) + case "$rhelv" in + 4 | 5) + retval='i386' + ;; + *) + retval='' + ;; + esac + ;; + aarch64) + retval='' + ;; + ppc64le) + retval='' + ;; + *) + rlLogError "rlGetSecondaryArch: Do not know what the arch is ('$(uname -a)')." + retval='' + res=1 + ;; + esac + + if ! rlIsRHEL; then + rlLogError "rlGetSecondaryArch: Concept of primary and secondary architectures is defined on RHEL only" + retval='' + res=2 + fi + + rlLogDebug "rlGetSecondaryArch: The secondary architecture is '$retval'" + echo "$retval" + return $res +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetDistroRelease, rlGetDistroVariant +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlGetDistroRelease + +=head3 rlGetDistroVariant + +Return release or variant of the distribution on the system. + + rlGetDistroRelease + rlGetDistroVariant + +For example on the RHEL-4-AS you will get release 4 and variant AS, +on the RHEL-5-Client you will get release 5 and variant Client. + +=cut + +__rlGetDistroVersion() { + local version=0 + if rpm -q redhat-release &>/dev/null; then + version=$( rpm -q --qf="%{VERSION}" redhat-release ) + elif rpm -q fedora-release &>/dev/null; then + version=$( rpm -q --qf="%{VERSION}" fedora-release ) + elif rpm -q centos-release &>/dev/null; then + version=$( rpm -q --qf="%{VERSION}" centos-release ) + elif rpm -q --whatprovides redhat-release &>/dev/null; then + version=$( rpm -q --qf="%{VERSION}" --whatprovides redhat-release ) + else + version="unknown" + fi + rlLogDebug "__rlGetDistroVersion: This is distribution version '$version'" + echo "$version" +} +rlGetDistroRelease() { + __rlGetDistroVersion | sed "s/^\([0-9.]\+\)[^0-9.]\+.*$/\1/" | sed "s/6\.9[0-9]/7/" | cut -d '.' -f 1 +} +rlGetDistroVariant() { + VARIANT="$(__rlGetDistroVersion | sed "s/^[0-9.]\+\(.*\)$/\1/")" + if [ -z "$VARIANT" ]; then + rpm -q --qf="%{NAME}" --whatprovides redhat-release | cut -c 16- | sed 's/.*/\u&/' + else + echo $VARIANT + fi +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlShowRunningKernel +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlShowRunningKernel + +Log a message with version of the currently running kernel. + + rlShowRunningKernel + +=cut + +rlShowRunningKernel() { + rlLog "Kernel version: $(uname -r)" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPhaseStart +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Phases + +=head3 rlPhaseStart + +Starts a phase of a specific type. The final phase result is based +on all asserts included in the phase. Do not forget to end phase +with C when you are done. + + rlPhaseStart type [name] + +=over + +=item type + +Type of the phase, one of the following: + +=over + +=item FAIL + +When assert fails here, phase will report a FAIL. + +=item WARN + +When assert fails here, phase will report a WARN. + +=back + +=item name + +Optional name of the phase (if not provided, one will be generated). + +=back + +If all asserts included in the phase pass, phase reports PASS. + +=cut + +rlPhaseStart() { + if [ "x$1" = "xFAIL" -o "x$1" = "xWARN" ] ; then + rljAddPhase "$1" "$2" + return $? + else + rlLogError "rlPhaseStart: Unknown phase type: $1" + return 1 + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPhaseEnd +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlPhaseEnd + +End current phase, summarize asserts included and report phase result. + + rlPhaseEnd + +Final phase result is based on included asserts and phase type. + +=cut + +rlPhaseEnd() { + rljClosePhase +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPhaseStart* +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlPhaseStartSetup + +=head3 rlPhaseStartTest + +=head3 rlPhaseStartCleanup + +Start a phase of the specified type: Setup -> WARN, Test -> FAIL, Cleanup -> WARN. + + rlPhaseStartSetup [name] + rlPhaseStartTest [name] + rlPhaseStartCleanup [name] + +=over + +=item name + +Optional name of the phase. If not specified, default Setup/Test/Cleanup are +used. + +=back + +If you do not want these shortcuts, use plain C function. + +=cut + +rlPhaseStartSetup() { + rlPhaseStart "WARN" "${1:-Setup}" +} +rlPhaseStartTest() { + rlPhaseStart "FAIL" "${1:-Test}" +} +rlPhaseStartCleanup() { + rlPhaseStart "WARN" "${1:-Cleanup}" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlLogLowMetric +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Metric + +=head3 rlLogMetricLow + +Log a metric, which should be as low as possible to the journal. +(Example: memory consumption, run time) + + rlLogMetricLow name value [tolerance] + +=over + +=item name + +Name of the metric. It has to be unique in a phase. + +=item value + +Value of the metric. + +=item tolerance + +It is used when comparing via rcw. It means how larger can the +second value be to not trigger a FAIL. Default is 0.2 + +=back + +When comparing FIRST, SECOND, then: + + FIRST >= SECOND means PASS + FIRST+FIRST*tolerance >= SECOND means WARN + FIRST+FIRST*tolerance < SECOND means FAIL + +B Simple benchmark is compared via this metric type in +rcw. It has a tolerance of 0.2. First run had 1 second. So: + + For PASS, second run has to be better or equal to first. + So any value of second or less is a PASS. + For WARN, second run can be a little worse than first. + Tolerance is 0.2, so anything lower than 1.2 means WARN. + For FAIL, anything worse than 1.2 means FAIL. + +=cut + +rlLogMetricLow() { + rljAddMetric "low" "$1" "$2" "$3" +} + +rlLogLowMetric() { + rlLogWarning "rlLogLowMetric is deprecated, use rlLogMetricLow instead" + rljAddMetric "low" "$1" "$2" "$3" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlLogMetricHigh +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlLogMetricHigh + +Log a metric, which should be as high as possible to the journal. +(Example: number of executions per second) + + rlLogMetricHigh name value [tolerance] + +=over + +=item name + +Name of the metric. It has to be unique in a phase. + +=item value + +Value of the metric. + +=item tolerance + +It is used when comparing via rcw. It means how lower can the +second value be to not trigger a FAIL. Default is 0.2 + +=back + +When comparing FIRST, SECOND, then: + + FIRST <= SECOND means PASS + FIRST+FIRST*tolerance <= SECOND means WARN + FIRST+FIRST*tolerance > SECOND means FAIL + +=cut + +rlLogMetricHigh() { + rljAddMetric "high" "$1" "$2" "$3" +} + +rlLogHighMetric() { + rlLogWarning "rlLogHighMetric is deprecated, use rlLogMetricHigh instead" + rljAddMetric "high" "$1" "$2" "$3" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Ales Zelinka + +=item * + +Petr Splichal + +=item * + +Dalibor Pospisil + +=item * + +Jakub Heger + +=back + +=cut diff --git a/src/lsb_release b/src/lsb_release new file mode 100755 index 0000000..9d7afbc --- /dev/null +++ b/src/lsb_release @@ -0,0 +1,415 @@ +#!/bin/sh +# +# lsb_release - collect LSB conformance status about a system +# +# Copyright (C) 2000, 2002, 2004 Free Standards Group, Inc. +# Originally by Dominique MASSONIE +# +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# * Changes in 2.0 +# - Support LSB 2.0 module layout (Mats Wichmann) +# The LSB_VERSION is now a colon-separated field of supported module versions +# An /etc/lsb-release.d is searched for modules beyond the core. +# Only the filenames in this directory is looked at, those names are added +# to LSB_VERSION. This allows module support to be handled easily by +# package install/removal without a need to edit lsb-release on the fly. +# - Correct license: FSG == Free Standards Group, Inc. +# +# * Changes in 1.4 +# - "awk" not needed anymore (Loic Lefort) +# - fixed bug #121879 reported by Chris D. Faulhaber, +# some shells doesn't support local variables +# - fixed a bug when single parameter sets many args including -s +# - function DisplayProgramVersion (undocumented) now exits script like Usage +# - cosmetic changes in comments/outputs +# +# * Changes in 1.3 +# - No changes in script, only in build infrastructure +# +# * Changes in 1.2 +# - Fixed more bash'isms +# - LSB_VERSION is no longer required in /etc/lsb-release file +# +# * Changes in 1.1 +# - removed some bash-ism and typos +# Notice: script remains broken with ash because of awk issues +# - changed licence to FSG - "Free Software Group, Inc" +# - fixed problem with --short single arg call +# - changed Debian specifics, codename anticipates release num +# +# Description: +# Collect information from sourceable /etc/lsb-release file (present on +# LSB-compliant systems) : LSB_VERSION, DISTRIB_ID, DISTRIB_RELEASE, +# DISTRIB_CODENAME, DISTRIB_DESCRIPTION (all optional) +# Then (if needed) find and add names from /etc/lsb-release.d +# Then (if needed) find and parse the /etc/[distro]-release file + + +############################################################################### +# DECLARATIONS +############################################################################### + +# This script version +SCRIPTVERSION="2.0" + +# Defines the data files +INFO_ROOT="/etc" # directory of config files +INFO_LSB_FILE="lsb-release" # where to get LSB version +INFO_LSB_DIR="lsb-release.d" # where to get LSB addon modules +INFO_DISTRIB_SUFFIX="release" # - +ALTERNATE_DISTRIB_FILE="/etc/debian_version" # for Debian [based distrib] +ALTERNATE_DISTRIB_NAME="Debian" # " +CHECKFIRST="/etc/redhat-release" # check it before file search + +# Defines our exit codes +EXIT_STATUS="0" # default = Ok :) +ERROR_UNKNOWN="10" # unknown error +ERROR_USER="1" # program misuse +ERROR_PROGRAM="2" # internal error +ERROR_NOANSWER="3" # all required info not available + # typically non LSB compliant distro! + +# Defines our messages +MSG_LSBVER="LSB Version:\t" +MSG_DISTID="Distributor ID:\t" +MSG_DISTDESC="Description:\t" +MSG_DISTREL="Release:\t" +MSG_DISTCODE="Codename:\t" +MSG_NA="n/a" +MSG_NONE="(none)" +MSG_RESULT="" # contains the result in case short output selected + +# Description string delimiter +DESCSTR_DELI="release" + + +############################################################################### +# FUNCTIONS +############################################################################### + +# Display Program Version for internal use (needed by help2man) +DisplayProgramVersion() { + echo "FSG `basename $0` v$SCRIPTVERSION" + echo + echo "Copyright (C) 2000, 2002, 2004 Free Standards Group, Inc." + echo "This is free software; see the source for copying conditions. There\ + is NO" + echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR\ + PURPOSE." + echo + echo "Originally written by Dominique MASSONIE." + + exit $EXIT_STATUS +} + +# defines the Usage for lsb_release +Usage() { + echo "FSG `basename $0` v$SCRIPTVERSION prints certain LSB (Linux\ + Standard Base) and" + echo "Distribution information." + echo + echo "Usage: `basename $0` [OPTION]..." + echo "With no OPTION specified defaults to -v." + echo + echo "Options:" + echo " -v, --version" + echo " Display the version of the LSB specification against which the distribution is compliant." + echo " -i, --id" + echo " Display the string id of the distributor." + echo " -d, --description" + echo " Display the single line text description of the distribution." + echo " -r, --release" + echo " Display the release number of the distribution." + echo " -c, --codename" + echo " Display the codename according to the distribution release." + echo " -a, --all" + echo " Display all of the above information." + echo " -s, --short" + echo " Use short output format for information requested by other options (or version if none)." + echo " -h, --help" + echo " Display this message." + + exit $EXIT_STATUS +} + +# Handles the enhanced args (i.e. --something) +EnhancedGetopt() { + getopt -T >/dev/null 2>&1 # is getopt the enhanced one ? + if [ $? = 4 ] + then # Yes, advanced args ALLOWED + OPT=$(getopt -o acdhirsvp \ +--long all,codename,description,help,id,release,short,version,program_version \ + -n 'lsb_release' \ + -- "$@") + else # No, advanced args NOT allowed + # convert (if needed) the enhanced options into basic ones + MYARGS=$(echo "$@" | sed -e "/--/s/-\(-[[:alnum:]]\)[[:alnum:]]*/\1/g") + OPT=$(getopt -o acdhirsvp \ + -n 'lsb_release' \ + -- "$MYARGS") + fi + if [ $? != 0 ] + then + exit $ERROR_PROGRAM + fi + + NB_ARG="" # enabled if many args set in one parameter (i.e. -dris) + eval set -- "$OPT" + while true ; do + case "$1" in + -a|--all) ARG_A="y"; NB_ARG="y"; shift;; + -c|--codename) ARG_C="y"; NB_ARG="y"; shift;; + -d|--description) ARG_D="y"; NB_ARG="y"; shift;; + -i|--id) ARG_I="y"; NB_ARG="y"; shift;; + -r|--release) ARG_R="y"; NB_ARG="y"; shift;; + -s|--short) ARG_S="y"; shift;; + -v|--version) ARG_V="y"; NB_ARG="y"; shift;; + -p|--program_version) DisplayProgramVersion;; + -h|--help) Usage;; + --) shift; break;; + *) EXIT_STATUS=$ERROR_USER + Usage;; + esac + done +} + +# Get/Init LSB infos (maybe Distrib infos too) +GetLSBInfo() { + # if we found LSB_VERSION, continue to look in directory + if [ -d "$INFO_ROOT/$INFO_LSB_DIR" ] + then + for tag in "$INFO_ROOT/$INFO_LSB_DIR/"* + do + LSB_VERSION=$LSB_VERSION:`basename $tag` + done + fi +} + +# Get the whole distrib information string (from ARG $1 file) +InitDistribInfo() { +## Notice: Debian has a debian_version file +## (at least) Mandrake has two files, a mandrake and a redhat one + FILENAME=$1 # CHECKFIRST or finds' result in GetDistribInfo() or "" + + if [ -z "$FILENAME" ] + then + if [ -f "$ALTERNATE_DISTRIB_FILE" ] + then # For Debian only + [ -z "$DISTRIB_ID" ] && DISTRIB_ID="$ALTERNATE_DISTRIB_NAME" + [ -z "$DISTRIB_RELEASE" ] \ + && DISTRIB_RELEASE=$(cat $ALTERNATE_DISTRIB_FILE) + [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.1" ] \ + && DISTRIB_CODENAME="Slink" + [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.2" ] \ + && DISTRIB_CODENAME="Potato" +# [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.3" ] \ +# && DISTRIB_CODENAME="Woody" + [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$DISTRIB_RELEASE + # build the DISTRIB_DESCRIPTION string (never need to be parsed) + [ -z "$DISTRIB_DESCRIPTION" ] \ + && DISTRIB_DESCRIPTION="$DISTRIB_ID $DESCSTR_DELI $DISTRIB_REL\ +EASE ($DISTRIB_CODENAME)" + else # Only for nothing known compliant distrib :( + [ -z "$DISTRIB_ID" ] && DISTRIB_ID=$MSG_NA + [ -z "$DISTRIB_RELEASE" ] && DISTRIB_RELEASE=$MSG_NA + [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$MSG_NA + [ -z "$DISTRIB_DESCRIPTION" ] && DISTRIB_DESCRIPTION=$MSG_NONE + + EXIT_STATUS=$ERROR_NOANSWER + fi + else + NO="" # is Description string syntax correct ? + if [ -z "$DISTRIB_DESCRIPTION" ] \ + || [ -n "$(echo $DISTRIB_DESCRIPTION | \ + sed -e "s/.*$DESCSTR_DELI.*//")" ] + then + TMP_DISTRIB_DESC=$(head -n 1 $FILENAME 2>/dev/null) + [ -z "$DISTRIB_DESCRIPTION" ] \ + && DISTRIB_DESCRIPTION=$TMP_DISTRIB_DESC + else + TMP_DISTRIB_DESC=$DISTRIB_DESCRIPTION + fi + + if [ -z "$TMP_DISTRIB_DESC" ] # head or lsb-release init + then # file contains no data + DISTRIB_DESCRIPTION=$MSG_NONE + NO="y" + else # Do simple check + [ -n "$(echo $TMP_DISTRIB_DESC | \ + sed -e "s/.*$DESCSTR_DELI.*//")" ] \ + && NO="y" + fi + + if [ -n "$NO" ] + then # does not contain "release" delimiter + [ -z "$DISTRIB_ID" ] && DISTRIB_ID=$MSG_NA + [ -z "$DISTRIB_RELEASE" ] && DISTRIB_RELEASE=$MSG_NA + [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$MSG_NA + fi + fi +} + +# Check missing and requested infos, then find the file and get infos +GetDistribInfo() { + NO="" # /etc/lsb-release data are enough to reply what is requested? + [ -n "$ARG_D" ] && [ -z "$DISTRIB_DESCRIPTION" ] && NO="y" + [ -z "$NO" ] && [ -n "$ARG_I" ] && [ -z "$DISTRIB_ID" ] && NO="y" + [ -z "$NO" ] && [ -n "$ARG_R" ] && [ -z "$DISTRIB_RELEASE" ] && NO="y" + [ -z "$NO" ] && [ -n "$ARG_C" ] && [ -z "$DISTRIB_CODENAME" ] && NO="y" + + if [ -n "$NO" ] + then + if [ ! -f "$CHECKFIRST" ] + then + CHECKFIRST=$(find $INFO_ROOT/ -maxdepth 1 \ + -name \*$INFO_DISTRIB_SUFFIX \ + -and ! -name $INFO_LSB_FILE \ + -and -type f \ + 2>/dev/null \ + | head -n 1 ) # keep one of the files found (if many) + fi + InitDistribInfo $CHECKFIRST + fi +} + +# Display version of LSB against which distribution is compliant +DisplayVersion() { + if [ -z "$ARG_S" ] + then + echo -e "$MSG_LSBVER$LSB_VERSION" # at least "n/a" + else + MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$LSB_VERSION" + fi +} + +# Display string id of distributor ( i.e. a single word! ) +DisplayID() { + if [ -z "$DISTRIB_ID" ] + then +## Linux could be part of the distro name (i.e. Turbolinux) or a separate word +## set before, after... +## also expect a delimiter ( i.e. "release" ) + if [ -n "$(echo $TMP_DISTRIB_DESC | sed "s/.*$DESCSTR_DELI.*//")" ] + then + DISTRIB_ID=$MSG_NA + else + DISTRIB_ID=$(echo " $TMP_DISTRIB_DESC" \ + | sed -e "s/[[:blank:]][Ll][Ii][Nn][Uu][Xx][[:blank:]]/ /g" \ + -e "s/\(.*\)[[:blank:]]$DESCSTR_DELI.*/\1/" -e "s/[[:blank:]]//g") + fi + fi + if [ -z "$ARG_S" ] + then + echo -e "$MSG_DISTID$DISTRIB_ID" + else + MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$DISTRIB_ID" + fi +} + +# Diplay single line text description of distribution +DisplayDescription() { + if [ -z "$DISTRIB_DESCRIPTION" ] + then + # should not be empty since GetDistribInfo called on Initialization ! + EXIT_STATUS=$ERROR_PROGRAM + fi + if [ -z "$ARG_S" ] + then + echo -e "$MSG_DISTDESC$DISTRIB_DESCRIPTION" + else + MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }\"$DISTRIB_DESCRIPTION\"" + fi +} + +# Display release number of distribution. +DisplayRelease() { + if [ -z "$DISTRIB_RELEASE" ] + then # parse the "$DISTRIB_DESCRIPTION" string + DISTRIB_RELEASE=$(echo "$TMP_DISTRIB_DESC" | \ + sed -e "s/.*$DESCSTR_DELI[[:blank:]]*\([[:digit:]][[:graph:]]*\).*/\1/" ) + [ "$DISTRIB_RELEASE" = "$TMP_DISTRIB_DESC" ] \ + || [ -z "$DISTRIB_RELEASE" ] \ + && DISTRIB_RELEASE=$MSG_NA + fi + if [ -z "$ARG_S" ] + then + echo -e "$MSG_DISTREL$DISTRIB_RELEASE" + else + MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$DISTRIB_RELEASE" + fi +} + +# Display codename according to distribution version. +DisplayCodename() { + if [ -z "$DISTRIB_CODENAME" ] + then # parse the "$DISTRIB_DESCRIPTION" string + DISTRIB_CODENAME=$(echo "$TMP_DISTRIB_DESC" | \ + sed -e "s/.*$DESCSTR_DELI.*(\(.*\)).*/\1/") + [ "$DISTRIB_CODENAME" = "$TMP_DISTRIB_DESC" ] \ + || [ -z "$DISTRIB_CODENAME" ] \ + && DISTRIB_CODENAME=$MSG_NA + fi + if [ -z "$ARG_S" ] + then + echo -e "$MSG_DISTCODE$(echo "$DISTRIB_CODENAME" | \ + tr -d "[:blank:]")" # Remove blanks + else + MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$(echo "$DISTRIB_CODENAME" | \ + tr -d "[:blank:]")" + fi +} + + +############################################################################### +# MAIN +############################################################################### + +# Check if any prog argument +if [ -z "$1" ] +then + ARG_V="y" # default set to Display LSB Version (not Usage) +else + EnhancedGetopt "$@" # Parse program args + if [ -n "$ARG_S" ] && [ -z "$NB_ARG" ] + then + ARG_V="y" # set also default for --short when single arg + fi +fi + +# Update args to All if requested +if [ -n "$ARG_A" ] +then + [ -z "$ARG_C" ] && ARG_C="y" + [ -z "$ARG_D" ] && ARG_D="y" + [ -z "$ARG_I" ] && ARG_I="y" + [ -z "$ARG_R" ] && ARG_R="y" + [ -z "$ARG_V" ] && ARG_V="y" +fi + +# Initialization +GetLSBInfo +GetDistribInfo + +# Display requested infos (order as follow) +[ -n "$ARG_V" ] && DisplayVersion +[ -n "$ARG_I" ] && DisplayID +[ -n "$ARG_D" ] && DisplayDescription +[ -n "$ARG_R" ] && DisplayRelease +[ -n "$ARG_C" ] && DisplayCodename + +[ -n "$ARG_S" ] && echo "$MSG_RESULT" + +exit $EXIT_STATUS diff --git a/src/performance.sh b/src/performance.sh new file mode 100644 index 0000000..b2c3c60 --- /dev/null +++ b/src/performance.sh @@ -0,0 +1,191 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: performance.sh - part of the BeakerLib project +# Description: Performance measuring routines +# +# Author: Petr Muller +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - performance - Performance measuring routines + +=head1 DESCRIPTION + +This is a library of helpers and shortcut for performance monitoring +of applications. It provides various means of measuring time +and memory performance of programs. + +=head1 FUNCTIONS + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPerfTime_RunsInTime +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Time Performance + +=head3 rlPerfTime_RunsInTime + +Measures, how many runs of some commands can be done in specified time. +This approach is suitable for short-time running tasks (up to few seconds), +where averaging few runs is not precise. This is done several times, and +the final result is the average of all runs. It prints the number on stdout, +so it has to be captured. + + rlPerfTime_RunsInTime command [time] [runs] + +=over + +=item command + +Command to run. + +=item time + +Time in seconds (optional, default=30). + +=item runs + +Number of averaged runs (optional, default=3). + +=back + +=cut + +rlPerfTime_RunsInTime(){ + local command=$1 + local time=${2:-"30"} + local runs=${3:-"3"} + local RES=$((0)) + local TOTAL=$((0)) + local DONE_RUNS=$((1)) + local PID=$$ + rlLog "Measuring how much runs we'll make in $time seconds" + rlLog "Command: '$command'" + rlLog "The result is an average of $runs rounds" + trap ' TOTAL=$((TOTAL+RES));\ + rlLog "Round $DONE_RUNS finished, made $RES runs in it";\ + if [ "$DONE_RUNS" == "$runs" ];\ + then\ + rlLog "Done, the average ($TOTAL/$DONE_RUNS) is $((TOTAL/DONE_RUNS)) runs";\ + echo $((TOTAL/DONE_RUNS));\ + return 0;\ + else\ + RES=$((0));\ + DONE_RUNS=$((DONE_RUNS+1));\ + eval "sleep $time; kill -USR1 $PID" & + fi' SIGUSR1 + + eval "sleep $time; kill -USR1 $$" & + while true ; do + eval "$1" + RES=$((RES+1)) + done +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPerfTime_AvgFromRuns +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlPerfTime_AvgFromRuns + +Measures the average time of running some task. This approach is suitable +for long-time running tasks (tens of seconds and more), where it is +precise enough. Measured runs can be preceded by dry run, which is not +measured and it's purpose is to warm up various caches. +It prints the number on stdout, so it has to be captured. +Or, result is then stored in special rl_retval variable. + + rlPerfTime_AvgFromRuns command [count] [warmup] + +=over + +=item command + +Command to run. + +=item count + +Times to run (optional, default=3). + +=item warmup + +Warm-up run, run if this option is not "warmup" (optional, default="warmup") + +=back + +=cut + +rlPerfTime_AvgFromRuns(){ + local command="$1" + local runs=${2:-"3"} + local warmup=${3:-"warmup"} + local total=0 + rlLog "Measuring the average time of runnning command '$command'" + rlLog "The result will be an average of $runs runs" + + if [ "$warmup" == "warmup" ]; then + rlLog "Doing non-measured warmup run" + eval "$command" + fi + local __INTERNAL_TIMER=$(mktemp) # no-reboot + + local cnt + for cnt in $(seq $runs); do + /usr/bin/time -o $__INTERNAL_TIMER -f "bt=\"%U + %S\"" $command + . $__INTERNAL_TIMER + rlLog "Run $cnt took $bt seconds" + total="$(echo "scale=5; $total + $bt" | bc)" + done + rlLog "The average of $runs runs was $(echo "scale=5; $total / $runs" | bc) seconds" + echo "$(echo "scale=5; $total / $runs" | bc)" + export rl_retval="$(echo "scale=5; $total / $runs" | bc)" + rm -f $__INTERNAL_TIMER +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=back + +=cut diff --git a/src/perl/deja-summarize b/src/perl/deja-summarize new file mode 100755 index 0000000..b2b105b --- /dev/null +++ b/src/perl/deja-summarize @@ -0,0 +1,473 @@ +#!/usr/bin/perl -w + +# Original coding: J.W. Lockhart + +################################# +# Usage: deja-summarize file1 [file2] +# cat suitename.sum | deja-summarize +# Where files are dejagnu test.sum output from one or two runs of the test suite. +# Regression checking requires two input files. +# +# Intent: to summarize test output from suites such as gdb and gcc, +# and possibly spot any regressions from previous runs, if any such +# results are available for comparison. +# +# The scoring mechanism and weighting can be adjusted by +# editing the hashes below -- or you can use the rest of the summarized +# output to come up with your own scheme. +############################## + +$suiteName = ''; +use vars qw($run1 $run2 $cur $res $ste $mdl $progName $fileCnt); +$passCnt = $failCnt = $xfailCnt = $kfailCnt = $unSupCnt = $errCnt = 0; +$unTestCnt = $unResCnt = $warnCnt = $kpassCnt = $xpassCnt = $suiteCnt = $fileCnt = 0; +$totalScore = 0; +$resultType = 'default'; +$VERBOSE_SUM = 0; + +# To get a meaningful score, make the base increase +# or decrease sanely per type of test result. +# Note that these labels must match the %weights that follow. +%baseScores = ( + 'pass' => 1, + 'fail' => (-2), + 'xfail' => 0, + 'kfail' => (-1), + 'kpass' => (-1), + 'xpass' => (-1), + 'warning' => 0, + 'brokentest' => (-10), + 'ransuite' => 0, + 'regression' => (-10), + 'unsupported' => 0, + 'untested' => 0, + 'unresolved' => 0, + 'default' => 0, + ); + +# To make the differences between scores more noticeable +# and meaningful, define how important each type of test +# result is -- especially based on expected results, +# where you're likely to have 8,000 to 11,000 PASS results. +# Note that these labels must match the %baseScores above. +%weights = ( + 'pass' => 1, + 'fail' => 2, + 'xfail' => 0, + 'kfail' => 1, + 'kpass' => 1, + 'xpass' => 1, + 'warning' => 0, + 'brokentest' => 5, + 'ransuite' => 0, + 'regression' => 10, + 'unsupported' => 0, + 'untested' => 0, + 'unresolved' => 0, + 'default' => 1, + ); + +# Names to print for the result summary +%lNames = ( + pass => 'PASS', + fail => 'FAIL', + regression => 'REGRESSION', + ransuite => 'SUITES', + default => 'default', + xfail => 'XFAIL', + kfail => 'KFAIL', + kpass => 'KPASS', + xpass => 'XPASS', + warning => 'WARN', + unsupported => 'UNSUPPORTED', + untested => 'UNTESTED', + unresolved => 'UNRESOLVED', + brokentest => 'ERROR', + ); +# For later formatting use, figure out the longest of those displayed names. +$maxLen_lNames = 0; +for my $i (keys(%lNames)) { + my $l = length($i); + $maxLen_lNames = $l if ($l > $maxLen_lNames); +} + +# Thus the actual amount that we will vary the score +# is given by (BASE * WEIGHT). +# +# TAG TYPES: +# pass - testcase returned PASS +# fail - testcase returned FAIL +# xfail - testcase returned an expected failure +# kfail - not sure; either known or kernel-caused failure, perhaps. +# brokentest - testcase did not run correctly, generated 'ERROR' string +# regression - testcase PASSed in first file, FAILed ('unexpected') in second +# default - any other result (Unsupported, or misc logfile output). + +## FIXME: implement the 'regression' category. This requires +## that we have 2 files as input rather than just one. +$fNameA = 'none'; +$fNameB = 'none'; + +# refs/pointers to items of interest + +$progName = $0; +$run1 = $run2 = $cur = $res = $ste = $mdl = ''; + +### [for those whose perl-fu is, well, python... ] +### One could think of the data structures this way, +### if it adds any familiarity... +# struct resultsFile { +# String fileName; +# struct suiteList *ste; +# struct resultsCount *res; +# } +# struct suiteList { +# String suiteName; +# struct moduleList *mod; +# struct resultsCount *res; +# } +# struct moduleList { +# String moduleName; +# String result; // pass/fail etc +# String moduleDetails; +# } +# struct resultsCount { +# unsigned passCnt; +# unsigned failCnt; +# unsigned xfailCnt; +# unsigned unSupCnt; +# unsigned errCnt; +# unsigned testCnt; +# } + +sub newFile { + my ($fn) = @_; + my %r = (); + my @tmpSuites = (); + + $r{filename} = $fn; + $r{name} = $fn; + $r{suites} = \@tmpSuites; + $r{results} = &newResults; + $r{total_score} = 0; + $fileCnt++; + + return \%r; +} +sub newResults { + my %tmpResCnt = (); + + for my $n (keys(%lNames)) { + $tmpResCnt{$n} = 0; + } + + return \%tmpResCnt; +} +################################################ +# isBad - return 1 if string might indicate regression, 0 otherwise. +# These are results as found in %lNames that might indicate a regression. +# For example if the test used to pass, but now has this kind of result +# (fail or broken, or whatever). +sub isBad { + my ($b) = @_; + for my $bad qw(fail brokentest kfail) { + return 1 if ($b eq $bad); + } + return 0; +} +################################################# +# incrResult +# increment the count of a given result type in a hashref +# result type as found in lNames; hashref must have member 'results'. +sub incrResult { + my ($r, $rType) = @_; + my $tmp = $r->{'results'}; + + $tmp->{$rType} = $tmp->{$rType} + 1; +} + +################################################ +# newSuite - return hashref to an initialized Suite hash +sub newSuite { + my ($sName) = @_; + my %tmpSuite = (); + my @tmpModules = (); + + $tmpSuite{name} = $sName; + $tmpSuite{modules} = \@tmpModules; + $tmpSuite{results} = &newResults; + + return \%tmpSuite; +} + +########################################################################## +# findMod: find hashref to module 'm' in suite 's' of run 'r', or return ''. +sub findMod { + my ($m, $s, $r) = @_; + + for my $st (@{$r->{suites}}) { + if ($st->{name} eq $s->{name}) { + for my $md (@{$st->{modules}}) { + if (($md->{name} eq $m->{name}) and ($md->{detail} eq $m->{detail})) { + return $md; + } + } + } + } + return ''; +} +######################################################## +# printMod: print out the result, name, and detail for a given test module. +sub printMod { + my ($m) = @_; + print $m->{result}, ": ", $m->{name}, ": ", $m->{detail}, "\n"; +} + +######################################################## +# newModule - return ref to initialized new module hash +# inputs: +# n - module name (per %lNames) +# r - test result +# d - test detail +# Example: +# FAIL: gdb.base/auxv.exp: generate native core dump +# $newM = newModule('fail', 'gdb.base/auxv.exp', 'generate native core dump'); +sub newModule { + my ($n,$r,$d) = @_; + my %resRecord = ( 'name' => $n, + 'result' => $r, + 'detail' => $d, + ); + return \%resRecord; +} + +######################################################## +# doHelp - print out the usage +sub doHelp { + print STDERR "Usage: $progName file1 ", '[file2]',"\n"; + print STDERR " cat suitename.sum | $progName \n"; + print STDERR "Where files are dejagnu test.sum output from one or two runs of the test suite.\n"; + print STDERR "Regression checking requires two input files.\n"; +} + +######################################################## +# "MAIN" STARTS HERE +######################################################## +if (($#ARGV >= 0) and ($ARGV[-1] =~ m|-+help|)) { + # FIXME: should really have real option + # handling if we grow real options. + doHelp(); + exit 0; +} + +while (defined ($ln = <>)) { + chomp($ln); + if ($fNameA ne 'none') { + if ($ARGV ne $cur->{filename}) { + ## RESET ALL POINTERS + die("cannot handle more than 2 files\n") if ($fNameB ne 'none'); + $fNameB = $ARGV; + $run2 = newFile($ARGV); + $cur = $run2; + $cur->{filename} = $ARGV; + # print STDERR "New Input File: ", $cur->{filename}, "\n"; + } + } else { + $fNameA = $ARGV; + $run1 = newFile($ARGV); + $cur = $run1; + $cur->{filename} = $ARGV; + # print STDERR "New Input File: ", $cur->{filename}, "\n"; + } + + $resultType = 'default'; + # if ($ln =~ m|^PASS:\s+([^:]+):\s+(.*)|) { + if ($ln =~ m|^PASS:\s+([^:[:space:]]+):*\s*(.*)|) { + ### Possible New Module for GDB-style output + # PASS: gdb.base/assign.exp: continuing after dummy() + $testName = $1; + $testDetail = $2; + $passCnt++; + $resultType = 'pass'; + my $tempMod = newModule($testName,$resultType,$testDetail); + $mdl = $tempMod; + push @{$ste->{modules}}, $tempMod; + print "ps: $testName with: $testDetail\n" if ($VERBOSE_SUM); + incrResult($cur, $resultType); + incrResult($ste, $resultType); + # } elsif ($ln =~ m|^FAIL:\s+([^:]+):\s+(.*)|) { + } elsif ($ln =~ m|^FAIL:\s+([^:[:space:]]+):*\s*(.*)|) { + ### New Module + # FAIL: gdb.base/auxv.exp: generate native core dump + $failName = $1; + $failDetail = $2; + my ($steName, $modName) = split(m|/|, $failName, 2); + print "fn: ste $steName / mod $modName with: $failDetail\n" if ($VERBOSE_SUM); + $failCnt++; + $resultType = 'fail'; + my $tempMod = newModule($failName,$resultType,$failDetail); + push @{$ste->{modules}}, $tempMod; + incrResult($cur, $resultType); + incrResult($ste, $resultType); + # } elsif ($ln =~ m|^XFAIL:\s+([^:]+):\s+(.*)|) { + } elsif ($ln =~ m|^XFAIL:\s+([^:[:space:]]+):*\s+(.*)|) { + ### New Module + # XFAIL: gdb.base/list.exp: list line 1 with unlimited listsize + $xfailName = $1; + $xfailDetail = $2; + print "xf: $xfailName with: $xfailDetail\n" if ($VERBOSE_SUM); + $xfailCnt++; + $resultType = 'xfail'; + my $tempMod = newModule($xfailName,$resultType,$xfailDetail); + $mdl = $tempMod; + push @{$ste->{modules}}, $tempMod; + incrResult($cur, $resultType); + incrResult($ste, $resultType); + # } elsif ($ln =~ m|^KFAIL:\s+([^:]+):\s+(.*)|) { + } elsif ($ln =~ m|^KFAIL:\s+([^:[:space:]]+):*\s+(.*)|) { + ### New Module + # KFAIL: gdb.threads/tls.exp: info address me (PRMS: gdb/1294) + $kfailName = $1; + $kfailDetail = $2; + print "kf: $kfailName with: $kfailDetail\n" if ($VERBOSE_SUM); + $kfailCnt++; + $resultType = 'kfail'; + my $tempMod = newModule($kfailName,$resultType,$kfailDetail); + $mdl = $tempMod; + push @{$ste->{modules}}, $tempMod; + incrResult($cur, $resultType); + incrResult($ste, $resultType); + } elsif ($ln =~ m|^Running\s+(\.+/)*(\S+)\s+|) { + ### New Suite + # Running ../../../gdb/testsuite/gdb.base/bitfields.exp ... + my $foo = $2; + $foo =~ s|gdb/testsuite/||g; + my @tmpName = split(m|/|, $foo); + my ($indx1, $indx2) = ($#tmpName, ($#tmpName - 1)); + $suiteName = $tmpName[$indx2]; + $moduleName = $tmpName[$indx1]; + print "suiteName: $suiteName :: moduleName: $moduleName\n" if ($VERBOSE_SUM); + my $tempSuite = newSuite($suiteName); + push @{$cur->{suites}}, $tempSuite; + $ste = $tempSuite; + $resultType = 'ransuite'; + $suiteCnt++; + incrResult($cur, $resultType); + } elsif ($ln =~ m|^UNSUPPORTED: (\S+)|) { + # UNSUPPORTED: gdb.base/auxv.exp: info auxv on native core dump + $unSupName = $1; + print "unsup: $unSupName\n" if ($VERBOSE_SUM); + $resultType = 'unsupported'; + $unSupCnt++; + incrResult($cur, $resultType); + } elsif ($ln =~ m|^UNTESTED: (\S+)|) { + # UNSUPPORTED: gdb.base/auxv.exp: info auxv on native core dump + $unSupName = $1; + print "unsup: $unSupName\n" if ($VERBOSE_SUM); + $resultType = 'untested'; + $unTestCnt++; + incrResult($cur, $resultType); + } elsif ($ln =~ m|^UNRESOLVED: (\S+)|) { + # UNSUPPORTED: gdb.base/auxv.exp: info auxv on native core dump + $unSupName = $1; + print "unsup: $unSupName\n" if ($VERBOSE_SUM); + $resultType = 'unresolved'; + $unResCnt++; + incrResult($cur, $resultType); + } elsif ($ln =~ m|^WARNING: (\S+)|) { + # WARNING: Couldn't test self + # e.g., fairly useless message. + $unSupName = $1; + print "warn: $unSupName\n" if ($VERBOSE_SUM); + $resultType = 'warning'; + $warnCnt++; + incrResult($cur, $resultType); + # } elsif ($ln =~ m|^KPASS:\s+([^:]+):\s+(.*)|) { + } elsif ($ln =~ m|^KPASS:\s+([^:[:space:]]+):*\s+(.*)|) { + ### Possible New Module + # KPASS: gdb.base/sigstep.exp: continue on breakpoint, to handler entry; performing continue (PRMS gdb/1738) + $testName = $1; + $testDetail = $2; + $kpassCnt++; + $resultType = 'kpass'; + my $tempMod = newModule($testName,$resultType,$testDetail); + $mdl = $tempMod; + push @{$ste->{modules}}, $tempMod; + print "kp: $testName with: $testDetail\n" if ($VERBOSE_SUM); + incrResult($cur, $resultType); + incrResult($ste, $resultType); + } elsif ($ln =~ m|^XPASS:\s+([^:[:space:]]+):*\s*(.*)|) { + ### Possible New Module + # XPASS: gcc.dg/cpp/cmdlne-dI-M.c scan-file (^|\\n)cmdlne-dI-M.*:[^\\n]*cmdlne-dI-M.c + # XPASS: gdb.mi/mi-var-display.exp: eval variable anone + $testName = $1; + $testDetail = $2; + $xpassCnt++; + $resultType = 'xpass'; + my $tempMod = newModule($testName,$resultType,$testDetail); + $mdl = $tempMod; + push @{$ste->{modules}}, $tempMod; + print "xp: $testName with: $testDetail\n" if ($VERBOSE_SUM); + incrResult($cur, $resultType); + incrResult($ste, $resultType); + } elsif (($ln =~ m|^ERROR:.*?(\S+testsuite\S+)|) || ($ln =~ m|^ERROR:\s+(.*)|)) { + # ERROR: tcl error sourcing ../../../gdb/testsuite/gdb.base/attach-32.exp. + # ERROR: couldn't execute "/usr/src/redhat/BUILD/gdb-6.3/build-x86_64-redhat-linux/gdb/testsuite/gdb.base/attach-32": no such file or directory + $testError = $1; + # $testFullError = $ln; + print "test err: $testError\n" if ($VERBOSE_SUM); + $errCnt++; + $resultType = 'brokentest'; + incrResult($cur, $resultType); + } else { + # miscellaneous stuff such as tcl tracebacks, blank lines, warnings, etc. + $resultType = 'default'; + incrResult($cur, $resultType); + # print STDERR "MISC: $ln\n" if ($VERBOSE_SUM); + } + $totalScore += ($baseScores{$resultType} * $weights{$resultType}); + $cur->{total_score} += ($baseScores{$resultType} * $weights{$resultType}); + $ln = ''; +} + +# print "SUITES: $suiteCnt\n"; +# print " PASS: $passCnt\n"; +# print " FAIL: $failCnt\n"; +# print " XFAIL: $xfailCnt\n"; +# print " ERR: $errCnt\n"; +# print "UNSUPP: $unSupCnt\n"; +# print " SCORE: $totalScore\n"; + +if ($fileCnt > 1) { + my $oldMod; + my $banner = 0; + for $ste (@{$run2->{suites}}) { + for $mdl (@{$ste->{modules}}) { + if (isBad($mdl->{result})) { + if ($oldMod = findMod($mdl,$ste,$run1)) { + if ($oldMod->{result} =~ m|pass|i) { + $resultType = 'regression'; + incrResult($ste, $resultType); + incrResult($run2, $resultType); + $run2->{total_score} += ($baseScores{$resultType} * $weights{$resultType}); + print "\nREGRESSION INFO:\n" unless (++$banner > 1); + printMod($oldMod); + printMod($mdl); + } + } + } + } + } +} + +for my $run ($run1, $run2) { + my $x = $run->{results}; + print "\nFilename: $run->{filename}\n"; + for my $y (keys(%$x)) { + printf "%-${maxLen_lNames}s: %7d\n", $lNames{$y}, $x->{$y}; + } + $x = $run->{suites}; + #printf STDERR "%8s: %7d\n", "SUITEDAT", $#{@$x}; + printf "%-${maxLen_lNames}s: %7d\n", "SCORE", $run->{total_score}; + last if (1 >= $fileCnt); +} diff --git a/src/perl/docsjoin b/src/perl/docsjoin new file mode 100755 index 0000000..6f6f86d --- /dev/null +++ b/src/perl/docsjoin @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Petr Splichal + +# This perl script takes all supplied files or stdin and extracts +# every =pod section that does not include =head1 headings except +# those which contain beakerlib-manual-{header,footer} directives. +# +# Finally it spits out parsed =pod sections in the following order: +# beakerlib-manual-header +# beakerlib-manual-include (all non-=head1 sections included here) +# beakerlib-manual-footer + +my $pod = 0; +my $type = ''; +my %text = (include => '=head1 FUNCTIONS'); + +while (<>) { + # end of =pod section + if (m/^=cut/) { + $pod = 0; + } + + # recognize header/footer and trash =head1's + $type = 'header' if m/beakerlib-manual-header/; + $type = 'footer' if /beakerlib-manual-footer/; + $type = 'trash' if /^=head1/ && ! ($type =~ m/header|footer/); + + # add to corresponding place + $text{$type} .= $_ if $pod; + + # start of =pod section + if (m/^=pod/) { + $pod = 1; + $type = 'include'; + } +} + +print $text{header}; +print $text{include}; +print $text{footer}; diff --git a/src/python/daemonize.py b/src/python/daemonize.py new file mode 100755 index 0000000..efb4e08 --- /dev/null +++ b/src/python/daemonize.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python + +# Authors: Jiri Jaburek +# +# Description: Daemonization backend for rlDaemonize +# +# Copyright (c) 2012 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + +import os, sys + +from pwd import getpwnam +from grp import getgrnam + +from optparse import OptionParser +from shlex import shlex + +def file_write(filename, content): + fd = open(filename, 'w') + fd.write(content + '\n') + fd.close() + +def close_all_fds(): + try: + maxfd = os.sysconf('SC_OPEN_MAX') # same as _SC_OPEN_MAX in C + except: + maxfd = 1024 + + for fd in range(0, maxfd): + try: + os.close(fd) + except OSError: + pass + +# daemonize `command' (list) with arguments, +# optionally change argv[0] to `alias', +# write child pid to file named `pidfile', +# fully daemonize or just background (fork) - `true_daemon', +# change effective+real user/group to `su' (list, user and group) +# (when true_daemon=True) redirect stdin/out/err to filenames +# specified in ioredir vector (list), +def daemonize(command, alias=None, pidfile=None, true_daemon=True, su=None, ioredir=None): + if not alias: + alias = command[0] + + if not true_daemon: + pid = os.fork() + if (pid != 0): + # parent, write pidfile and _exit, + # avoiding possible double cleanups/flushes + if pidfile: + file_write(pidfile, str(pid)) + os._exit(0) + else: + # child, simply execute + os.execvp(command[0],[alias]+command[1:]) + + else: + pid = os.fork() + if (pid != 0): + # parent, just exit, since final pid depends on the second fork + os._exit(0) + else: + os.setsid() + pid = os.fork() + if (pid != 0): + # parent of the second child + if pidfile: + file_write(pidfile, str(pid)) + os._exit(0) + else: + # second child, real daemon! + + os.chdir('/') + + # change real and effective uid/gid of a process + if su: + uid = getpwnam(su[0]).pw_uid + gid = getgrnam(su[1]).gr_gid + os.setgroups([]) + os.setregid(gid, gid) + os.setreuid(uid, uid) + + # pre-create possible in/out files with default umask, + # with original stderr (in case of errors), but with new uid/gid + if ioredir: + os.open(ioredir[0], os.O_RDWR) + os.open(ioredir[1], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666) + os.open(ioredir[2], os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0666) + + os.umask(0) + + close_all_fds() + if ioredir: + for ioport in ioredir: + os.open(ioport, os.O_RDWR) + else: + os.open(os.devnull, os.O_RDWR) + os.dup2(0,1) + os.dup2(0,2) + + # execute + os.execvp(command[0],[alias]+command[1:]) + + +# argument parsing +def error(msg): + print >> sys.stderr, "error: " + str(msg) + sys.exit(1) + +parser = OptionParser(usage='%prog [options] COMMAND') +parser.add_option('--alias', action='store', type='string', metavar='NAME', + dest='alias', help='specify custom argv[0]') +parser.add_option('--background', action='store_true', + dest='background', help='background (fork) only, nothing else') +parser.add_option('--su', action='store', type='string', metavar='USER:GROUP', + dest='su', help='run daemon under another user') +parser.add_option('--ioredir', action='store', type='string', metavar='IN,OUT,ERR', + dest='ioredir', help='redirect std{in,out,err} of the daemon to files') +parser.add_option('--pidfile', action='store', type='string', metavar='FILE', + dest='pidfile', help='write daemon pid to a file') + +(opts, args) = parser.parse_args() + +# additional parsing +if opts.su: + su = opts.su.split(':') +else: + su = None +if opts.ioredir: + ioredir = opts.ioredir.split(',') +else: + ioredir = None + +# sanity checks +if not args: + error("no COMMAND specified") +if len(args) > 1: + error("COMMAND can be only one argument, quote it") +if opts.su: + if len(su) != 2: + error("wrong --su argument specification") + for i in su: + if not i: + error("wrong --su argument specification") +if opts.ioredir: + if len(ioredir) != 3: + error("wrong --ioredir argument specification") + for i in ioredir: + if not i: + error("wrong --ioredir argument specification") + +# shell-expand the COMMAND into list +lex = shlex(args[0]) +lex.whitespace_split = True +args = list(lex) + +# input parsing finished +daemonize(args, alias=opts.alias, pidfile=opts.pidfile, + true_daemon=(not opts.background), su=su, ioredir=ioredir) diff --git a/src/python/journal-compare.py b/src/python/journal-compare.py new file mode 100755 index 0000000..1fc8915 --- /dev/null +++ b/src/python/journal-compare.py @@ -0,0 +1,198 @@ +#!/usr/bin/python + +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Petr Muller + +import xml.dom.minidom +import sys + +class Result: + def __init__(self): + self.name = "" + self.result = "" + self.messages = [] + + def addMessage(self, message): + self.messages.append(message) + + def canBePass(self): + if self.result == "": + self.result = "PASS" + def canBeWarn(self): + if self.result != "FAIL": + self.result = "WARN" + def isFail(self): + self.result = "FAIL" + +class Metric: + def __init__(self, name, value, type, tolerance): + self.value = value + self.type = type + self.tolerance = tolerance + self.name = name + + def compare(self, other): + if self.type == "low": + first = self.value + second = other.value + message = "First %s, second %s, toleranced first %s" % (first, second, first+first*tolerance) + else: + first = other.value + second = self.value + message = "First %s, second %s, toleranced first %s" % (second, first, second+second*tolerance) + + result = Result() + result.name = self.name + result.addMessage(message) + + if first >= second: + result.result = "PASS" + elif first+first*tolerance >= second: + result.result = "WARN" + else: + result.result = "FAIL" + return result + +class Test: + def __init__(self, name): + self.name = name + self.passes = 0 + self.failures = 0 + self.warnings = 0 + + def addResult(self, result): + if result == "PASS": + self.passes += 1 + elif result == "FAIL": + self.failures += 1 + elif result == "WARN": + self.warnings += 1 + + def compare(self, other): + result = Result() + result.name = self.name + if self.passes <= other.passes: + result.canBePass() + if 0 not in (self.passes, other.passes): + result.addMessage("PASSES OK (old %s, new %s)" % (self.passes, other.passes)) + else: + result.canBeWarn() + result.addMessage("PASSES NOT OK (old %s, new %s)" % (self.passes, other.passes)) + + if self.failures >= other.failures and other.failures == 0: + result.canBePass() + if 0 not in (self.failures, other.failures): + result.addMessage("FAILS OK (old %s, new %s)" % (self.failures, other.failures)) + elif self.failures >= other.failures: + result.canBeWarn() + if 0 not in (self.failures, other.failures): + result.addMessage("FAILS REMAINING (old %s, new %s)" % (self.failures, other.failures)) + elif self.failures < other.failures and self.passes > other.passes: + result.isFail() + result.addMessage("FAILS REGRESSION (old %s, new %s)" % (self.failures, other.failures)) + else: + result.isFail() + result.addMessage("FAILS NOT OK (old %s, new %s)" % (self.failures, other.failures)) + + if self.warnings >= other.warnings and other.warnings == 0: + result.canBePass() + if 0 not in (self.warnings, other.warnings): + result.addMessage("WARNINGS OK (old %s, new %s)" % (self.warnings, other.warnings)) + elif self.warnings >= other.warnings: + result.canBeWarn() + if 0 not in (self.warnings, other.warnings): + result.addMessage("WARNINGS REMAINING (old %s, new %s)" % (self.warnings, other.warnings)) + else: + result.isFail() + result.addMessage("WARNINGS NOT OK (old %s, new %s)" % (self.warnings, other.warnings)) + + return result + +class TestSet: + def __init__(self): + self.results = {} + + def addTestResult(self, name, result): + if not self.results.has_key(name): + self.results[name] = Test(name) + self.results[name].addResult(result) + + def compare(self, other): + result_list = [] + for key in self.results.keys(): + try: + result_list.append(self.results[key].compare(other.results[key])) + except KeyError: + print "[WARN] Could not find corresponding test for: %s" % key + return result_list + +try: + old = sys.argv[1] + new = sys.argv[2] +except IndexError: + old = "old/rcw-journal" + new = "new/rcw-journal" + +journal_old = xml.dom.minidom.parse(old) +journal_new = xml.dom.minidom.parse(new) + +old_log = journal_old.getElementsByTagName("log")[0] +new_log = journal_new.getElementsByTagName("log")[0] + +old_phases = old_log.getElementsByTagName("phase") +new_phases = new_log.getElementsByTagName("phase") + +walk_through = range(len(new_phases)) + +for i in walk_through: + old_type, old_name = old_phases[i].getAttribute("type"), old_phases[i].getAttribute("name") + new_type, new_name = new_phases[i].getAttribute("type"), new_phases[i].getAttribute("name") + + if old_type == new_type and old_name == new_name: + print "Types match, so we are comparing phase %s of type %s" % (old_type, new_type) + old_tests = TestSet() + new_tests = TestSet() + old_metrics = {} + new_metrics = {} + + for phases, results, metrics in ((old_phases, old_tests, old_metrics), (new_phases, new_tests, new_metrics)): + for test in phases[i].getElementsByTagName("test"): + key = test.getAttribute("message") + result = test.childNodes[0].data.strip() + results.addTestResult(key, result) + + for metric in phases[i].getElementsByTagName("metric"): + key = metric.getAttribute("name") + value = float(metric.childNodes[0].data.strip()) + tolerance = float(metric.getAttribute("tolerance")) + metrics[key] = Metric(key, value, metric.getAttribute("type"), tolerance) + + print "==== Actual compare ====" + print " * Metrics * " + metric_results = [] + for key in old_metrics.keys(): + metric_results.append(old_metrics[key].compare(new_metrics[key])) + for metric in metric_results: + for message in metric.messages: + print "[%s] %s (%s)" % (metric.result, metric.name, message) + print " * Tests * " + test_results = old_tests.compare(new_tests) + for test in test_results: + print "[%s] %s" % (test.result, test.name) + for message in test.messages: + print "\t - %s" % message + + else: + print "We are not doing any compare, types dont match" diff --git a/src/python/journalling.py b/src/python/journalling.py new file mode 100755 index 0000000..ed29882 --- /dev/null +++ b/src/python/journalling.py @@ -0,0 +1,315 @@ +#!/usr/bin/python + +# Authors: Jakub Heger +# Dalibor Pospisil +# Ales Zelinka +# +# Description: Translates Beakerlibs metafile into XML Journal +# +# Copyright (c) 2008 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + + +import sys +import os +import time +import re +from optparse import OptionParser +from lxml import etree +import shlex +import base64 + +# TODO fix xml pretty print + + +xmlForbidden = [0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0xFFFE, 0xFFFF] +xmlTrans = dict([(x, None) for x in xmlForbidden]) + + +class Stack: + def __init__(self): + self.items = [] + + def push(self, item): + self.items.append(item) + + def pop(self): + return self.items.pop() + + def peek(self): # Returns top element without popping it + return self.items[-1] + + +def saveJournal(journal, journal_path): + try: + output = open(journal_path, 'wb') + output.write(etree.tostring(journal, xml_declaration=True, encoding='utf-8', pretty_print=True)) + output.close() + return 0 + except IOError, e: + sys.stderr.write('Failed to save journal to %s: %s' % (journal_path, str(e))) + return 1 + + +# Adds attributes starttime and endtime to a element +def addStartEndTime(element, starttime, endtime): + element.set("starttime", starttime) + element.set("endtime", endtime) + # Removing timestamp from paired element (not needed as it has start/endtime) + # 'None' is to not raise an exception if attribute 'timestamp' does not exist + element.attrib.pop("timestamp", None) + return 0 + + +# Find first and last timestamp to fill in starttime and endtime elements of given element +def getStartEndTime(element): + starttime = "" + endtime = "" + for child in element.iter(): + if child.get("timestamp"): + if starttime == "": + starttime = child.get("timestamp") + endtime = child.get("timestamp") + + return starttime, endtime + + +# Parses and decodes lines given to it +# Returns number of spaces before element, name of the element, +# its attributes in a dictionary, and content of the element +def parseLine(line): + TIME_FORMAT = "%Y-%m-%d %H:%M:%S %Z" + CONTENT_FLAG = 0 + attributes = {} + content = "" + + # Stripping comments + line = line.split('#')[0] + # Count number of leading spaces + indent = len(line) - len(line.lstrip()) + + # using shlex to get rid of the quotes + splitted = shlex.split(line) + + # if the line is not empty + if splitted: + # if first 2 characters are '-', it is not new element, but ending of pair element + if splitted[0][0] == '-' and splitted[0][1] == '-': + element = "" + else: + element = splitted[0] + # else it is ending line + else: + return 0, "", {}, "" + + # parsing the rest of the line + for part in splitted: + # if flag is set, string is an elements content + if CONTENT_FLAG == 1: + content = base64.b64decode(part) + # end parsing after content is stored + break + # test if string is an elements content indicator + if part == '--': + CONTENT_FLAG = 1 + continue + # test if string is an elements time attribute + if re.match(r'^--timestamp=', part): + attribute_name = "timestamp" + attribute_value = part.split('=', 1)[1] + attributes[attribute_name] = time.strftime(TIME_FORMAT, time.localtime(int(attribute_value))) + continue + # test if string is an elements regular attribute + if re.match(r'^--[a-zA-Z0-9]+=', part): + attribute_name = part.split('=', 1)[0][2:] + attribute_value = part.split('=', 1)[1] + attributes[attribute_name] = base64.b64decode(attribute_value) + continue + + return indent, element, attributes, content + + +# Returns xml element created with +# information given as parameters +def createElement(element, attributes, content): + element = unicode(element, 'utf-8', errors='replace').translate(xmlTrans) + new_el = etree.Element(element) + + content = unicode(content, 'utf-8', errors='replace').translate(xmlTrans) + new_el.text = content + + for key, value in attributes.iteritems(): + key = unicode(key, 'utf-8', errors='replace').translate(xmlTrans) + value = unicode(value, 'utf-8', errors='replace').translate(xmlTrans) + new_el.set(key, value) + return new_el + + +# Main loop of the program +# Reads metafile or stdin line by line and adds +# information from them into XML document +def createJournalXML(options): + # If --metafile option is used read from it, else read standard input + if options.metafile: + try: + fh = open(options.metafile, 'r+') + except IOError, e: + sys.stderr.write('Failed to open queue file with' + str(e), 'FAIL') + return 1 + + lines = fh.readlines() + fh.close() + else: + lines = sys.stdin.readlines() + + # Indent level of previous line, initialized to -1 + old_indent = -1 + # Initialize root element + previous_el = etree.Element("BEAKER_TEST") + journal = previous_el + # Stack of elements + el_stack = Stack() + + # Main loop, going through lines of metafile, adding elements + for line in lines: + indent, element, attributes, content = parseLine(line) + # Empty line is ignored + if element == "" and attributes == {}: + continue + + if indent > old_indent: + # Creating new element + new_el = createElement(element, attributes, content) + # Putting previous element to the top of the stack + el_stack.push(previous_el) + # New element is now current element + previous_el = new_el + + elif indent == old_indent: + # Closing element with updates to it with no elements inside it + # TODO refactor + if element == "": + # Updating start and end time + starttime, endtime = getStartEndTime(previous_el) + # If the closing element has a --timestamp, this value will be used as endtime + if "timestamp" in attributes: + endtime = attributes["timestamp"] + # Updating attributes found on closing line + for key, value in attributes.iteritems(): + previous_el.set(key, value) + # add start/end time and remove timestamp attribute + addStartEndTime(previous_el, starttime, endtime) + # New element is on the same level as previous one + else: + # Previous element has ended so it is appended to the element 1 level above + el_stack.peek().append(previous_el) + # Creating new element + new_el = createElement(element, attributes, content) + # New element is now current element + previous_el = new_el + + # New element is on higher level than previous one + elif indent < old_indent: + # Difference between indent levels = how many paired elements will be closed + indent_diff = old_indent - indent + for _ in xrange(indent_diff): + el_stack.peek().append(previous_el) + previous_el = el_stack.pop() + + # Closing element with updates to it + if element == "" and attributes != {}: + # Updating start and end time + starttime, endtime = getStartEndTime(previous_el) + # If the closing element has a --timestamp, this value will be used as endtime + if "timestamp" in attributes: + endtime = attributes["timestamp"] + # Updating attributes found on closing line + for key, value in attributes.iteritems(): + previous_el.set(key, value) + # add start/end time and remove timestamp attribute + addStartEndTime(previous_el, starttime, endtime) + + # Ending paired element and creating new one on the same level as the paired one that just ended + elif element != "": + # Updating start and end time + starttime, endtime = getStartEndTime(previous_el) + addStartEndTime(previous_el, starttime, endtime) + # Appending previous element to the element 1 level above + if el_stack.items: + el_stack.peek().append(previous_el) + + new_el = createElement(element, attributes, content) + previous_el = new_el + + # Changing indent level to new value + old_indent = indent + + # Final appending + for _ in el_stack.items: + el_stack.peek().append(previous_el) + previous_el = el_stack.pop() + if el_stack.items: + el_stack.peek().append(previous_el) + + # Updating start and end time of last opened paired element(log) + starttime, endtime = getStartEndTime(previous_el) + addStartEndTime(previous_el, starttime, endtime) + + # Updating start/end time of the whole test + starttime, endtime = getStartEndTime(journal) + journal.xpath("starttime")[0].text = starttime + journal.xpath("endtime")[0].text = endtime + + # XSL transformation + try: + if options.xslt: + xslt = etree.parse(options.xslt) + transform = etree.XSLT(xslt) + journal = transform(journal) + except etree.LxmlError: + sys.stderr.write("\nTransformation template file " + options.xslt + + " could not be parsed.\nAborting journal creation.") + return 1 + + if options.journal: + # Save journal to a file and return its exit code + return saveJournal(journal, options.journal) + else: + # Write the XML on standard output + return sys.stdout.write(etree.tostring(journal, xml_declaration=True, encoding='utf-8', pretty_print=True)) + + +def main(): + DESCRIPTION = "Tool creating journal out of metafile." + usage = __file__ + " --metafile=METAFILE --journal=JOURNAL" + optparser = OptionParser(description=DESCRIPTION, usage=usage) + + optparser.add_option("-j", "--journal", default=None, dest="journal", metavar="JOURNAL") + optparser.add_option("-m", "--metafile", default=None, dest="metafile", metavar="METAFILE") + optparser.add_option("-x", "--xslt", default=None, dest="xslt", metavar="XSLT") + + (options, args) = optparser.parse_args() + + # If metafile option is used, check if the value exists + if options.metafile and not os.path.exists(options.metafile): + sys.stderr.write("Metafile " + options.metafile + " does not exist.\nExiting unsuccessfully.\n") + exit(1) + + # Create journal + return createJournalXML(options) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/python/rlMemAvg.py b/src/python/rlMemAvg.py new file mode 100755 index 0000000..5f459e0 --- /dev/null +++ b/src/python/rlMemAvg.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +# Authors: Petr Muller +# +# Description: Prints memory consumption average for an executed program +# +# Copyright (c) 2008 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + +import sys, time, re + +use_sub = False +use_popen = False + +try: + import subprocess + use_sub = True +except ImportError: + import popen2 + use_popen = True + +if len(sys.argv) < 2: + print 'syntax: rlMemAvg ' + sys.exit(1) + +proglist = sys.argv[1:] + +if use_sub: + task = subprocess.Popen(proglist) +elif use_popen: + task = popen2.Popen3(" ".join(proglist)) + +memsum = 0 +tick = 0 +fn = '/proc/%d/status' % task.pid +mre = re.compile(r'VmRSS:[ \t]+(?P\d+)') + +while True: + for line in open(fn, 'r').readlines(): + m = mre.search(line) + if m: + mem = int(m.group('mem')) + memsum += mem + tick += 1 + break + time.sleep(0.1) + finish = task.poll() + if (use_sub and finish != None) or (use_popen and finish != -1): + break + +print "%d" % (memsum/tick) diff --git a/src/python/rlMemPeak.py b/src/python/rlMemPeak.py new file mode 100755 index 0000000..6228210 --- /dev/null +++ b/src/python/rlMemPeak.py @@ -0,0 +1,60 @@ +#!/usr/bin/python + +# Authors: Petr Muller +# +# Description: Prints a memory consumption peak of an executed program +# +# Copyright (c) 2008 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + +import sys, time, re + +use_sub = False +use_popen = False + +try: + import subprocess + use_sub = True +except ImportError: + import popen2 + use_popen = True + +if len(sys.argv) < 2: + print 'syntax: rlMemPeak ' + sys.exit(1) + +proglist = sys.argv[1:] + +if use_sub: + task = subprocess.Popen(proglist) +elif use_popen: + task = popen2.Popen3(" ".join(proglist)) + +maxmem = 0 +fn = '/proc/%d/status' % task.pid +mre = re.compile(r'VmRSS:[ \t]+(?P\d+)') + +while True: + for line in open(fn, 'r').readlines(): + m = mre.search(line) + if m: + mem = int(m.group('mem')) + maxmem = max(mem, maxmem) + break + time.sleep(0.1) + finish = task.poll() + if (use_sub and finish != None) or (use_popen and finish != -1): + break + +print "%d" % (maxmem) diff --git a/src/python/testwatcher.py b/src/python/testwatcher.py new file mode 100755 index 0000000..8d716c7 --- /dev/null +++ b/src/python/testwatcher.py @@ -0,0 +1,324 @@ +#!/usr/bin/python -u +# +# Authors: Jiri Jaburek +# +# Description: Test watching wrapper for runtest.sh or similar runnable +# +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + +# +# test watcher - an idea +# +# LWD = Beaker Local Watchdog, expires when TestTime (Makefile) reaches zero +# EWD = Beaker External Watchdog, expires 30m after LWD expiration +# +# - set up a temporary file usable for cleanup file path transfer +# and export its path via an env variable +# - hook LWD when run from Beaker +# - this hook will send SIGHUP to the watcher on LWD expire and block +# until the watcher process exits +# - set up SIGHUP handling, which +# - sends SIGKILL to test if running +# - sets up SIGALRM handler for EWD and schedules alarm(2) +# - EWD handler SIGKILLs cleanup (if running) +# - run test +# - if it finishes in time, do nothing (unset pid) +# - if INT is received while it is running, SIGKILL the test, unset pid +# - execute possible cleanup +# - if it still finishes in time (no HUP so far), do nothing (unset pid) +# - if INT is received while it is running, SIGKILL cleanup, unset pid +# - exit cleanly +# +# Some considerations taken into account / tested: +# +# - SIGHUP is received while running cleanup (TestTime expired after test exit) +# - testpid is already 0, only SIGALRM (cleanup kill) is scheduled, giving +# the cleanup another ewd_maxsecs seconds to finish +# - SIGTERM is received at any time +# - the only reasonable case is system reboot/poweroff, which we cannot +# delay anyway (to allow cleanup execution), so just exit (SIG_DFL) +# - this doesn't really damage anything, the test can be easily re-run and +# continue creating the cleanup, if the previous cleanup state was saved +# and the test sends the cleanup path to the watcher again + + +import os +import sys +import signal +import time +import errno +import fcntl +import tempfile + + +### CONFIG +# +# Beaker External watchdog = 30 minutes after LWD +# (25 minutes = 1500 secs by default, configurable via env) +if 'TESTWATCHER_EWD_SECS' in os.environ: + ewd_maxsecs = int(os.environ['TESTWATCHER_EWD_SECS']) + if ewd_maxsecs <= 0: + raise Exception("invalid TESTWATCHER_EWD_SECS env var value") +else: + ewd_maxsecs = 1500 + +# beah LWD hook +lwd_guard_file = '/usr/share/rhts/hooks/watchdog/testwatcher-cleanup-guard' + +# file descriptor and file path (name) used for cleanup filename transfer +# via temporary file from test to watcher, the watcher expects the test +# to write path to cleanup executable into it, it's checked just before +# cleanup execution +clfd, clpath = tempfile.mkstemp(prefix='testwatcher-', dir='/var/tmp') # no-reboot +# env var containing the path, so the test can write to it +os.environ['TESTWATCHER_CLPATH'] = clpath +# +### + +### GLOBALS +# +selfname = os.path.basename(__file__) + +testpid = 0 +cleanuppid = 0 + +if os.environ.get('TASKID'): + beah = True +else: + beah = False +# +### + + +### HELPERS +# +def debug(msg): + print 'TESTWATCHER: '+msg + sys.stdout.flush() + + +def fatal(msg): + print >> sys.stderr, 'TESTWATCHER fatal: '+msg + sys.stderr.flush() + sys.exit(1) + + +def sigpgkill_safe(pid): + # if pid does not exist / is not related, return + try: + os.kill(pid, 0) + except: + return + os.killpg(pid, signal.SIGKILL) + + +def beah_warn(part): + # python "subprocess" not on RHEL4 + os.system('rhts-report-result "TESTWATCHER ('+part+')" WARN /dev/null') +# +### + +### BEAH LWD WATCHDOG +# +# custom shell-based watchdog guard +# (selfname[:15] works around 15-char /proc/pid/comm limitation) +watchdog_guard_cont = r""" +#!/bin/sh +rm -f "$0" +wrap_pid='"""+str(os.getpid())+r"""' +wrap_name="$(ps c --no-headers -o comm --pid $wrap_pid)" +[ $? -ne 0 ] && { echo "wrapper pid is not running"; exit 0; } +[ "$wrap_name" != '"""+selfname[:15]+r"""' ] && \ + { echo "wrapper pid not a testwatch process: $wrap_name"; exit 0; } +kill -HUP "$wrap_pid" +while ps --no-headers -o pid --pid $wrap_pid >/dev/null; do sleep 1; done; +""" + + +# write out custom watchdog guard to beaker hooks dir, +# causing it to be launched when local watchdog expires +def beah_lwd_hook(): + debug('hooking beah LWD') + try: + os.makedirs(os.path.dirname(lwd_guard_file)) + except OSError, e: + if e.errno == errno.EEXIST: + pass + f = open(lwd_guard_file, 'w') + f.write(watchdog_guard_cont) + f.close() + os.chmod(lwd_guard_file, 0755) + + +# called when EWD (external watchdog) is about to expire +def beah_ewd_action(signum, frame): + debug('beah EWD is about to strike') + global cleanuppid + if cleanuppid != 0: + sigpgkill_safe(cleanuppid) + if beah: + beah_warn('external watchdog') + + +# called when LWD expires +def beah_lwd_action(signum, frame): + debug('beah LWD expired') + global testpid + signal.signal(signal.SIGHUP, signal.SIG_IGN) + if testpid != 0: + sigpgkill_safe(testpid) + testpid = 0 + signal.signal(signal.SIGALRM, beah_ewd_action) + signal.alarm(ewd_maxsecs) + if beah: + beah_warn('local watchdog') +# +### + + +### CLEANUP WATCHER +# +# executed by INT sent to the test watcher process +def cleanup_interrupt(signum, frame): + debug('cleanup interrupted') + global cleanuppid + + signal.signal(signal.SIGINT, signal.SIG_IGN) + + if cleanuppid != 0: + sigpgkill_safe(cleanuppid) + + if beah: + beah_warn('cleanup interrupt') + + +def exec_cleanup(): + global cleanuppid + + # no os.SEEK_SET on RHEL4 + os.lseek(clfd, 0, 0) + + filename = os.read(clfd, 1024).strip() + + # no cleanup + if not filename: + debug('no cleanup set') + return + + if not os.path.isfile(filename) or not os.access(filename, os.X_OK): + debug('cleanup file not found / not executable, skipping') + return + + signal.signal(signal.SIGINT, cleanup_interrupt) + + cleanuppid = os.fork() + if cleanuppid == 0: + os.setpgrp() + debug('executing cleanup at '+filename) + os.execvp(filename, [filename]) + else: + debug('parent waiting for cleanup '+str(cleanuppid)) + while cleanuppid != 0: + try: + os.waitpid(cleanuppid, 0) + cleanuppid = 0 + except OSError, e: + if e.errno == errno.EINTR: + pass + if e.errno == errno.ECHILD: + cleanuppid = 0 +# +### + + +### TEST WATCHER +# +# executed by INT sent to the test watcher process +def test_interrupt(signum, frame): + debug('test interrupted') + global testpid + + # ignore future INT + signal.signal(signal.SIGINT, signal.SIG_IGN) + + # kill frozen test + its process group + if testpid != 0: + sigpgkill_safe(testpid) + + # log warn + if beah: + beah_warn('test interrupt') + + +def exec_test(): + # NOTE: signal handling can be set up before fork, it won't make it + # past execve/execvp and it's safer this way + # (the child can be killed via SIGINT received right after pid is available + # to the parent, ie. right after fork) + + # beaker LWD + signal.signal(signal.SIGHUP, beah_lwd_action) + # user interrupt + signal.signal(signal.SIGINT, test_interrupt) + + # fork and exec the test, wait for it in the parent process + global testpid + testpid = os.fork() + if testpid == 0: + # become process group leader, so we can kill all related + # processes (from the parent) when interrupted + os.setpgrp() + + debug('executing test at '+' '.join(sys.argv[1:])) + os.execvp(sys.argv[1], sys.argv[1:]) + + else: + debug('parent waiting for test '+str(testpid)) + while testpid != 0: + try: + # wait for entire process group + os.waitpid(testpid, 0) + testpid = 0 + except OSError, e: + # no traceback if interrupted by a signal + if e.errno == errno.EINTR: + pass + # safety measure, shouldn't happen + if e.errno == errno.ECHILD: + testpid = 0 +# +### + + +### MAIN +# +# sanity check +if len(sys.argv) < 2: + fatal('usage: '+selfname+' [args]') + +if beah: + beah_lwd_hook() + +exec_test() +debug('parent done waiting for test') + +exec_cleanup() +debug('parent done waiting for cleanup') + +# remove temporary (mkstemp'ed) file # no-reboot +os.unlink(clpath) + +debug('all done, finishing watcher') +sys.exit(0) diff --git a/src/rpms.sh b/src/rpms.sh new file mode 100644 index 0000000..e22cecb --- /dev/null +++ b/src/rpms.sh @@ -0,0 +1,1132 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: rpms.sh - part of the BeakerLib project +# Description: Package manipulation helpers +# +# Author: Petr Muller +# Author: Jan Hutar +# Author: Petr Splichal +# Author: Ales Zelinka +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - rpms - Package manipulation helpers + +=head1 DESCRIPTION + +Functions in this BeakerLib script are used for RPM manipulation. + +=head1 FUNCTIONS + +=cut + +. $BEAKERLIB/testing.sh +. $BEAKERLIB/infrastructure.sh + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__INTERNAL_RpmPresent() { + local assert=$1 + local name=$2 + local version=$3 + local release=$4 + local arch=$5 + + local package=$name-$version-$release.$arch + [ "$arch" == "" ] && package=$name-$version-$release + [ "$release" == "" ] && package=$name-$version + [ "$version" == "" ] && package=$name + + export __INTERNAL_RPM_ASSERTED_PACKAGES="$__INTERNAL_RPM_ASSERTED_PACKAGES $name" + rljRpmLog "$name" + + if [ -n "$package" ]; then + rpm -q $package + local status=$? + else + local status=100 + fi + + if [ "$assert" == "assert" ] ; then + __INTERNAL_ConditionalAssert "Checking for the presence of $package rpm" $status + elif [ "$assert" == "assert_inverted" ] ; then + if [ $status -eq 1 ]; then + status=0 + elif [ $status -eq 0 ]; then + status=1 + fi + __INTERNAL_ConditionalAssert "Checking for the non-presence of $package rpm" $status + elif [ $status -eq 0 ] ; then + rlLog "Package $package is present" + else + rlLog "Package $package is not present" + fi + + if rpm -q --quiet $package + then + rlLog "Package versions:" + rpm -q --qf '%{name}-%{version}-%{release}.%{arch}\n' $package | while read line + do + rlLog " $line" + done + fi + + return $status +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCheckRpm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Rpm Handling + +=head3 rlCheckRpm + +Check whether a package is installed. + + rlCheckRpm name [version] [release] [arch] + +=over + +=item name + +Package name like C + +=item version + +Package version like C<2.6.25.6> + +=item release + +Package release like C<55.fc9> + +=item arch + +Package architucture like C + +=back + +Returns 0 if the specified package is installed. + +=cut + +rlCheckRpm() { + __INTERNAL_RpmPresent noassert $1 $2 $3 $4 +} + +# backward compatibility +rlRpmPresent() { + rlLogWarning "rlRpmPresent is obsoleted by rlCheckRpm" + __INTERNAL_RpmPresent noassert $1 $2 $3 $4 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertRpm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertRpm + +Assertion making sure that a package is installed. + + rlAssertRpm name [version] [release] [arch]> + rlAssertRpm --all + +=over + +=item name + +Package name like C + +=item version + +Package version like C<2.6.25.6> + +=item release + +Package release like C<55.fc9> + +=item arch + +Package architucture like C + +=item --all + +Assert all packages listed in the $PACKAGES $REQUIRES and $COLLECTIONS +environment variables. + +=back + +Returns 0 and asserts PASS if the specified package is installed. + +=cut + +rlAssertRpm() { + local package packages + if [ "$1" = "--all" ] ; then + packages="$PACKAGES $REQUIRES $COLLECTIONS" + if [ "$packages" = " " ] ; then + __INTERNAL_ConditionalAssert "rlAssertRpm: No package provided" 1 + return + fi + for package in $packages ; do + rlAssertRpm $package + done + else + __INTERNAL_RpmPresent assert $1 $2 $3 $4 + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertNotRpm +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertNotRpm + +Assertion making sure that a package is not installed. This is +just inverse of C. + + rlAssertNotRpm name [version] [release] [arch]> + +=over + +=item name + +Package name like C + +=item version + +Package version like C<2.6.25.6> + +=item release + +Package release like C<55.fc9> + +=item arch + +Package architucture like C + +=back + +Returns 0 and asserts PASS if the specified package is not installed. + +=head3 Example + +Function C is useful especially in prepare phase +where it causes abort if a package is missing, while +C is handy when doing something like: + + if ! rlCheckRpm package; then + yum install package + rlAssertRpm package + fi + +=cut + +rlAssertNotRpm() { + __INTERNAL_RpmPresent assert_inverted $1 $2 $3 $4 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertBinaryOrigin +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertBinaryOrigin + +Assertion making sure that given binary is owned by (one of) the given +package(s). + + rlAssertBinaryOrigin binary package [package2 [...]] + PACKAGES=... rlAssertBinaryOrigin binary + +=over + +=item binary + +Binary name like C or C + +=item package + +List of packages like C. The parameter is optional. If missing, +contents of environment variable $PACKAGES are taken into account. + +=back + +Returns 0 and asserts PASS if the specified binary belongs to (one of) the given package(s). +Returns 1 and asserts FAIL if the specified binary does not belong to (any of) the given package(s). +Returns 2 and asserts FAIL if the specified binary is not found. +Returns 3 and asserts FAIL if no packages are given. +Returns 100 and asserts FAIL if invoked with no parameters. + +=head3 Example + +Function C is useful especially in prepare phase +where it causes abort if a binary is missing or is owned by different package: + + PACKAGES=mksh rlAssertBinaryOrigin ksh + or + rlAssertBinaryOrigin ksh mksh + +Returns true if ksh is owned by the mksh package (in this case: /bin/ksh is a +symlink pointing to /bin/mksh). + +=cut + +rlAssertBinaryOrigin() { + # without parameters, exit immediatelly + local status CMD FULL_CMD + status=0 + [ $# -eq 0 ] && { + status=100 + rlLogError "rlAssertBinaryOrigin called without parameters" + __INTERNAL_ConditionalAssert "rlAssertBinaryOrigin: No binary given" $status + return $status + } + + CMD=$1 + shift + + # if not given explicitly as param, take PKGS from $PACKAGES + local PKGS=$@ + [ $# -eq 0 ] && PKGS=$PACKAGES + if [ -z "$PKGS" ]; then + status=3 + __INTERNAL_ConditionalAssert "rlAssertBinaryOrigin: No packages given" $status + return $status + fi + + status=2 + FULL_CMD=$(which $CMD) &>/dev/null && \ + { + status=1 + # expand symlinks (if any) + local BINARY=$(readlink -f $FULL_CMD) + + # get the rpm owning the binary + local BINARY_RPM=$(rpm -qf --qf="%{name}\n" $BINARY | uniq) + + rlLogDebug "Binary rpm: $BINARY_RPM" + + local TESTED_RPM + for TESTED_RPM in $PKGS ; do + if [ "$TESTED_RPM" = "$BINARY_RPM" ] ; then + status=0 + echo $BINARY_RPM + break + fi + done + } + + [ $status -eq 2 ] && rlLogError "$CMD: command not found" + [ $status -eq 1 ] && rlLogError "$BINARY belongs to $BINARY_RPM" + + __INTERNAL_ConditionalAssert "Binary '$CMD' should belong to: $PKGS" $status + return $status +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlGetMakefileRequires +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlGetMakefileRequires + +Prints comma separated list of requirements defined in Makefile using 'Requires' +attribute. + +Return 0 if success. + +=cut + +rlGetMakefileRequires() { + [[ -s ./Makefile ]] || { + rlLogError "Could not find ./Makefile or the file is empty" + return 1 + } + grep '"Requires:' Makefile | sed -e 's/.*Requires: *\(.*\)".*/\1/' | tr ' ' '\n' | sort | uniq | tr '\n' ' ' | sed -r 's/^ +//;s/ +$//;s/ +/ /g' + return 0 +}; # end of rlGetMakefileRequires + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCheckRequirements +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlCheckRequirements + +Check that all given requirements are covered eigther by installed package or by +binary available in PATHs or by some package's provides. + + rlRun "rlCheckRequirements REQ..." + +=over + +=item REQ + +Requirement to be checked. It can be package name, provides string or binary +name. + +=back + +Returns number of unsatisfied requirements. + +=cut +#' + +rlCheckRequirements() { + local req res=0 package binary provides LOG=() LOG2 l=0 ll + for req in "$@"; do + package="$(rpm -q "$req" 2> /dev/null)" + if [[ $? -eq 0 ]]; then + LOG=("${LOG[@]}" "$package" "covers requirement '$req'") + rljRpmLog "$package" + else + binary="$(which "$req" 2> /dev/null)" + if [[ $? -eq 0 ]]; then + package="$(rpm -qf "$binary")" + LOG=("${LOG[@]}" "$package" "covers requirement '$req' by binary '$binary' from package '$package'") + rljRpmLog "$package" + else + package="$(rpm -q --whatprovides "$req" 2> /dev/null)" + if [[ $? -eq 0 ]]; then + LOG=("${LOG[@]}" "$package" "covers requirement '$req'") + rljRpmLog "$package" + else + rlLogWarning "requirement '$req' not satisfied" + let res++ + fi + fi + fi + done + LOG2=("${LOG[@]}") + while [[ ${#LOG2[@]} -gt 0 ]]; do + [[ ${#LOG2} -gt $l ]] && l=${#LOG2} + LOG2=("${LOG2[@]:2}") + done + local spaces='' + for ll in `seq $l`; do spaces="$spaces "; done + while [[ ${#LOG[@]} -gt 0 ]]; do + let ll=$l-${#LOG}+1 + rlLog "package '$LOG' ${spaces:0:$ll} ${LOG[1]}" + LOG=("${LOG[@]:2}") + done + return $res +}; # end of rlCheckRequirements + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCheckMakefileRequires +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlCheckMakefileRequires + +This is just a bit smarted wrapper of + +C + +=head3 Example + + rlRun "rlCheckMakefileRequires" + +Return 255 if requirements could not be retrieved, 0 if all requirements are +satisfied or number of unsatisfied requirements. + + + +=cut + +rlCheckMakefileRequires() { + local req + req="$(rlGetMakefileRequires)" || return 255 + eval rlCheckRequirements $req +}; # end of rlCheckMakefileRequires + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertRequired +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlAssertRequired + +Ensures that all Requires, specified in beakerlib-style beaker-wizard layout +Makefile, are installed. + + rlAssertRequired + +Prints out a verbose list of installed/missing packages during operation. + +Returns 0 if all required packages are installed, 1 if one +or more packages are missing or if no Makefile is present. + +=cut + +rlAssertRequired(){ + local MAKEFILE="Makefile" + + if [ ! -e "$MAKEFILE" ]; then + rlLogError "rlAssertRequired: $MAKEFILE not present" + return 1 + fi + + list="$(grep 'Requires:' $MAKEFILE)" + if [ -z "$list" ]; then + rlLogError "rlAssertRequired: $MAKEFILE does not contain 'Requires:'" + return 1 + fi + + list=$(echo "$list" | sed -r 's/^[ \t]+@echo "Requires:[ \t]+([^"]+)" >> \$\(METADATA\)$/\1/' | tr '\n' ' ') + + rpm -q $list + + __INTERNAL_ConditionalAssert "Assert required packages installed" $? + + return $? +} + + +: <<=cut +=pod + +=head2 Getting RPMs + +=head3 Download methods + +Functions handling rpm downloading/installing can use more methods for actual +download of the rpm. + +Currently there are two download methonds available: + +=over + +=item direct + +Use use dirct download from build system (brew). + +=item yum + +Use yumdownloader. + +=back + +The methods and their order are defined by BEAKERLIB_RPM_DOWNLOAD_METHODS +variable as space separated list. By default it is 'direct yum'. This can be +overridden by user. +There may be done also additions or changes to the original value, +e.g. BEAKERLIB_RPM_DOWNLOAD_METHODS='yum ${BEAKERLIB_RPM_DOWNLOAD_METHODS/yum/}' + +=head3 + +Beakerlib is prepared for more Koji-based sources of packages while usigng +direct download method. By default packages are fetched from Koji, particularly +from https://kojipkgs.fedoraproject.org/packages. + +=cut + + +# return information of the first matching package +# $1 - method (rpm | repoquery) +# $2 - packages (NVR) +__INTERNAL_rpmGetPackageInfo() { + local package_info package_info_raw res=0 + rlLogDebug "${FUNCNAME}(): getting package info for '$2'" + package_info_raw="$($1 --qf "%{name} %{version} %{release} %{arch} %{sourcerpm}\n" -q "$2")" + [[ $? -ne 0 || -z "$package_info_raw" ]] && { + rlLogDebug "${FUNCNAME}(): package_info_raw: '$package_info_raw'" + rlLogInfo "rpm '$2' not available using '$1' tool" + let res++ + } + if [[ $res -eq 0 ]]; then + rlLogDebug "${FUNCNAME}(): package_info_raw: '$package_info_raw'" + # get first package + local tmp=( $(echo "$package_info_raw" | head -n 1 ) ) + # extract component name from source_rpm + package_info="${tmp[@]:0:4} $(__INTERNAL_getNVRA "${tmp[4]/.rpm}")" || { + rlLogError "parsing package info '$package_info_raw' failed" + let res++ + } + rlLogDebug "${FUNCNAME}(): package_info: '$package_info'" + rlLogDebug "got rpm info for '$2' via '$1'" + echo "$package_info" + fi + rlLogDebug "${FUNCNAME}(): returning $res" + return $res +} + + +BEAKERLIB_rpm_fetch_base_url=( "https://kojipkgs.fedoraproject.org/packages" ) +BEAKERLIB_rpm_packageinfo_base_url=( "http://koji.fedoraproject.org/koji" ) + +# generate combinations for various methods and parameters of finding the package +__INTERNAL_rpmInitUrl() { + local i j k + + rlLogDebug "${FUNCNAME}(): preparing download variants" + + __INTERNAL_rpmGetNextUrl_phase=() + for k in rpm repoquery; do + for j in nvr n; do + for i in "${BEAKERLIB_rpm_fetch_base_url[@]}"; do + __INTERNAL_rpmGetNextUrl_phase+=( "$k" ); # tool + __INTERNAL_rpmGetNextUrl_phase+=( "$j" ); # package_spec + __INTERNAL_rpmGetNextUrl_phase+=( "$i" ); # base_url + done + done + done + for j in koji; do + for i in "${BEAKERLIB_rpm_packageinfo_base_url[@]}"; do + __INTERNAL_rpmGetNextUrl_phase+=( "$j" ) ; # tool + __INTERNAL_rpmGetNextUrl_phase+=( "nvra.rpm" ) ; # package_spec + __INTERNAL_rpmGetNextUrl_phase+=( "$i" ) ; # base_url + done + done + rlLogDebug "${FUNCNAME}(): $(set | grep ^__INTERNAL_rpmGetNextUrl_phase=)" +} + + +__INTERNAL_WGET="wget -t 3 -T 180 -w 20 --waitretry=30 --no-check-certificate --progress=dot:giga" + +# __INTERNAL_rpmGetNextUrl N V R A | --source N V R +__INTERNAL_rpmGetNextUrl() { + local source='' + [[ "$1" == "--source" ]] && { + source="$1" + shift + } + local N=$1 V=$2 R=$3 A=$4 nil res url + rlLogDebug "${FUNCNAME}(): process $N-$V-$R.$A" + while [[ -n "$__INTERNAL_rpmGetNextUrl_phase" ]]; do + res=0 + local tool=${__INTERNAL_rpmGetNextUrl_phase[0]} + local package_spec=${__INTERNAL_rpmGetNextUrl_phase[1]} + local base_url=${__INTERNAL_rpmGetNextUrl_phase[2]} + #rlLogDebug "${FUNCNAME}(): $(set | grep ^__INTERNAL_rpmGetNextUrl_phase=)" + rlLogDebug "${FUNCNAME}(): remove first three indices of __INTERNAL_rpmGetNextUrl_phase" + __INTERNAL_rpmGetNextUrl_phase=( "${__INTERNAL_rpmGetNextUrl_phase[@]:3}" ) + rlLogDebug "trying tool $tool with $package_spec" + case $tool,$package_spec in + *,nvr) + IFS=' ' read nil nil nil nil CN CV CR CA < <(__INTERNAL_rpmGetPackageInfo $tool "$N-$V-$R") + res=$? + if [[ -n "$source" ]]; then + url="$base_url/$CN/$CV/$CR/src/$CN-$CV-$CR.src.rpm" + else + url="$base_url/$CN/$CV/$CR/$A/$N-$V-$R.$A.rpm" + fi + ;; + *,n) + IFS=' ' read nil nil nil nil CN CV CR CA < <(__INTERNAL_rpmGetPackageInfo $tool "$N") + res=$? + if [[ -n "$source" ]]; then + url="$base_url/$CN/$V/$R/src/$CN-$V-$R.src.rpm" + else + url="$base_url/$CN/$V/$R/$A/$N-$V-$R.$A.rpm" + fi + ;; + koji,nvra.rpm) + rlLogDebug "$FUNCNAME(): get rpm info" + local rpm_info=$($__INTERNAL_WGET -O - "$base_url/search?match=exact&type=rpm&terms=$N-$V-$R.$A.rpm") + [[ $? -ne 0 || -z "$rpm_info" ]] && { + rlLogError "could not download rpm information" + let res++ + continue + } + #[[ "$DEBUG" ]] && rlLogDebug "rpm_info='$rpm_info'" + local buildurl=$(echo "$rpm_info" | grep Version | grep -o '[^"]*buildID=[^"]*') + [[ $? -ne 0 || -z "$buildurl" ]] && { + rlLogError "could not find buildID" + let res++ + continue + } + rlLogDebug "$FUNCNAME(): extracted buildurl='$buildurl'" + [[ "$buildurl" =~ http ]] || buildurl="$base_url/$buildurl" + rlLogDebug "$FUNCNAME(): using buildurl='$buildurl'" + local buildinfo=$($__INTERNAL_WGET -O - "$buildurl") + [[ $? -ne 0 || -z "$buildinfo" ]] && { + rlLogError "could not download build information" + let res++ + continue + } + #[[ -n "$DEBUG" ]] && rlLogDebug "buildinfo='$buildinfo'" + if [[ -n "$source" ]]; then + url=$(echo "$buildinfo" | grep download | grep -o 'http[^"]*.src.rpm') + else + url=$(echo "$buildinfo" | grep download | grep -o "http[^\"]*/$N-$V-$R.$A.rpm") + fi + [[ $? -ne 0 ]] && { + rlLogError "could not find package url" + let res++ + continue + } + ;; + *) + rlLogDebug "$FUNCNAME(): unknown case" + rlLogError "there's a bug in the code, unknown case!" + let res++ + break + esac + [[ $res -eq 0 ]] && break + done + + [[ -z "$url" ]] && { + rlLogError "could not find package url" + let res++ + } + + rlLogDebug "$FUNCNAME(): using url='$url'" + + [[ $res -eq 0 ]] && __INTERNAL_RETURN_VALUE="$url" + + rlLogDebug "${FUNCNAME}(): returning $res" + return $res +} + + +__INTERNAL_getNVRA() { + rlLogDebug "${FUNCNAME}(): parsing NVRA for '$1'" + local pat='(.+)-([^-]+)-(.+)\.([^.]+)$' + [[ "$1" =~ $pat ]] && echo "${BASH_REMATCH[@]:1}" +} + + +__INTERNAL_rpmDirectDownload() { + local url pkg quiet='' + [[ "$1" == "--quiet" ]] && { + quiet="$1" + shift + } + + __INTERNAL_rpmInitUrl + while __INTERNAL_rpmGetNextUrl "$@"; do + url="$__INTERNAL_RETURN_VALUE"; unset __INTERNAL_RETURN_VALUE + local pkg=$(basename "$url") + rlLog "trying download from '$url'" + if $__INTERNAL_WGET $quiet -O $pkg "$url"; then + rlLogDebug "$FUNCNAME(): package '$pkg' was successfully downloaded" + echo "$pkg" + return 0 + fi + rm -f "$pkg" + rlLogWarning "package '$pkg' was not successfully downloaded" + done + rlLogError "package '$pkg' was not successfully downloaded" + rlLogDebug "${FUNCNAME}(): returning 1" + return 1 +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# __INTERNAL_rpmGetWithYumDownloader +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Download package using yumdownloader + +__INTERNAL_rpmGetWithYumDownloader() { + local source='' quiet='' + while [[ "${1:0:2}" == "--" ]]; do + case $1 in + --quiet) + quiet="$1" + ;; + --source) + source="$1" + ;; + esac + shift + done + + local package="$1-$2-$3.$4" + [[ -n "$source" ]] && package="$1-$2-$3" + rlLogDebug "${FUNCNAME}(): Trying yumdownloader to download $package" + if ! which yumdownloader &> /dev/null ; then + rlLogInfo "yumdownloader not installed: attempting to install yum-utils" + yum install $quiet -y yum-utils >&2 + fi + + if ! which yumdownloader &> /dev/null ; then + rlLogError "yumdownloader not installed even after instalation attempt" + return 1 + else + local tmp + if tmp=$(mktemp -d); then + rlLogDebug "$FUNCNAME(): downloading package to tmp dir '$tmp'" + ( cd $tmp; yumdownloader $quiet -y $source $package >&2 ) || { + rlLogError "yum downloader failed" + rm -rf $tmp + return 1 + } + local pkg=$( ls -1 $tmp ) + rlLogDebug "$FUNCNAME(): got '$pkg'" + local pkg_cnt=$( echo "$pkg" | wc -w ) + rlLogDebug "$FUNCNAME(): pkg_cnt=$pkg_cnt" + [[ $pkg_cnt -eq 0 ]] && { + rlLogError "did not download anything" + rm -rf $tmp + return 1 + } + [[ $pkg_cnt -gt 1 ]] && rlLogWarning "got more than one package" + rm -f $pkg + rlLogDebug "$FUNCNAME(): moving package to local dir" + mv $tmp/* ./ + rlLogDebug "$FUNCNAME(): removing tmp dir '$tmp'" + rm -rf $tmp + rlLogDebug "$FUNCNAME(): Download with yumdownloader successful" + echo "$pkg" + return 0 + else + rlLogError "could not create tmp dir" + return 1 + fi + fi +} + +# magic to allow this: +# BEAKERLIB_RPM_DOWNLOAD_METHODS="yum ${BEAKERLIB_RPM_DOWNLOAD_METHODS/yum/}" +__INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_default='direct yum' +__INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_cmd="BEAKERLIB_RPM_DOWNLOAD_METHODS=\"${BEAKERLIB_RPM_DOWNLOAD_METHODS:-$__INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_default}\"" +BEAKERLIB_RPM_DOWNLOAD_METHODS=$__INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_default +eval "$__INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_cmd" +unset __INTERNAL_BEAKERLIB_RPM_DOWNLOAD_METHODS_cmd + +__INTERNAL_rpmDownload() { + local method rpm res + + rlLogDebug "$FUNCNAME(): trying $* with methods '$BEAKERLIB_RPM_DOWNLOAD_METHODS'" + + for method in $BEAKERLIB_RPM_DOWNLOAD_METHODS; do + rlLogDebug "trying to get the package using method '$method'" + case $method in + direct) + if rpm="$(__INTERNAL_rpmDirectDownload "$@")"; then + res=0 + break + else + rlLogWarning "could not get package unsing method '$method'" + let res++ + fi + ;; + yum) + if rpm="$(__INTERNAL_rpmGetWithYumDownloader "$@")"; then + res=0 + break + else + rlLogWarning "could not get package unsing method '$method'" + let res++ + fi + ;; + *) + rlLogError "invalid fetch method '$method'" + return 1 + ;; + esac + done + [[ $res -eq 0 ]] && echo "$rpm" + rlLogDebug "$FUNCNAME(): returning $res" + return $res +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlRpmInstall +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlRpmInstall + +Try to install specified package from local Red Hat sources. + + rlRpmInstall [--quiet] package version release arch + +=over + +=item --quiet + +Make the download and the install process be quiet. + +=item package + +Package name like C + +=item version + +Package version like C<2.6.25.6> + +=item release + +Package release like C<55.fc9> + +=item arch + +Package arch like C + +=back + +Returns 0 if specified package was installed succesfully. + +=cut + +rlRpmInstall(){ + local quiet='' + [[ "$1" == "--quiet" ]] && { + quiet="$1" + shift + } + + if [ $# -ne 4 ]; then + rlLogError "Missing parameter(s)" + return 1 + fi + + local N=$1; + local V=$2; + local R=$3; + local A=$4; + rlLog "Installing RPM $N-$V-$R.$A" + + if rlCheckRpm $N $V $R $A; then + rlLog "$FUNCNAME: $N-$V-$R.$A already present. Doing nothing" + return 0 + else + local tmp=$(mktemp -d) + ( cd $tmp; __INTERNAL_rpmDownload $quiet $N $V $R $A ) + if [ $? -eq 0 ]; then + rlLog "RPM: $N-$V-$R.$A.rpm" + if [[ -z "$quiet" ]]; then + rpm -Uhv --oldpackage "$tmp/$N-$V-$R.$A.rpm" + else + rpm -Uq --oldpackage "$tmp/$N-$V-$R.$A.rpm" + fi + local ECODE=$? + if [ $ECODE -eq 0 ] ; then + rlLogInfo "$FUNCNAME: RPM installed successfully" + else + rlLogError "$FUNCNAME: RPM installation failed" + fi + rm -rf "$tmp" + return $ECODE + else + rlLogError "$FUNCNAME: RPM not found" + fi + return 1 + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlRpmDownload +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlRpmDownload + +Try to download specified package. + + rlRpmDownload [--quiet] {package version release arch|N-V-R.A} + rlRpmDownload [--quiet] --source {package version release|N-V-R} + +=over + +=item --quiet + +Make the download process be quiet. + +=item package + +Package name like C + +=item version + +Package version like C<2.6.25.6> + +=item release + +Package release like C<55.fc9> + +=item arch + +Package arch like C + +=back + +Returns 0 if specified package was downloaded succesfully. + +=cut + +rlRpmDownload(){ + local source='' res pkg N V R A quiet='' + while [[ "${1:0:2}" == "--" ]]; do + case $1 in + --quiet) + quiet="$1" + ;; + --source) + source="$1" + ;; + esac + shift + done + + if [[ $# -eq 1 ]]; then + local package="$1" + [[ -n "$source" ]] && package="$package.src" + if ! IFS=' ' read N V R A < <(__INTERNAL_getNVRA "$package"); then + rlLogError "$FUNCNAME: Bad N.V.R-A format" + return 1 + fi + elif [[ -z "$source" && $# -eq 4 ]]; then + N="$1" + V="$2" + R="$3" + A="$4" + elif [[ -n "$source" && $# -ge 3 ]]; then + N="$1" + V="$2" + R="$3" + A="" + else + rlLogError "$FUNCNAME: invalid parameter(s)" + return 1 + fi + + rlLog "$FUNCNAME: Fetching ${source:+source }RPM $N-$V-$R.$A" + + if pkg=$(__INTERNAL_rpmDownload $quiet $source $N $V $R $A); then + rlLog "RPM: $pkg" + echo "$pkg" + return 0 + else + rlLogError "$FUNCNAME: RPM download failed" + return 1 + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlFetchSrcForInstalled +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlFetchSrcForInstalled + +Tries various ways to download source rpm for specified installed rpm. + + rlFetchSrcForInstalled [--quiet] package + +=over + +=item --quiet + +Make the download process be quiet. + +=item package + +Installed package name like C. It accepts in-direct names. +Eg for the package name C will the function download +the C source rpm. + +=back + +Returns 0 if the source package was succesfully downloaded. + +=cut + +rlFetchSrcForInstalled(){ + local quiet='' + [[ "$1" == "--quiet" ]] && { + quiet="$1" + shift + } + + local PKGNAME=$1 srcrpm + local N V R nil + + if ! IFS=' ' read N V R nil < <((__INTERNAL_rpmGetPackageInfo rpm "$PKGNAME")); then + rlLogError "$FUNCNAME: The package is not installed, can't download the source" + return 1 + fi + rlLog "$FUNCNAME: Fetching source rpm for installed $N-$V-$R" + + if srcrpm="$(__INTERNAL_rpmDownload $quiet --source $N $V $R)"; then + echo "$srcrpm" + return 0 + else + rlLogError "$FUNCNAME: could not get package" + return 1 + fi +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=item * + +Ales Zelinka + +=item * + +Dalibor Pospisil + +=back + +=cut diff --git a/src/storage.sh b/src/storage.sh new file mode 100644 index 0000000..875732c --- /dev/null +++ b/src/storage.sh @@ -0,0 +1,88 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: storage.sh - part of the BeakerLib project +# Description: An interface to journal persistent storage capabilities +# +# Author: Petr Muller +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - storage - Internal storage helpers + +=head1 DESCRIPTION + +There are currently no public functions in this module + +=cut + + +__INTERNAL_STORAGE_DEFAULT_SECTION="GENERIC" +__INTERNAL_STORAGE_DEFAULT_NAMESPACE="GENERIC" + +__INTERNAL_ST_OPTION_PARSER=' + local namespace="$__INTERNAL_STORAGE_DEFAULT_NAMESPACE" + local section="$__INTERNAL_STORAGE_DEFAULT_SECTION" + local GETOPT=$(getopt -o : -l namespace:,section: -- "$@") || return 126 + eval set -- "$GETOPT" + while true; do + case $1 in + --) shift; break ;; + --namespace) shift; namespace="$1" ;; + --section) shift; section="$1" ;; + esac; shift + done + [[ -z "$1" ]] && { + rlLogError "$FUNCNAME(): missing the Key!" + return 1 + } + local key="$1" + local file="${BEAKERLIB_DIR}/storage/${namespace}/${section}/${key}" + rlLogDebug "$FUNCNAME(): using file \"$file\"" +' + +__INTERNAL_ST_GET() { + eval "$__INTERNAL_ST_OPTION_PARSER" + if [[ -f "$file" && -r "$file" ]]; then + local value="$(cat "$file")" + rlLogDebug "$FUNCNAME(): got value '$value'" + echo "$value" + else + rlLogDebug "$FUNCNAME(): reading unset key '$key' from section '$section' in namespace '$namespace', will return an empty string" + fi +} + +__INTERNAL_ST_PUT() { + eval "$__INTERNAL_ST_OPTION_PARSER" + local value="$2" + mkdir -p "$(dirname "$file")" + rlLogDebug "$FUNCNAME(): setting value '$value'" + echo "$value" > "$file" +} + +__INTERNAL_ST_PRUNE() { + eval "$__INTERNAL_ST_OPTION_PARSER" + rm -f "$file" +} diff --git a/src/synchronisation.sh b/src/synchronisation.sh new file mode 100644 index 0000000..2b6121a --- /dev/null +++ b/src/synchronisation.sh @@ -0,0 +1,549 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: synchronisation.sh - part of the BeakerLib project +# Description: Process synchronisation routines +# +# Author: Hubert Kario +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +getopt -T || ret=$? +if [ ${ret:-0} -ne 4 ]; then + echo "ERROR: Non enhanced getopt version detected" 1>&2 + exit 1 +fi + +# add ability to kill whole process tree +# unfortunately, because we're running inside bash script, we can't +# use the simple solution of process groups and `kill -s SIG -$pid` +# usage: __INTERNAL_killtree PID [SIGNAL] +# returns first failed kill return code or 0 if all returned success +__INTERNAL_killtree() { + local _pid=$1 + if [[ ! -n $_pid ]]; then + return 2 + fi + local _sig=${2:-TERM} + local _ret= + kill -s SIGSTOP ${_pid} || : # prevent parent from forking + local _children=$(pgrep -P ${_pid}) + local _pret=$? + if [[ $_pret -ne 0 && $_pret -ne 1 ]]; then + return 4 + fi + + local _child + for _child in $_children; do + __INTERNAL_killtree ${_child} ${_sig} || _ret=${_ret:-$?} + done + + kill -s ${_sig} ${_pid} || _ret=${_ret:-$?} + kill -s SIGCONT ${_pid} || : # allow for signal delivery to parent + return ${_ret:-0} +} + +# wrapper around bash builtin `wait', adds timeout capability +__INTERNAL_wait() { + local timeout=30 + local sigspec=SIGTERM + + while true ; do + case "$1" in + -t) timeout="$2"; shift 2 + ;; + -s) sigspec="$2"; shift 2 + ;; + --) shift 1 + break; + ;; + *) rlLogError "rlWait: unrecognized option" + return 128 + ;; + esac + done + + local pids="$@" + + (sleep $timeout && for pid in $pids; do __INTERNAL_killtree $pid "$sigspec"; done)& + local watcher=$! + disown $watcher + + wait "$@" + local my_ret=$? + + __INTERNAL_killtree $watcher SIGKILL 2> /dev/null + + return $my_ret +} + +# Since all "wait for something to happen" utilities are basically the same, +# use a generic routine that can do all their work +__INTERNAL_wait_for_cmd() { + + # don't wait more than this many seconds + local timeout=120 + # delay between command invocations + local delay=1 + # abort if this process terminates + local proc_pid=1 + # command to run + local cmd + # maximum number of command invocations + local max_invoc="" + # expected return code of command + local exp_retval=0 + # name of routine to return errors for + local routine_name="$1" + shift 1 + + # that is the GNU extended getopt syntax! + local TEMP=$(getopt -o t:p:m:d:r: -n '$routine_name' -- "$@") + if [[ $? != 0 ]] ; then + rlLogError "$routine_name: Can't parse command options, terminating..." + return 127 + fi + + eval set -- "$TEMP" + + while true ; do + case "$1" in + -t) timeout="$2"; shift 2 + ;; + -p) proc_pid="$2"; shift 2 + ;; + -m) max_invoc="$2"; shift 2 + ;; + -d) delay="$2"; shift 2 + ;; + -r) exp_retval="$2"; shift 2 + ;; + --) shift 1 + break + ;; + *) rlLogError "$routine_name: unrecognized option" + return 127 + ;; + esac + done + cmd="$1" + + if [[ $routine_name == "rlWaitForCmd" ]]; then + rlLogInfo "$routine_name: waiting for \`$cmd' to return $exp_retval in $timeout seconds" + fi + + # the case statement is a portable way to check if variable contains only + # digits (regexps are not available in old, RHEL3-era, bash) + case "$timeout" in + ''|*[!0-9]*) rlLogError "${routine_name}: Invalid timeout provided" + return 127 + ;; + esac + case "$proc_pid" in + ''|*[!0-9]*) rlLogError "${routine_name}: Invalid PID provided" + return 127 + ;; + esac + if [[ -n "$max_invoc" ]]; then + case "$max_invoc" in + ''|*[!0-9]*) rlLogError "${routine_name}: Invalid maximum number of invocations provided" + return 127 + ;; + esac + fi + # delay can be fractional, so "." is OK + case "$delay" in + ''|*[!0-9.]*) rlLogError "${routine_name}: Invalid delay specified" + return 127 + ;; + esac + case "$exp_retval" in + ''|*[!0-9]*) rlLogError "${routine_name}: Invalid expected command return value provided" + return 127 + ;; + esac + + # we use two child processes to get the timeout and process execution + # one (command_pid) runs the command until it returns expected return value + # the other is just a timout (watcher) + + # run command in loop + ( local i=0 + while [[ -n $max_invoc && $i -lt $max_invoc ]] || [[ ! -n $max_invoc ]]; do + eval $cmd + if [[ $? -eq $exp_retval ]]; then + exit 0; + else + if [[ ! -e "/proc/$proc_pid" ]]; then + exit 1; + fi + sleep $delay + fi + i=$((i+1)) + done + exit 2) & + local command_pid=$! + + # kill command running in background if the timout has elapsed + __INTERNAL_wait -t $timeout -s SIGKILL -- $command_pid 2> /dev/null + local ret=$? + if [[ $ret -eq 0 ]]; then + rlLogInfo "${routine_name}: Wait successful!" + return 0 + else + case $ret in + 1) + rlLogWarning "${routine_name}: specified PID was terminated!" + ;; + 2) + rlLogWarning "${routine_name}: Max number of test command invocations reached!" + ;; + 143|137) + rlLogWarning "${routine_name}: Timeout reached" + ;; + *) + rlLogError "${routine_name}: Unknown termination cause! Return code: $ret" + esac + return 1 + fi +} + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - synchronisation - Process synchronisation routines + +=head1 DESCRIPTION + +This is a library of helpers for process synchronisation of applications. + +NOTE: none of this commands will cause the test proper to fail, even in case +of critical errors during their invocation. If you want your test to fail +if those test fail, use their return codes and rlFail(). + +=head1 FUNCTIONS + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlWaitForCmd +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head2 Process Synchronisation + +=head3 rlWaitForCmd + +Pauses script execution until command exit status is the expeced value. +Logs a WARNING and returns 1 if the command didn't exit successfully +before timeout elapsed or a maximum number of invocations has been +reached. + + rlWaitForCmd command [-p PID] [-t time] [-m count] [-d delay] [-r retval] + +=over + +=item command + +Command that will be executed until its return code is equal 0 or value +speciefied as option to `-r'. + +=item -t time + +Timeout in seconds, default=120. If the command doesn't return 0 +before time elapses, the command will be killed. + +=item -p PID + +PID of the process to check before running command. If the process +exits before the socket is opened, the command will log a WARNING. + +=item -m count + +Maximum number of `command' executions before continuing anyway. Default is +infite. Returns 1 if the maximum was reached. + +=item -d delay + +Delay between `command' invocations. Default 1. + +=item -r retval + +Expected return value of command. Default 0. + +=back + +=cut +rlWaitForCmd() { + __INTERNAL_wait_for_cmd rlWaitForCmd "$@" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlWaitForFile +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlWaitForFile + +Pauses script execution until specified file or directory starts existing. +Returns 0 if file started existing, 1 if timeout was reached or PID exited. +Return code is greater than 1 in case of error. + + rlWaitForFile path [-p PID] [-t time] [-d delay] + +=over + +=item path + +Path to file that should start existing. + +=item -t time + +Timeout in seconds (optional, default=120). If the file isn't opened before +the time elapses the command returns 1. + +=item -p PID + +PID of the process that should also be running. If the process exits before +the file is created, the command returns with status code of 1. + +=item -d delay + +Delay between subsequent checks for existence of file. Default 1. + +=back +=cut +rlWaitForFile() { + local timeout=120 + local proc_pid=1 + local delay=1 + local file="" + + # that is the GNU extended getopt syntax! + local TEMP=$(getopt -o t:p:d: -n 'rlWaitForFile' -- "$@") + if [[ $? != 0 ]] ; then + rlLogError "rlWaitForSocket: Can't parse command options, terminating..." + return 127 + fi + + eval set -- "$TEMP" + + while true ; do + case "$1" in + -t) timeout="$2"; shift 2 + ;; + -p) proc_pid="$2"; shift 2 + ;; + -d) delay="$2"; shift 2 + ;; + --) shift 1 + break + ;; + *) rlLogError "rlWaitForFile: unrecognized option" + return 127 + ;; + esac + done + file="$1" + + rlLogInfo "rlWaitForFile: Waiting max ${timeout}s for file \`$file' to start existing" + + local cmd="[[ -e '$file' ]]" + + __INTERNAL_wait_for_cmd "rlWaitForFile" "${cmd}" -t "$timeout" -p "$proc_pid" -d "$delay" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlWaitForSocket +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlWaitForSocket + +Pauses script execution until socket starts listening. +Returns 0 if socket started listening, 1 if timeout was reached or PID exited. +Return code is greater than 1 in case of error. + + rlWaitForSocket {port|path} [-p PID] [-t time] [-d delay] [--close] + +=over + +=item port|path + +Network port to wait for opening or a path to UNIX socket. +Regular expressions are also supported. + +=item -t time + +Timeout in seconds (optional, default=120). If the socket isn't opened before +the time elapses the command returns 1. + +=item -p PID + +PID of the process that should also be running. If the process exits before +the socket is opened, the command returns with status code of 1. + +=item -d delay + +Delay between subsequent checks for availability of socket. Default 1. + +=item --close + +Wait for the socket to stop listening. + +=back + +=cut + +rlWaitForSocket(){ + + local timeout=120 + local proc_pid=1 + local delay=1 + local socket="" + local close="" + + # that is the GNU extended getopt syntax! + local TEMP=$(getopt -o t:p:d: --longoptions close -n 'rlWaitForSocket' -- "$@") + if [[ $? != 0 ]] ; then + rlLogError "rlWaitForSocket: Can't parse command options, terminating..." + return 127 + fi + + eval set -- "$TEMP" + + while true ; do + case "$1" in + -t) timeout="$2"; shift 2 + ;; + -p) proc_pid="$2"; shift 2 + ;; + -d) delay="$2"; shift 2 + ;; + --close) close="true"; shift 1 + ;; + --) shift 1 + break + ;; + *) rlLogError "rlWaitForSocket: unrecognized option" + return 127 + ;; + esac + done + socket="$1" + + # the case statement is a portable way to check if variable contains only + # digits (regexps are not available in old, RHEL3-era, bash) + case "$socket" in + *[0-9]) + #socket_type="network" + local grep_opt="\:$socket[[:space:]]" + ;; + "") rlLogError "rlWaitForSocket: No socket specified" + return 127 + ;; + *) + #socket_type="unix" + local grep_opt="$socket" + ;; + esac + + local cmd="netstat -nl | grep -E '$grep_opt' >/dev/null" + + if [[ ${close:-false} == true ]]; then + rlLogInfo "rlWaitForSocket: Waiting max ${timeout}s for socket \`$socket' to close" + __INTERNAL_wait_for_cmd "rlWaitForSocket" "${cmd}" -t $timeout -p $proc_pid -d $delay -r 1 + else + rlLogInfo "rlWaitForSocket: Waiting max ${timeout}s for socket \`$socket' to start listening" + __INTERNAL_wait_for_cmd "rlWaitForSocket" "${cmd}" -t $timeout -p $proc_pid -d $delay + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlWait +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlWait + +Wrapper around bash builtin `wait' command. See bash_builtins(1) man page. +Kills the process and all its children if the timeout elapses. + + rlWaitFor [n ...] [-s SIGNAL] [-t time] + +=over + +=item n + +List of PIDs to wait for. They need to be background tasks of current shell. +See bash_builtins(1) section for `wait' command/ + +=item -t time + +Timeout in seconds (optional, default=30). If the wait isn't successful +before the time elapses then all specified tasks are killed. + +=item -s SIGNAL + +Signal used to kill the process, optional SIGTERM by default. + +=back + +=cut + +rlWait() { + # that is the GNU extended getopt syntax! + local TEMP=$(getopt -o t:s: -n 'rlWait' -- "$@") + if [[ $? != 0 ]]; then + rlLogError "rlWait: Can't parse command options, terminating..." + return 128 + fi + + eval set -- "$TEMP" + + __INTERNAL_wait "$@" + + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Hubert Kario + +=back + +=cut diff --git a/src/test/Makefile b/src/test/Makefile new file mode 100644 index 0000000..a748c26 --- /dev/null +++ b/src/test/Makefile @@ -0,0 +1,5 @@ +test: +ifndef AREA + ./testwatcher.sh +endif + ./test.sh $(AREA) diff --git a/src/test/README b/src/test/README new file mode 100644 index 0000000..3c3a465 --- /dev/null +++ b/src/test/README @@ -0,0 +1,52 @@ + +BeakerLib unit tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is a set of unit tests used for BeakerLib sanity checking. +Should be run during package building and before submitting a +patch. Some of the tests are expected to fail (backup functions +need root permission and the coverage test suite fails because... +well because the coverage is not complete... yet :-) + + +How to run tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All available tests: ./test.sh +Selected files only: ./test.sh beakerlibTest.sh ... +Selected tests only: ./test.sh test_rlServiceStart ... +Check test.sh sanity: ./test.sh test + +For more detailed output & logging use: DEBUG=1 ./test.sh ... + + +How to add a test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just create a "*Test.sh" file, which contains functions named +"test_*" which use any of the following asserts: + + assertRun command [status] [comment] + * run the command + * check its exit status + * log the result with a comment + + assertTrue comment command + * check that command succeeded (status 0) + + assertFalse comment command + * check that command failed (status 1) + + assertGoodBad command good bad + * run the command + * check that correct asserts are saved into journal + * specify number of expected good/bad asserts, "" to skip + + assertParameters assert + * check that missing parameters are reported + * removes parameters from assert call - must not pass + * works only for assert (checks journal, not the exit code) + +The tests should not produce any output unless DEBUG=1 specified +and should clean up after themselves when finished. The expected +status for the assertRun command can also be a regular expression. diff --git a/src/test/beakerlibTest.sh b/src/test/beakerlibTest.sh new file mode 100644 index 0000000..a2a3faa --- /dev/null +++ b/src/test/beakerlibTest.sh @@ -0,0 +1,22 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Jan Hutar + +test_BEAKERLIBVariableNotNull() { + assertTrue "BEAKERLIB variable is empty" "[ -n '$BEAKERLIB' ]" +} + +test_BEAKERLIBVariableSane() { + assertTrue "BEAKERLIB points to an existing directory" "[ -d '$BEAKERLIB' ]" +} diff --git a/src/test/benchmark-messages.sh b/src/test/benchmark-messages.sh new file mode 100755 index 0000000..c4a2973 --- /dev/null +++ b/src/test/benchmark-messages.sh @@ -0,0 +1,12 @@ +. $BEAKERLIB/beakerlib.sh +export __INTERNAL_JOURNALIST="$BEAKERLIB/python/journalling.py" + +rlJournalStart + rlPhaseStartTest + for msg in $( seq $1 ) + do + rlLog "Message $msg" + done + rlPass + rlPhaseEnd +rlJournalEnd diff --git a/src/test/benchmark-phases.sh b/src/test/benchmark-phases.sh new file mode 100755 index 0000000..e33b0fc --- /dev/null +++ b/src/test/benchmark-phases.sh @@ -0,0 +1,12 @@ +. $BEAKERLIB/beakerlib.sh +export __INTERNAL_JOURNALIST="$BEAKERLIB/python/journalling.py" + +rlJournalStart + for msg in $( seq $1 ) + do + rlPhaseStartTest + rlLog "Message $msg" + rlPass + rlPhaseEnd + done +rlJournalEnd diff --git a/src/test/benchmark-tests.sh b/src/test/benchmark-tests.sh new file mode 100755 index 0000000..780110a --- /dev/null +++ b/src/test/benchmark-tests.sh @@ -0,0 +1,13 @@ +. $BEAKERLIB/beakerlib.sh +export __INTERNAL_JOURNALIST="$BEAKERLIB/python/journalling.py" + +rlJournalStart + rlPhaseStartTest + for msg in $( seq $1 ) + do + [ "$(( $RANDOM % 2 ))" == "0" ] + rlAssert0 "Test $msg" $? + done + rlPass + rlPhaseEnd +rlJournalEnd diff --git a/src/test/benchmark.sh b/src/test/benchmark.sh new file mode 100755 index 0000000..1e519f3 --- /dev/null +++ b/src/test/benchmark.sh @@ -0,0 +1,29 @@ +#!/usr/bin/bash + +export BEAKERLIB="$PWD/.." +export TESTID='123456' +export TEST='beakerlib-benchmarks' +. ../beakerlib.sh + +export TIMEFORMAT="System: %S seconds; User: %U seconds" +TIMEFILE=$( mktemp -u ) # no-reboot + +for benchmark in messages tests phases +do + for count in 100 200 300 400 500 600 700 800 900 1000 1100 1200 + do + rm -rf /var/tmp/beakerlib-123456 + rm -f $TIMEFILE.$benchmark + echo -n "Running $benchmark benchmark with $count records: " + ( time ( { ./benchmark-$benchmark.sh $count &>/dev/null; } 2>&3 ) ) 3>&2 2>>$TIMEFILE.$benchmark + cat $TIMEFILE.$benchmark + OLDFILE=".benchmark-$count-$benchmark.old" + if [ -e $OLDFILE ] + then + echo -n " With $count old was: " + cat $OLDFILE + fi + rm -f $OLDFILE + cp $TIMEFILE.$benchmark $OLDFILE + done +done diff --git a/src/test/coverageTest.sh b/src/test/coverageTest.sh new file mode 100644 index 0000000..dfd38c5 --- /dev/null +++ b/src/test/coverageTest.sh @@ -0,0 +1,25 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Ales Zelinka + +#tries to find test for every existing rl-function +test_coverage(){ + TEST_SCRIPTS=$(ls *Test.sh|grep -v coverageTest.sh) + #doesn't work with redirection, must use temp file instead + declare -f |grep '^rl.* ()' |grep -v '^rlj'|cut -d ' ' -f 1 >fnc.list + while read FUNCTION ; do + assertTrue "Test coverage for $FUNCTION" "grep -q $FUNCTION $(echo $TEST_SCRIPTS)" + done < fnc.list + rm -f fnc.list +} diff --git a/src/test/infrastructureTest.sh b/src/test/infrastructureTest.sh new file mode 100644 index 0000000..738ed39 --- /dev/null +++ b/src/test/infrastructureTest.sh @@ -0,0 +1,978 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Petr Splichal + +# +# rlFileBackup & rlFileRestore unit test +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# this test has to be run as root +# [to restore ownership, permissions and attributes] +# run with DEBUG=1 to get more details about progress + +BackupSanityTest() { + # detect selinux & acl support + local selinux=false + if [ -d "/selinux" ]; then + selinux=true + fi + + local acl=false + if setfacl -m u:root:rwx "$BEAKERLIB_DIR" &>/dev/null; then + local acl=true + fi + + [ -d "$BEAKERLIB_DIR" ] && chmod -R 777 "$BEAKERLIB_DIR" \ + && rm -rf "$BEAKERLIB_DIR" && rlJournalStart + score=0 + + list() { + ls -ld --full-time directory + ls -lL --full-time directory + $acl && ( find directory | sort | getfacl - ) + $selinux && ls -Z directory + cat directory/content + } + + mess() { + echo -e "\nBackupSanityTest: $1" + } + + fail() { + echo "BackupSanityTest FAIL: $1" + ((score++)) + } + + # setup + tmpdir="$(mktemp -d /tmp/backup-test-XXXXXXX)" # no-reboot + pushd "$tmpdir" >/dev/null + + # create files + mkdir directory + pushd directory >/dev/null + mkdir -p sub/sub/dir + mkdir 'dir with spaces' + mkdir 'another dir with spaces' + touch file permissions ownership times context acl + echo "hello" >content + ln file hardlink + ln -s file softlink + chmod 750 permissions + chown nobody.nobody ownership + touch --reference /var/www times + $acl && setfacl -m u:root:rwx acl + $selinux && chcon --reference /var/www context + popd >/dev/null + + # save details and backup + list >original + mess "Doing the backup" + rlFileBackup directory || fail "Backup" + + # remove completely + mess "Testing restore after complete removal " + rm -rf directory + rlFileRestore || fail "Restore after complete removal" + list >removal + diff -u original removal || fail "Restore after complete removal not ok, differences found" + + # remove some, change content + mess "Testing restore after partial removal" + rm -rf directory/sub/sub directory/hardlink directory/permissions 'dir with spaces' + echo "hi" >directory/content + rlFileRestore || fail "Restore after partial removal" + list >partial + diff -u original partial || fail "Restore after partial removal not ok, differences found" + + # attribute changes + mess "Testing restore after attribute changes" + pushd directory >/dev/null + chown root ownership + chown nobody file + touch times + chmod 777 permissions + $acl && setfacl -m u:root:--- acl + $selinux && chcon --reference /home context + popd >/dev/null + rlFileRestore || fail "Restore attributes" + list >attributes + diff -u original attributes || fail "Restore attributes not ok, differences found" + + # acl check for correct path restore + if $acl; then + mess "Checking that path ACL is correctly restored" + # create acldir with modified ACL + pushd directory >/dev/null + mkdir acldir + touch acldir/content + setfacl -m u:root:--- acldir + popd >/dev/null + list >original + # backup it's contents (not acldir itself) + rlFileBackup directory/acldir/content + rm -rf directory/acldir + # restore & check for differences + rlFileRestore || fail "Restore path ACL" + list >acl + diff -u original acl || fail "Restoring correct path ACL not ok" + fi + + # clean up + popd >/dev/null + rm -rf "$tmpdir" + + mess "Total score: $score" + return $score +} + + +test_rlFileBackupAndRestore() { + assertFalse "rlFileRestore should fail when no backup was done" \ + 'rlFileRestore' 2 + assertTrue "rlFileBackup should fail and return 2 when no file/dir given" \ + 'rlFileBackup; [ $? == 2 ]' + assertRun 'rlFileBackup i-do-not-exist' 8 \ + "rlFileBackup should fail when given file/dir does not exist" + + if [[ $UID -eq 0 ]]; then + assertTrue "rlFileBackup & rlFileRestore sanity test" BackupSanityTest + else + assertLog "rlFileBackup & rlFileRestore sanity test is not meant to be executed under non-priviledged user" SKIP + fi +} + +test_rlFileBackupSymlinkWarn() { + assertTrue 'journalReset' + FILE="$(mktemp)" # no-reboot + SYMLINK="$FILE".symlink + ln -s "$FILE" "$SYMLINK" + if [[ $UID -eq 0 ]]; then + assertRun "rlFileBackup '$FILE'" + assertFalse "No symlink warn for a regular file" "rlJournalPrintText |grep 'Backing up symlink (not its target)'" + assertRun "rlFileBackup '$SYMLINK'" + assertTrue "Warning issued when backing up a symlink" "rlJournalPrintText |grep 'Backing up symlink (not its target)'" + else + assertLog "rlFileBackup is not meant to be executed under non-priviledged user" SKIP + fi + rm -f "$FILE" "$SYMLINK" +} + +test_rlFileBackupCleanAndRestore() { + test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) # no-reboot + date > "$test_dir/date1" + date > "$test_dir/date2" + if [ "$DEBUG" == "1" ]; then + rlFileBackup --clean "$test_dir" + else + rlFileBackup --clean "$test_dir" >/dev/null 2>&1 + fi + rm -f "$test_dir/date1" # should be restored + date > "$test_dir/date3" # should be removed + ###tree "$test_dir" + if [ "$DEBUG" == "1" ]; then + rlFileRestore + else + rlFileRestore >/dev/null 2>&1 + fi + ###tree "$test_dir" + assertTrue "rlFileBackup with '--clean' option adds" \ + "ls '$test_dir/date1'" + assertFalse "rlFileBackup with '--clean' option removes" \ + "test -f '$test_dir/date3'" +} + +test_rlFileBackupCleanAndRestoreWhitespace() { + test_dir=$(mktemp -d '/tmp/beakerlib-test-XXXXXX') # no-reboot + + mkdir "$test_dir/noclean" + mkdir "$test_dir/noclean clean" + date > "$test_dir/noclean/date1" + date > "$test_dir/noclean clean/date2" + + silentIfNotDebug "rlFileBackup '$test_dir/noclean'" + silentIfNotDebug "rlFileBackup --clean '$test_dir/noclean clean'" + + date > "$test_dir/noclean/date3" # this should remain + date > "$test_dir/noclean clean/date4" # this should be removed + + silentIfNotDebug 'rlFileRestore' + + assertTrue "rlFileBackup without '--clean' do not remove in dir with spaces" \ + "test -f '$test_dir/noclean/date3'" + assertFalse "rlFileBackup with '--clean' remove in dir with spaces" \ + "test -f '$test_dir/noclean clean/date4'" +} + +test_rlFileBackupAndRestoreNamespaces() { + test_file=$(mktemp '/tmp/beakerlib-test-XXXXXX') # no-reboot + test_file2=$(mktemp '/tmp/beakerlib-test-XXXXXX') # no-reboot + + # namespaced backup should restore independently + echo "abcde" > "$test_file" + silentIfNotDebug "rlFileBackup '$test_file'" + echo "fghij" > "$test_file2" + silentIfNotDebug "rlFileBackup --namespace myspace1 '$test_file2'" + echo "12345" > "$test_file" + echo "67890" > "$test_file2" + silentIfNotDebug "rlFileRestore" + assertRun "grep 'abcde' '$test_file' && grep '67890' '$test_file2'" 0 \ + "Normal restore shouldn't restore namespaced backup" + echo "12345" > "$test_file" + echo "67890" > "$test_file2" + silentIfNotDebug "rlFileRestore --namespace myspace1" + assertRun "grep '12345' '$test_file' && grep 'fghij' '$test_file2'" 0 \ + "Namespaced restore shouldn't restore normal backup" + + # namespaced backup doesn't overwrite normal one + echo "abcde" > "$test_file" + silentIfNotDebug "rlFileBackup '$test_file'" + echo "12345" > "$test_file" + silentIfNotDebug "rlFileBackup --namespace myspace2 '$test_file'" + silentIfNotDebug 'rlFileRestore' + assertRun "grep 'abcde' '$test_file'" 0 \ + "Namespaced backup shouldn't overwrite normal one" + + rm -f "$test_file" "$test_file2" +} + +test_rlFileBackup_MissingFiles() { + local dir="$(mktemp -d)" # no-reboot + assertTrue "Preparing the directory" "pushd $dir && mkdir subdir" + selinuxenabled && assertTrue "Changing selinux context" "chcon -t httpd_user_content_t subdir" + assertTrue "Saving the old context" "ls -lZd subdir > old" + assertRun "rlFileBackup $dir/subdir/missing" 8 "Backing up without --clean" + assertRun "rlFileBackup --missing-ok $dir/subdir/missing" 0 "Backing up with --missing-ok" + assertRun "rlFileBackup --clean $dir/subdir/missing" 0 "Backing up with --clean" + assertRun "rlFileBackup --clean --no-missing-ok $dir/subdir/missing" 8 "Backing up with --clean and --no-missing-ok" + assertRun "rlFileRestore" 0 "Restoring" + assertTrue "Saving the new context" "ls -lZd subdir > new" + assertTrue "Checking security context (BZ#618269)" "diff old new" + popd >/dev/null + rm -rf "$dir" +} + + +# backing up symlinks [BZ#647231] +test_rlFileBackup_Symlinks() { + local dir="$(mktemp -d)" #no-reboot + assertTrue "Preparing files" "pushd $dir && touch file && ln -s file link" + assertRun "rlFileBackup link" "[07]" "Backing up the link" + assertTrue "Removing the link" "rm link" + assertRun "rlFileRestore link" "[02]" "Restoring the link" + assertTrue "Symbolic link should be restored" "test -L link" + popd >/dev/null + rm -rf "$dir" +} + +# backing up dir with symlink in the parent dir [BZ#647231#c13] +test_rlFileBackup_SymlinkInParent() { + local dir="$(mktemp -d)" #no-reboot + assertTrue "Preparing tmp directory" "pushd $dir" + assertTrue "Preparing target directory" 'mkdir target && touch target/file1 target/file2' + assertTrue "Preparing linked directory" 'ln -s target link' + assertRun "rlFileBackup link/file1" '[07]' "Backing up the link/file1" + assertTrue "Removing link/file1" 'rm link/file1' + assertRun "rlFileRestore" [02] "Restoring the file1" + assertTrue "Testing that link points to target" 'readlink link | grep target' + assertTrue "Testing that link/file1 was restored" 'test -f link/file1' + assertTrue "Testing that link/file2 is still present" 'test -f link/file2' + popd >/dev/null + rm -rf "$dir" +} + +test_rlFileRestore_ECs() { + test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) # no-reboot + date > "$test_dir/date1" + date > "$test_dir/date2" + assertRun "rlFileRestore --blabla" 1 "test invalid long option" + assertRun "rlFileRestore -b" 1 "test invalid short option" + assertRun "rlFileRestore" 2 "no backup done" + assertRun "rlFileBackup --clean '$test_dir/date3'" + date > "$test_dir/date3" + chattr +i "$test_dir/date3" + assertRun "rlFileRestore" 20 "could not cleanup file + not backed up files" + assertRun "rlFileBackup '$test_dir/date2'" + assertRun "rlFileRestore" 4 "could not cleanup file" + chattr -i "$test_dir/date3" + chattr +i "$test_dir/date2" + assertRun "rlFileRestore" 8 "could not rewrite original file" + chattr -i "$test_dir/date2" + rm -rf $test_dir +} + +test_rlServiceStart() { + assertTrue "rlServiceStart should fail and return 99 when no service given" \ + 'rlServiceStart; [ $? == 99 ]' + + assertTrue "down-starting-pass" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlRun "rlServiceStart down-starting-pass"' + + assertTrue "down-starting-ok" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart down-starting-ok' + + assertTrue "up-starting-ok" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart up-starting-ok' + + assertTrue "weird-starting-ok" \ + 'service() { case $2 in status) return 33;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart weird-starting-ok' + + assertFalse "up-starting-stop-ko" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 1;; esac; }; + rlServiceStart up-starting-stop-ko' + + assertFalse "up-starting-stop-ok-start-ko" \ + 'service() { case $2 in status) return 0;; start) return 1;; stop) return 0;; esac; }; + rlServiceStart up-starting-stop-ok-start-ko' + + assertFalse "down-starting-start-ko" \ + 'service() { case $2 in status) return 3;; start) return 1;; stop) return 0;; esac; }; + rlServiceStart down-starting-start-ko' + + assertFalse "weird-starting-start-ko" \ + 'service() { case $2 in status) return 33;; start) return 1;; stop) return 0;; esac; }; + rlServiceStart weird-starting-start-ko' +} + +test_rlServiceStop() { + assertTrue "rlServiceStop should fail and return 99 when no service given" \ + 'rlServiceStop; [ $? == 99 ]' + + assertTrue "down-stopping-ok" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop down-stopping-ok' + + assertTrue "up-stopping-ok" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop up-stopping-ok' + + assertTrue "weird-stopping-ok" \ + 'service() { case $2 in status) return 33;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop weird-stopping-ok' + + assertFalse "up-stopping-stop-ko" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 1;; esac; }; + rlServiceStop up-stopping-stop-ko' +} + +test_rlServiceEnable() { + assertTrue "rlServiceEnable should fail and return 99 when no service given" \ + 'rlServiceEnable; [ $? == 99 ]' + + assertTrue "down-enabling-ok" \ + 'chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 0;; esac; }; + rlServiceEnable disabled-enabling-ok' + + assertTrue "up-enabling-ok" \ + 'chkconfig() { case $2 in "") return 0;; on) return 0;; off) return 0;; esac; }; + rlServiceEnable enabled-enabling-ok' + + assertTrue "weird-enabling-ok" \ + 'chkconfig() { case $2 in "") return 11;; on) return 0;; off) return 0;; esac; }; + rlServiceEnable weird-enabling-ok' + + assertFalse "up-enabling-enable-ko" \ + 'chkconfig() { case $2 in "") return 1;; on) return 1;; off) return 0;; esac; }; + rlServiceEnable up-enabling-enable-ko' +} + +test_rlServiceDisable() { + assertTrue "rlServiceDisable should fail and return 99 when no service given" \ + 'rlServiceDisable; [ $? == 99 ]' + + assertTrue "enabled-disabling-ok" \ + 'chkconfig() { case $2 in "") return 0;; on) return 0;; off) return 0;; esac; }; + rlServiceDisable enabled-disabling-ok' + + assertTrue "disabled-disabling-ok" \ + 'chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 0;; esac; }; + rlServiceDisable disabled-disabling-ok' + + assertTrue "weird-disabling-ok" \ + 'chkconfig() { case $2 in "") return 11;; on) return 0;; off) return 0;; esac; }; + rlServiceDisable weird-disabling-ok' + + assertFalse "up-disabling-disable-ko" \ + 'chkconfig() { case $2 in "") return 0;; on) return 1;; off) return 1;; esac; }; + rlServiceDisable up-disabling-disable-ko' +} + +test_rlServiceRestore() { + assertTrue "was-down-is-down-ok" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop was-down-is-down-ok; + service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceRestore was-down-is-down-ok' + + assertTrue "was-down-is-up-ok" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart was-down-is-up-ok; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceRestore was-down-is-up-ok' + + assertTrue "was-up-is-down-ok" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop was-up-is-down-ok; + service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceRestore was-up-is-down-ok' + + assertTrue "was-up-is-up-ok" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart was-up-is-up-ok; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceRestore was-up-is-up-ok' + + assertFalse "was-up-is-up-stop-ko" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 1;; esac; }; + rlServiceStart was-up-is-up-stop-ko; + service() { case $2 in status) return 0;; start) return 0;; stop) return 1;; esac; }; + rlServiceRestore was-up-is-up-stop-ko' + + assertFalse "was-down-is-up-stop-ko" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 1;; esac; }; + rlServiceStart was-down-is-up-stop-ko; + service() { case $2 in status) return 0;; start) return 0;; stop) return 1;; esac; }; + rlServiceRestore was-down-is-up-stop-ko' + + assertFalse "was-up-is-down-start-ko" \ + 'service() { case $2 in status) return 0;; start) return 1;; stop) return 0;; esac; }; + rlServiceStop was-up-is-down-start-ko; + service() { case $2 in status) return 3;; start) return 1;; stop) return 0;; esac; }; + rlServiceRestore was-up-is-down-start-ko' + + assertFalse "was-up-is-up-start-ko" \ + 'service() { case $2 in status) return 0;; start) return 1;; stop) return 0;; esac; }; + rlServiceStart was-up-is-up-start-ko; + service() { case $2 in status) return 0;; start) return 1;; stop) return 0;; esac; }; + rlServiceRestore was-up-is-up-start-ko' + + # verify that rlServiceRestore without arguments restores all services in reverse order + __INTERNAL_SERVICES_LIST="$BEAKERLIB_DIR/services_list" + rm -f $__INTERNAL_SERVICES_LIST + # initial setup: service1 is running, service2 is stopped + assertTrue "service1-is-up-stop" \ + 'service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlServiceStop service1;' + assertTrue "service1 status saved" \ + "grep -q 'service1' $__INTERNAL_SERVICES_LIST" # verify that the initial status is properly saved + assertTrue "service1-is-down-start-again" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart service1;' + assertRun "[ '$(grep service1 $__INTERNAL_SERVICES_LIST | wc -l)' -eq '1' ]" 0 + assertTrue "service2-is-down-start" \ + 'service() { case $2 in status) return 3;; start) return 0;; stop) return 0;; esac; }; + rlServiceStart service2;' + assertTrue "service2 status saved" \ + "grep -q 'service2' $__INTERNAL_SERVICES_LIST" + # at this moment service1 and service2 are running, we need to restore it properly into original setup + TMP_FILE=`mktemp` + assertTrue "restore all" \ + 'service() { case $2 in status) return 0;; start) echo -n "starting $1;">>$TMP_FILE; return 0;; stop) echo -n "stopping $1;">>$TMP_FILE; return 0;; esac; }; + rlServiceRestore;' + # proper restore sequence is: stop service2, stop service1, start service 1 again + assertTrue "grep 'stopping service2;stopping service1;starting service1;' $TMP_FILE" + rm -f $TMP_FILE +} + +__INTERNAL_fake_release() { + local release=${1} + ####### + # TODO: this part is copypasted from testingTest.sh, it would probably be nice to have + # it as it's own test function + #fake beakerlib-lsb_release so we can control what rlIsRHEL sees + local fake_release=$(mktemp) + cat >beakerlib-lsb_release <<-EOF +#!/bin/bash +RELEASE="\$(<$fake_release)" +[ \$1 = "-ds" ] && { + echo "Red Hat Enterprise Linux \$RELEASE (fakeone)" + exit 0 +} +[ \$1 = "-rs" ] && { echo "\$RELEASE" ; exit 0 ; } +echo invalid input, this stub might be out of date, please +echo update according to __INTERNAL_rlIsDistro usage of beakerlib-lsb_release +exit 1 +EOF + chmod a+x ./beakerlib-lsb_release + local OLD_PATH=$PATH + PATH="./:"$PATH + + echo "$release" > $fake_release +} + +test_rlServiceRestore_persistence() { + assertTrue "was-down-is-down-ok" \ + 'chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 0;; esac; }; + rlServiceDisable was-down-is-down-ok; + chkconfig() { case $2 in "") return 1;; on) return 1;; off) return 1;; esac; }; + rlServiceRestore was-down-is-down-ok' + + assertTrue "was-down-is-up-ok" \ + 'chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 0;; esac; }; + rlServiceEnable was-down-is-up-ok; + chkconfig() { case $2 in "") return 0;; on) return 1;; off) return 0;; esac; }; + rlServiceRestore was-down-is-up-ok' + + assertTrue "was-up-is-down-ok" \ + 'chkconfig() { case $2 in "") return 0;; on) return 0;; off) return 0;; esac; }; + rlServiceDisable was-up-is-down-ok; + chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 1;; esac; }; + rlServiceRestore was-up-is-down-ok' + + assertTrue "was-up-is-up-ok" \ + 'chkconfig() { case $2 in "") return 0;; on) return 0;; off) return 0;; esac; }; + rlServiceEnable was-up-is-up-ok; + chkconfig() { case $2 in "") return 0;; on) return 1;; off) return 1;; esac; }; + rlServiceRestore was-up-is-up-ok' + + assertFalse "was-down-is-up-stop-ko" \ + 'chkconfig() { case $2 in "") return 1;; on) return 0;; off) return 1;; esac; }; + rlServiceEnable was-down-is-up-stop-ko; + chkconfig() { case $2 in "") return 0;; on) return 0;; off) return 1;; esac; }; + rlServiceRestore was-down-is-up-stop-ko' + + assertFalse "was-up-is-down-start-ko" \ + 'chkconfig() { case $2 in "") return 0;; on) return 1;; off) return 0;; esac; }; + rlServiceDisable was-up-is-down-start-ko; + chkconfig() { case $2 in "") return 1;; on) return 1;; off) return 0;; esac; }; + rlServiceRestore was-up-is-down-start-ko' + +} + +test_rlSocketStart_legacy() { + local release=6.0 + __INTERNAL_fake_release $release + + assertTrue "rlSocketStart should fail and return 99 when no service given" \ + 'rlSocketStart; [ $? == 99 ]' + + assertTrue "exists-started-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart exists-started-start-ok' + + assertTrue "exists-started-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 1;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart exists-started-start-ko' + + assertTrue "exists-stopped-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart exists-stopped-start-ok' + + assertFalse "exists-stopped-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 1;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart exists-stopped-start-ko' + + assertTrue "exists-weirded-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "unknown";return 5;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart exists-weirded-start-ok' + + assertFalse "notexists-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") return 1;; on) return 1;; off) return 1;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStart notexists-start-ko' + +} + +test_rlSocketStart_systemd() { + local release=7.0 + __INTERNAL_fake_release $release + + assertTrue "rlSocketStart should fail and return 99 when no service given" \ + 'rlSocketStart; [ $? == 99 ]' + + assertTrue "exists-started-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-start-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketStart exists-started-start-ok' + + assertTrue "exists-started-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-start-ko.socket";; start) return 1;; stop) return 1;;is-active) echo "active";return 0;; is-enabled) echo "enabled"; return 0;; esac; }; + rlSocketStart exists-started-start-ko' + + assertTrue "exists-stopped-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-start-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStart exists-stopped-start-ok' + + assertFalse "exists-stopped-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-start-ko.socket";; start) return 1;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStart exists-stopped-start-ko' + + assertTrue "exists-weirded-start-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-weirded-start-ok.socket";; start) return 0;; stop) return 0;;is-active) return 2;; is-enabled) return 0;; esac; }; + rlSocketStart exists-weirded-start-ok' + + assertFalse "notexists-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "failure";; start) return 1;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStart notexists-start-ko' + +} + +test_rlSocketStop_legacy() { + local release=6.0 + __INTERNAL_fake_release $release + + assertTrue "rlSocketStop should fail and return 99 when no service given" \ + 'rlSocketStop; [ $? == 99 ]' + + assertTrue "exists-started-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop exists-started-start-ok' + + assertFalse "exists-started-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 1;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop exists-started-start-ko' + + assertTrue "exists-stopped-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop exists-stopped-start-ok' + + assertTrue "exists-stopped-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 1;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop exists-stopped-start-ko' + + assertTrue "exists-weirded-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") echo "unknown";return 5;; on) return 0;; off) return 0;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop exists-weirded-start-ok' + + assertTrue "notexists-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + chkconfig() { case $2 in "") return 1;; on) return 1;; off) return 1;; esac; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + rlSocketStop notexists-start-ko' + +} + +test_rlSocketStop_systemd() { + local release=7.0 + __INTERNAL_fake_release $release + + assertTrue "rlSocketStop should fail and return 99 when no service given" \ + 'rlSocketStop; [ $? == 99 ]' + + assertTrue "exists-started-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-stop-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketStop exists-started-stop-ok' + + assertFalse "exists-started-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-stop-ko.socket";; start) return 0;; stop) return 1;;is-active) echo "active";return 0;; is-enabled) echo "enabled"; return 0;; esac; }; + rlSocketStop exists-started-stop-ko' + + assertTrue "exists-stopped-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-stop-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStop exists-stopped-stop-ok' + + assertTrue "exists-stopped-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-stop-ko.socket";; start) return 0;; stop) return 1;;is-active) return 1;; is-enabled) return 1;; esac; }; + rlSocketStop exists-stopped-stop-ko' + + assertTrue "exists-weirded-stop-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-weirded-stop-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStop exists-weirded-stop-ok' + + assertTrue "notexists-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "failure";; start) return 1;; stop) return 1;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStop notexists-stop-ko' + +} + +test_rlSocketRestore_legacy() { + local release=6.0 + __INTERNAL_fake_release $release + + assertTrue "rlSocketStart should fail and return 99 when no service given" \ + 'rlSocketStart; [ $? == 99 ]' + + assertTrue "exists-started-started-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 0;; esac; }; + rlSocketStart exists-started-started-ok; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 1;; off) return 1;; esac; }; + rlSocketRestore exists-started-started-ok' + + assertTrue "exists-started-stopped-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 0;; esac; }; + rlSocketStop exists-started-stopped-ok; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 1;; esac; }; + rlSocketRestore exists-started-stopped-ok' + + assertFalse "exists-started-stopped-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 0;; esac; }; + rlSocketStop exists-started-stopped-start-ko; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 1;; off) return 0;; esac; }; + rlSocketRestore exists-started-stopped-start-ko' + + assertTrue "exists-stopped-started-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 0;; esac; }; + rlSocketStart exists-stopped-started-ok; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 1;; off) return 0;; esac; }; + rlSocketRestore exists-stopped-started-ok' + + assertFalse "exists-stopped-started-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 0;; esac; }; + rlSocketStart exists-started-started-ok; + chkconfig() { case $2 in "") echo "on";return 0;; on) return 0;; off) return 1;; esac; }; + rlSocketRestore exists-stopped-started-stop-ko' + + assertTrue "exists-stopped-stopped-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 1; }; + service() { case $2 in status) return 0;; start) return 0;; stop) return 0;; esac; }; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 0;; off) return 0;; esac; }; + rlSocketStop exists-stopped-stopped-ok; + chkconfig() { case $2 in "") echo "off";return 1;; on) return 1;; off) return 1;; esac; }; + rlSocketRestore exists-stopped-stopped-ok' + +} + +test_rlSocketRestore_systemd() { + local release=7.0 + + __INTERNAL_fake_release $release + assertTrue "rlSocketStart should fail and return 99 when no service given" \ + 'rlSocketStart; [ $? == 99 ]' + + assertTrue "exists-started-started-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-started-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketStart exists-started-started-ok; + systemctl() { case $2 in list-sockets) echo "exists-started-started-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-started-started-ok' + + assertTrue "exists-started-stopped-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-stopped-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketStop exists-started-stopped-ok; + systemctl() { case $2 in list-sockets) echo "exists-started-stopped-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-started-stopped-ok' + + assertFalse "exists-started-stopped-start-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-started-stopped-start-ko.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketStop exists-started-stopped-start-ko; + systemctl() { case $2 in list-sockets) echo "exists-started-stopped-start-ko.socket";; start) return 1;; stop) return 1;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-started-stopped-start-ko' + + assertTrue "exists-stopped-started-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-started-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStart exists-stopped-started-ok; + systemctl() { case $2 in list-sockets) echo "exists-stopped-started-ok.socket";; start) return 0;; stop) return 0;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-stopped-started-ok' + + assertFalse "exists-stopped-started-stop-ko" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-started-stop-ko.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStart exists-stopped-started-stop-ko; + systemctl() { case $2 in list-sockets) echo "exists-stopped-started-stop-ko.socket";; start) return 1;; stop) return 1;;is-active) return 0;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-stopped-started-stop-ko' + + assertTrue "exists-stopped-stopped-ok" \ + '__INTERNAL_SOCKET_get_handler() { return 0; }; + systemctl() { case $2 in list-sockets) echo "exists-stopped-stopped-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketStop exists-stopped-stopped-ok; + systemctl() { case $2 in list-sockets) echo "exists-stopped-stopped-ok.socket";; start) return 0;; stop) return 0;;is-active) return 1;; is-enabled) return 0;; esac; }; + rlSocketRestore exists-stopped-stopped-ok' + assertTrue "rlSocketStart should fail and return 99 when no service given" \ + 'rlSocketStart; [ $? == 99 ]' + +} + +__INTERNAL_fake_release() { + local release=${1} + ####### + # TODO: this part is copypasted from testingTest.sh, it would probably be nice to have + # it as it's own test function + #fake beakerlib-lsb_release so we can control what rlIsRHEL sees + local fake_release=$(mktemp) + cat >beakerlib-lsb_release <<-EOF +#!/bin/bash +RELEASE="\$(<$fake_release)" +[ \$1 = "-ds" ] && { + echo "Red Hat Enterprise Linux \$RELEASE (fakeone)" + exit 0 +} +[ \$1 = "-rs" ] && { echo "\$RELEASE" ; exit 0 ; } +echo invalid input, this stub might be out of date, please +echo update according to __INTERNAL_rlIsDistro usage of beakerlib-lsb_release +exit 1 +EOF + chmod a+x ./beakerlib-lsb_release + local OLD_PATH=$PATH + PATH="./:"$PATH + + echo "$release" > $fake_release +} + +#FIXME: no idea how to really test these mount function +MP="beakerlib-test-mount-point" +[ -d "$MP" ] && rmdir "$MP" + +test_rlMount(){ + mkdir "$MP" + mount() { return 0 ; } + assertTrue "rlMount returns 0 when internal mount succeeds" \ + "mount() { return 0 ; } ; rlMount server remote_dir $MP" + assertFalse "rlMount returns 1 when internal mount doesn't succeeds" \ + "mount() { return 4 ; } ; rlMount server remote_dir $MP" + rmdir "$MP" + unset mount +} + +test_rlMountAny(){ + assertTrue "rlmountAny is marked as deprecated" \ + "rlMountAny server remotedir $MP 2>&1 >&- |grep -q deprecated " +} + +test_rlAnyMounted(){ + assertTrue "rlAnymounted is marked as deprecated" \ + "rlAnyMounted server remotedir $MP 2>&1 >&- |grep -q deprecated " +} + +test_rlCheckMount(){ + MP1=$(readlink -m $MP) + [ -d "$MP" ] && rmdir "$MP1" + assertFalse "rlCheckMount returns non-0 on no-existing mount point" \ + "rlCheckMount server remotedir $MP1" + test_img=$( mktemp ) + dd if=/dev/zero of=$test_img count=1 bs=1 seek=1G + mkfs.ext4 -F $test_img + mkdir -p $MP1 + mount -o loop,noexec,rw $test_img $MP + mount + assertTrue "rlCheckMount returns 0 in existing mountpoint" \ + "rlCheckMount $MP1" + assertTrue "rlCheckMount returns 0 in existing mountpoint on existing target" \ + "rlCheckMount $test_img $MP1" + # no chance to test the third variant: no server-base mount is sure to be mounted + assertTrue "rlCheckMount returns 0 in existing mountpoint with correct option" \ + "rlCheckMount -o rw $MP1" + assertTrue "rlCheckMount returns 0 in existing mountpoint with correct options" \ + "rlCheckMount -o rw,noexec $MP1" + assertTrue "rlCheckMount returns 0 in existing mountpoint with correct options, reverted order" \ + "rlCheckMount -o noexec,rw $MP1" + assertTrue "rlCheckMount returns 0 in existing mountpoint with correct option" \ + "rlCheckMount -o rw $MP1" + assertTrue "rlCheckMount returns 0 in existing mountpoint and target with correct option" \ + "rlCheckMount -o rw $test_img $MP1" + assertFalse "rlCheckMount returns non-0 in existing mountpoint with incorrect option" \ + "rlCheckMount -o ro $MP1" 2 + assertFalse "rlCheckMount returns non-0 in existing mountpoint with incorrect options" \ + "rlCheckMount -o ro,nodev $MP1" 2 + assertFalse "rlCheckMount returns non-0 in existing mountpoint and target with incorrect options" \ + "rlCheckMount -o ro $test_img $MP1" 2 + umount $test_img + rm -f $test_img + rmdir "$MP1" +} + +test_rlAssertMount(){ + mkdir "$MP" + assertGoodBad "rlAssertMount server remote-dir $MP" 0 1 + assertFalse "rlAssertMount without paramaters doesn't succeed" \ + "rlAssertMount" + rmdir "$MP" "remotedir" +} + +test_rlSEBooleanTest() { + # this feature was dropped, now it is not tested + assertLog "skipping rlSEBooleanOn" SKIP + assertLog "skipping rlSEBooleanOff" SKIP + assertLog "skipping rlSEBooleanRestore" SKIP +} + +# NOTE: these two tests (Append/Prepend) verify ONLY the "no testwatcher" +# (bash only) scenario as incorporating the test watcher in this suite +# would be rather difficult +test_rlCleanupAppend() +{ + assertTrue 'journalReset' + local tmpfile=$(mktemp) + + assertTrue "rlCleanupAppend succeeds on initialized journal" "rlCleanupAppend \"echo -n one >> \\\"$tmpfile\\\"\"" + assertTrue "rlCleanupAppend issued a warning (no testwatcher)" \ + "rlJournalPrint | grep -q \"rlCleanupAppend: Running outside of the test watcher\"" + + silentIfNotDebug "rlCleanupAppend \"echo -n two >> '$tmpfile'\"" + + silentIfNotDebug "rlJournalEnd" + + assertTrue "Temporary file should contain 'onetwo' after rlJournalEnd" "grep -q 'onetwo' < \"$tmpfile\"" || cat "$tmpfile" + assertTrue "rlJournalEnd issued a warning (no testwatcher)" \ + "rlJournalPrint | grep -q \"rlJournalEnd: Not running in test watcher\"" + + rm -f "$tmpfile" +} +test_rlCleanupPrepend() +{ + assertTrue 'journalReset' + local tmpfile=$(mktemp) + + assertTrue "rlCleanupAppend succeeds on initialized journal" "rlCleanupAppend \"echo -n one >> \\\"$tmpfile\\\"\"" + assertTrue "rlCleanupAppend issued a warning (no testwatcher)" \ + "rlJournalPrint | grep -q \"rlCleanupAppend: Running outside of the test watcher\"" + + silentIfNotDebug "rlCleanupAppend \"echo -n two >> '$tmpfile'\"" + + #silentIfNotDebug "rlJournalEnd" + rlJournalEnd &> /dev/null + + assertTrue "Temporary file should contain 'twoone' after rlJournalEnd" "grep -q 'onetwo' < \"$tmpfile\"" || cat "$tmpfile" + assertTrue "rlJournalEnd issued a warning (no testwatcher)" \ + "rlJournalPrint | grep -q \"rlJournalEnd: Not running in test watcher\"" + + rm -f "$tmpfile" +} + diff --git a/src/test/journalTest.sh b/src/test/journalTest.sh new file mode 100644 index 0000000..6a3adba --- /dev/null +++ b/src/test/journalTest.sh @@ -0,0 +1,359 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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: +# Ales Zelinka +# Petr Splichal + +test_rlStartJournal(){ return 0; } # this is tested below, we keep this just for + # compatibility +test_rlJournalStart(){ + assertTrue "journal started" "rlJournalStart" + assertTrue "directory set & created" "[ -d $BEAKERLIB_DIR ]" + assertTrue "meta file created" "[ -f $__INTERNAL_BEAKERLIB_METAFILE ]" + __INTERNAL_JournalXMLCreate + assertTrue "journal file created" "[ -f $__INTERNAL_BEAKERLIB_JOURNAL ]" + assertTrue "journal is well-formed XML" "xmllint $__INTERNAL_BEAKERLIB_JOURNAL >/dev/null" + + # existing journal is not overwritten + silentIfNotDebug 'rlLog "I am"' + rlJournalStart + assertTrue "existing meta not overwritten" \ + "grep 'I\\\ am' $__INTERNAL_BEAKERLIB_METAFILE" + __INTERNAL_JournalXMLCreate + assertTrue "existing journal not overwritten" \ + "grep 'I am' $__INTERNAL_BEAKERLIB_JOURNAL" + + # if TESTID is unset, use user-provided BEAKERLIB_DIR, if available + local OLDTESTID="$TESTID" + unset TESTID + local OLDDIR="$BEAKERLIB_DIR" + local NEWDIR="$( mktemp -d /tmp/beakerlib-test-XXXXXXXX )" # no-reboot + export BEAKERLIB_DIR="$NEWDIR" + local OLDJOURNAL="$__INTERNAL_BEAKERLIB_JOURNAL" + local OLDMETA="$__INTERNAL_BEAKERLIB_METAFILE" + unset __INTERNAL_BEAKERLIB_JOURNAL __INTERNAL_BEAKERLIB_METAFILE + + journalReset + __INTERNAL_JournalXMLCreate + assertTrue "A new user-provided dir created when no TESTID available" \ + "[ '$BEAKERLIB_DIR' = '$NEWDIR' -a -d $BEAKERLIB_DIR ]" + assertTrue "A new metafile created in user-provided directory" \ + "[ '$__INTERNAL_BEAKERLIB_METAFILE' != '$OLDMETA' -a -f $__INTERNAL_BEAKERLIB_METAFILE ]" + assertTrue "A new journal created in user-provided directory" \ + "[ '$__INTERNAL_BEAKERLIB_JOURNAL' != '$OLDJOURNAL' -a -f $__INTERNAL_BEAKERLIB_JOURNAL ]" + + rm -rf "$NEWDIR" + export TESTID="$OLDTESTID" + export BEAKERLIB_DIR="$OLDDIR" + export __INTERNAL_BEAKERLIB_JOURNAL="$OLDJOURNAL" + export __INTERNAL_BEAKERLIB_METAFILE="$OLDMETA" + unset OLDTESTID OLDDIR NEWDIR OLDJOURNAL + + # if both TESTID and BEAKERLIB_DIR are unset, a temp dir should be created + local OLDTESTID="$TESTID" + unset TESTID + local OLDDIR="$BEAKERLIB_DIR" + unset BEAKERLIB_DIR + local OLDJOURNAL="$__INTERNAL_BEAKERLIB_JOURNAL" + local OLDMETA="$__INTERNAL_BEAKERLIB_METAFILE" + unset __INTERNAL_BEAKERLIB_JOURNAL __INTERNAL_BEAKERLIB_METAFILE + + journalReset + __INTERNAL_JournalXMLCreate + assertTrue "A new random dir created when no TESTID available" \ + "[ '$BEAKERLIB_DIR' -a -d $BEAKERLIB_DIR ]" + assertTrue "A new metafile created in random directory" \ + "[ '$__INTERNAL_BEAKERLIB_METAFILE' != '$OLDMETA' -a -f $__INTERNAL_BEAKERLIB_METAFILE ]" + assertTrue "A new journal created in random directory" \ + "[ '$__INTERNAL_BEAKERLIB_JOURNAL' != '$OLDJOURNAL' -a -f $__INTERNAL_BEAKERLIB_JOURNAL ]" + + rm -rf "$BEAKERLIB_DIR" + export TESTID="$OLDTESTID" + export BEAKERLIB_DIR="$OLDDIR" + export __INTERNAL_BEAKERLIB_JOURNAL="$OLDJOURNAL" + export __INTERNAL_BEAKERLIB_METAFILE="$OLDMETA" + unset OLDTESTID OLDDIR OLDJOURNAL +} + +test_rlJournalPrint(){ + #add something to journal + silentIfNotDebug "rlPhaseStart FAIL" + silentIfNotDebug 'rlAssert0 "failed" 1' + silentIfNotDebug 'rlAssert0 "passed" 0' + silentIfNotDebug 'rlPhaseEnd' + silentIfNotDebug 'rlLog "loginek"' + assertTrue "rlJournalPrint dump is wellformed xml" \ + "rlJournalPrint |xmllint -" + assertTrue "rlJournalPrint raw dump is wellformed xml" \ + "rlJournalPrint raw |xmllint -" + assertTrue "rlPrintJournal emits obsolete warnings" \ + "rlPrintJournal 2>&1 | grep 'rlPrintJournal is obsoleted by rlJournalPrint' -q" + rm -rf $BEAKERLIB_DIR +} + +test_rlJournalPrintText(){ + #this fnc is used a lot in other function's tests + #so here goes only some specific (regression?) tests + + #must not tracedump on an empty log message + #outside-of-phase log + silentIfNotDebug 'rlLog ""' + silentIfNotDebug 'rlPhaseStart FAIL' + #inside-phase log + silentIfNotDebug 'rlLog ""' + assertFalse "no traceback during log creation" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback during log creation" \ + "echo \"\$out\" | grep Traceback" + journalReset + + #no traceback on non-ascii characters (bz471257) + silentIfNotDebug 'rlPhaseStart FAIL' + assertTrue "no traceback on non-ascii chars (rlLog)" \ + 'rlLog "ščřžýáíéーれっどはっと"' + assertFalse "no traceback on non-ascii chars (rlJournalPrintText)" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback on non-ascii chars (__INTERNAL_JournalXMLCreate)" \ + "echo \"\$out\" | grep Traceback" + rm -rf $BEAKERLIB_DIR + + # no traceback on non-xml garbage + journalReset + silentIfNotDebug 'rlPhaseStart FAIL' + local X00="$( echo $'\x00' )" + assertTrue "no traceback on non-xml characters [1] (rlLog)" "rlLog '$X00'" + assertFalse "no traceback on non-xml characters [1] (rlJournalPrintText)" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback on non-xml characters [1] (__INTERNAL_JournalXMLCreate)" \ + "echo \"\$out\" | grep Traceback" + journalReset + silentIfNotDebug 'rlPhaseStart FAIL' + local X0C="$( echo $'\x0c' )" + assertTrue "no traceback on non-xml characters [2] (rlLog)" "rlLog '$X0C'" + assertFalse "no traceback on non-xml characters [2] (rlJournalPrintText)" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback on non-xml characters [2] (__INTERNAL_JournalXMLCreate)" \ + "echo \"\$out\" | grep Traceback" + journalReset + silentIfNotDebug 'rlPhaseStart FAIL' + local X1F="$( echo $'\x1F' )" + assertTrue "no traceback on non-xml characters [3] (rlLog)" "rlLog '$X1F'" + assertFalse "no traceback on non-xml characters [3] (rlJournalPrintText)" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback on non-xml characters [3] (__INTERNAL_JournalXMLCreate)" \ + "echo \"\$out\" | grep Traceback" + journalReset + silentIfNotDebug 'rlPhaseStart FAIL' + local FF="$( echo $'\xFF' )" + assertTrue "rlLog '\\xFF' does not give a traceback" \ + "rlLog '$FF' &> /dev/null" + assertTrue "rlPass '\\xFF' does not give a traceback" \ + "rlPass '$FF' &> /dev/null" + assertFalse "no traceback on non-xml characters [4] (rlJournalPrintText)" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "no traceback on non-xml characters [4] (__INTERNAL_JournalXMLCreate)" \ + "echo \"\$out\" | grep Traceback" + local A2=$(mktemp) + journalReset + silentIfNotDebug 'rlPhaseStart FAIL' + cat > $A2 << EOF +in: 0x2083010, size: 12, use: 8 +out: 0x2083060, size: 24, use: 0 +handler: 0x2083be0, name: ISO-8859-5, input: (nil), iconv_in: 0x2083c30 +xmlCharEncInFunc result: 21 +out: 0x2083060, size: 24, use: 21 +e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) e2 (�) 84 (�) 96 (�) +EOF + assertTrue "rlLog with specific UTF-8 characters won't give a traceback" \ + "rlLog '$(cat $A2 )' &>/dev/null" + assertFalse "rlJournalPrintText won't give a traceback" \ + "rlJournalPrintText 2>&1 | grep Traceback" + out="$(rlJournalPrint 2>&1)" + silentIfNotDebug 'echo "$out"' + assertFalse "__INTERNAL_JournalXMLCreate won't give a traceback" \ + "echo \"\$out\" | grep Traceback" + rm -f $A2 + rm -rf $BEAKERLIB_DIR + + # multiline logs + journalReset + + local MULTILINE="$( echo -e 'line1\nline2' )" + silentIfNotDebug "rlLog '$MULTILINE'" + rlJournalPrint + rlJournalPrintText | grep -v "line2" | grep -q "LOG.*line1" && + rlJournalPrintText | grep -v "line1" | grep -q "LOG.*line2" + assertTrue "multiline logs tagged on each line" "[ $? -eq 0 ]" + rm -rf $BEAKERLIB_DIR + + # obsoleted rlCreateLogFromJournal still works + journalReset + assertTrue "Checking the rlCreateLogFromJournal still works" \ + "rlCreateLogFromJournal | grep -q 'TEST PROTOCOL'" + assertTrue "Obsoleted message for rlCreateLogFromJournal" \ + "rlCreateLogFromJournal | grep -q 'obsoleted by rlJournalPrintText'" + rm -rf $BEAKERLIB_DIR + + # whole test summary (Bug 464155 - [RFE] summary of phase results in logfile) + ( journalReset + rlPhaseStart FAIL failed ; rlAssert0 "assert" 1 ; rlAssert0 "assert" 1 ; rlPhaseEnd; + rlPhaseStart FAIL failed2 ; rlAssert0 "assert" 1 ; rlPhaseEnd; + rlJournalEnd; ) &>/dev/null + assertTrue "failed test counted in summary" "rlJournalPrintText |grep 'Phases: 0 good, 2 bad'" + assertTrue "whole test reported as FAILed" "rlJournalPrintText |grep '\[ *FAIL *\].* RESULT: beakerlib-unit-tests'" + rm -rf $BEAKERLIB_DIR + ( journalReset + rlPhaseStart FAIL passed ; rlAssert0 "assert" 0 ; rlPhaseEnd + rlPhaseStart FAIL passed2 ; rlAssert0 "assert" 0 ; rlPhaseEnd + rlJournalEnd; ) &>/dev/null + assertTrue "passed test counted in summary" "rlJournalPrintText |grep 'Phases: 2 good, 0 bad'" + assertTrue "whole test reported as PASSed" "rlJournalPrintText |grep '\[ *PASS *\].* RESULT: beakerlib-unit-tests'" + rm -rf $BEAKERLIB_DIR + ( journalReset + rlPhaseStart FAIL passed ; rlAssert0 "assert" 0 ; rlPhaseEnd + rlPhaseStart FAIL failed ; rlAssert0 "assert" 1 ; rlPhaseEnd + rlPhaseStart FAIL passed2 ; rlAssert0 "assert" 0 ; rlPhaseEnd + rlJournalEnd; ) &>/dev/null + assertTrue "both failed and passed phases counted in summary" "rlJournalPrintText |grep 'Phases: 2 good, 1 bad'" + assertTrue "whole test reported as FAILed" "rlJournalPrintText |grep '\[ *FAIL *\].* RESULT: beakerlib-unit-tests'" + rm -rf $BEAKERLIB_DIR + } + +test_journalOptionalFields() { + rm -rf $BEAKERLIB_DIR + + export testversion="t3st.1.2.3" + export packagename="glibc" + journalReset &>/dev/null + silentIfNotDebug 'rlRun "true"' + rlJournalEnd &>/dev/null + + assertTrue "Checking the rlJournalPrintText does show CPU line" \ + "rlJournalPrintText | grep 'CPUs'" + assertTrue "Checking the rlJournalPrintText does show RAM line" \ + "rlJournalPrintText | grep 'RAM size'" + assertTrue "Checking the rlJournalPrintText does show HDD line" \ + "rlJournalPrintText | grep 'HDD size'" + assertTrue "Checking the rlJournalPrintText --full-journal shows CPU line" \ + "rlJournalPrintText --full-journal | grep 'CPUs'" + assertTrue "Checking the rlJournalPrintText --full-journal shows RAM line" \ + "rlJournalPrintText --full-journal | grep 'RAM size'" + assertTrue "Checking the rlJournalPrintText --full-journal shows HDD line" \ + "rlJournalPrintText --full-journal | grep 'HDD size'" + assertTrue "rlJournalPrintText shows beakerlib version" \ + "rlJournalPrintText | grep 'beakerlib RPM'" + assertTrue "rlJournalPrintText shows beakerlib-redhat version" \ + "rlJournalPrintText | grep 'bl-redhat RPM'" + assertTrue "rlJournalPrintText shows test version" \ + "rlJournalPrintText | grep 'Test version : $testversion'" + assertTrue "rlJournalPrintText shows test built date" \ + "rlJournalPrintText | grep 'Test built :'" + + unset testversion + unset packagename + journalReset &>/dev/null + silentIfNotDebug 'rlRun "true"' + rlJournalEnd &>/dev/null + + assertFalse "rlJournalPrintText does not show test version" \ + "rlJournalPrintText | grep 'Test version : $testversion'" + assertFalse "rlJournalPrintText does not show test built date" \ + "rlJournalPrintText | grep 'Test built :'" +} + +test_rlGetTestState(){ + #test this in developer mode to verify BZ#626953 + TESTID_BACKUP=$TESTID + unset TESTID + journalReset + assertRun "rlPhaseStart FAIL phase1" + rlGetTestState ; assertTrue "rlGetTestState return 0 at the beginning of the test" "[ $? -eq 0 ]" + rlGetPhaseState ; assertTrue "rlGetPhaseState return 0 at the beginning of the test" "[ $? -eq 0 ]" + assertRun 'rlAssert0 "failing assert#1" 1' 1 + rlGetTestState ; assertTrue "rlGetTestState return 1 after assert failed" "[ $? -eq 1 ]" + rlGetPhaseState ; assertTrue "rlGetPhaseState return 1 after assert failed" "[ $? -eq 1 ]" + assertRun 'rlAssert0 "failing assert#2" 1' 1 + rlGetTestState ; assertTrue "rlGetTestState return 2 after assert failed" "[ $? -eq 2 ]" + rlGetPhaseState ; assertTrue "rlGetPhaseState return 2 after assert failed" "[ $? -eq 2 ]" + assertRun 'for i in $(seq 3 260) ; do rlAssert0 "failing assert#$i" 1; done' 1 "Creating 260 failed asserts" + rlGetTestState ; assertTrue "rlGetTestState return 255 after more that 255 asserts failed" "[ $? -eq 255 ]" + rlGetPhaseState ; assertTrue "rlGetPhaseState return 255 after more that 255 asserts failed" "[ $? -eq 255 ]" + assertRun "rlPhaseEnd" + + assertRun "rlPhaseStart FAIL phase2" + rlGetTestState ; assertTrue "rlGetTestState return non-zero in passing phase but failing test" "[ $? -ne 0 ]" + rlGetPhaseState ; assertTrue "rlGetPhaseState return 0 in passing phase but failing test" "[ $? -eq 0 ]" + assertRun "rlPhaseEnd" + TESTID=$TESTID_BACKUP +} + +test_rlGetPhaseState(){ + assertLog "Tests for this function are included in rlGetTestState since it is more or less the same" +} + +test_packageLogging(){ + journalReset + silentIfNotDebug 'rlPhaseStartTest' + silentIfNotDebug 'rlAssertRpm glibc' + out=$(rlJournalPrint raw) + assertTrue "One tag immediately after rlAssertRpm" "echo \"\$out\" | grep -o -n ']*>glibc'" + TAGS="$(echo \"\$out\" | grep -o -n ']*>glibc' | wc -l)" + silentIfNotDebug 'rlPhaseEnd' + silentIfNotDebug 'rlPhaseStartTest' + assertTrue "More tag immediately after new phase starts" "[ $( rlJournalPrint raw | grep -o -n ']*>glibc' | wc -l ) -gt $TAGS ]" + silentIfNotDebug 'rlPhaseEnd' +} + +test_packageLoggingNotPresent() { + export PACKAGE="IreallyHOPEnoPACKAGElikeTHISwillEVERexist" + journalReset + out="$(rlJournalPrint raw)" + assertTrue "Non-installed package is marked as such" "echo \"\$out\" | grep -Eo -n '[^<>]*>IreallyHOPEnoPACKAGElikeTHISwillEVERexist'" + assertFalse "Non-installed package is not marked as installed" "echo \"\$out\" | grep -o -n '[^<>]*>IreallyHOPEnoPACKAGElikeTHISwillEVERexist'" + + export PACKAGE="glibc" + journalReset + out="$(rlJournalPrint raw)" + assertTrue "Installed package is marked as such" "echo \"\$out\" | grep -o -n ']*>glibc'" + assertFalse "Installed package is not marked as non-installed" "echo \"\$out\" | grep -o -n '[^<>]*>glibc'" + unset PACKAGE +} + +test_packageLoggingGuess() { + unset PACKAGE + export TEST="/some/glibc/Regression/test" + journalReset + assertTrue "Package name was correctly guessed from TEST" "rlJournalPrint raw | grep -q -o -n ']*>glibc'" + + unset TEST + journalReset + assertFalse "No tag when TEST and PACKAGE are not set" "rlJournalPrint | grep -q ''" + + export TEST="weeee some garbage" + journalReset + assertTrue "rlJournalStart survives garbage in TEST" "rlJournalStart" + assertFalse "No tag when TEST is garbage" "rlJournalPrint | grep -q ''" +} diff --git a/src/test/libraryTest.sh b/src/test/libraryTest.sh new file mode 100644 index 0000000..b1a13a8 --- /dev/null +++ b/src/test/libraryTest.sh @@ -0,0 +1,331 @@ +# Copyright (c) 2012 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Petr Muller + +__INTERNAL_TEST_PATH="tested/Sanity/dummy" +__INTERNAL_ILIB_PATH="tested/Library/dummy" +__INTERNAL_ELIB_PATH="external/Library/dummy" + +__INTERNAL_TEST_TEMPLATE="$( mktemp )" # no-reboot +__INTERNAL_LIB_TEMPLATE="$( mktemp )" # no-reboot + +__INTERNAL_ILIB_ID="tested/dummy" +__INTERNAL_ILIB_PREFIX="testedummy" + +__INTERNAL_ELIB_ID="external/dummy" +__INTERNAL_ELIB_PREFIX="externaldummy" + +createTemplate(){ + cat > $__INTERNAL_TEST_TEMPLATE << EOF +#!/bin/bash +export TESTID='library-test' +export TEST='beakerlib-library-test' + +export BEAKERLIB=BEAKERLIB-ANCHOR +. BEAKERLIB-ANCHOR/beakerlib.sh +export __INTERNAL_JOURNALIST="$BEAKERLIB/python/journalling.py" + +RETVAL=0 + +DEBUG=1 + +COMMAND-ANCHOR + +rlJournalStart + rlPhaseStartSetup + if ! rlImport LIBRARY-ANCHOR + then + RETVAL=1 + echo "rlImport failed" >&2 + fi + + if [ "\$RETVAL" != 1 ] && ! eval PREFIX-ANCHORFunction + then + RETVAL=1 + echo "Function was not found even when rlImport PASSed" >&2 + fi + rlPhaseEnd +rlJournalEnd +rm -rf \$BEAKERLIB_DIR +exit \$RETVAL +EOF +} + +createLibraryTemplate(){ + cat > $__INTERNAL_LIB_TEMPLATE << EOF +#!/bin/bash +# library-prefix = PREFIX-ANCHOR + +PREFIX-ANCHORFunction() { return 0; } +PREFIX-ANCHORLibraryLoaded() { return 0; } +EOF +} + +spawnTest(){ + local TESTFILE="$1" + local BEAKERLIB_PATH="$2" + local LIBRARY="$3" + local PREFIX="$4" + local COMMAND="$5" + local FUNCTION="$6" + + mkdir -p "$( dirname $TESTFILE )" + cat $__INTERNAL_TEST_TEMPLATE > $TESTFILE + + [[ -n "$FUNCTION" ]] && sed -i -e "s|PREFIX-ANCHORFunction|$FUNCTION|g" $TESTFILE + sed -i -e "s|PREFIX-ANCHOR|$PREFIX|g" $TESTFILE + sed -i -e "s|LIBRARY-ANCHOR|$LIBRARY|g" $TESTFILE + sed -i -e "s|BEAKERLIB-ANCHOR|$BEAKERLIB_PATH|g" $TESTFILE + sed -i -e "s|COMMAND-ANCHOR|$COMMAND|g" $TESTFILE + + chmod a+x $TESTFILE + [[ "$DEBUG" == "1" ]] && cat $TESTFILE +} + +spawnLibrary(){ + local LIBDIR="$1" + local PREFIX="$2" + + mkdir -p $LIBDIR + + cat $__INTERNAL_LIB_TEMPLATE > $LIBDIR/lib.sh + + sed -i -e "s|PREFIX-ANCHOR|$PREFIX|g" $LIBDIR/lib.sh + + [[ "$DEBUG" == "1" ]] && cat $LIBDIR/lib.sh +} + +spawnStructure(){ + local ROOTDIR="$1" + mkdir -p "$ROOTDIR" +} + +genericSetup(){ + createTemplate + createLibraryTemplate + spawnStructure "$1" +} + +genericTeardown(){ + rm -f ${__INTERNAL_TEST_TEMPLATE} + rm -f ${__INTERNAL_LIB_TEMPLATE} + rm -rf "$1" +} + +templateTest(){ + local LIB_ID="$1" + local LIB_PR="$2" + local LIB_PA="$3" + + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$LIB_ID" "$LIB_PR" + spawnLibrary "$ROOT/$LIB_PA" "$LIB_PR" + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + if [ $5 -eq 0 ] + then + assertTrue "Checking rlImport: $4" ./test.sh + else + assertFalse "Checking rlImport: $4" ./test.sh + fi + + popd >/dev/null + genericTeardown "$ROOT" +} + +test_WeirdNames(){ + for weird_character in "_" "-" "+" "5" "." + do + local weird_libname="weird${weird_character}library" + local weird_component="weird${weird_character}component" + + templateTest "tested/$weird_libname" "weirdlib" "tested/Library/$weird_libname" "Library with '$weird_character' in name" 0 + templateTest "$weird_component/weirdlib" "weirdlib" "$weird_component/Library/weirdlib" "Library in component with '$weird_character' in name" 0 + done +} + +test_LibrarySimple(){ + templateTest "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" "$__INTERNAL_ILIB_PATH" "Internal library" 0 + templateTest "$__INTERNAL_ELIB_ID" "$__INTERNAL_ELIB_PREFIX" "$__INTERNAL_ELIB_PATH" "External library" 0 + templateTest "$__INTERNAL_ILIB_ID" "666thebumberofthebeast" "$__INTERNAL_ILIB_PATH" "Fail for invalid prefix" 1 + templateTest "bad/path" "$__INTERNAL_ILIB_PREFIX" "$__INTERNAL_ILIB_PATH" "Fail for library not found" 1 + templateTest "invalid" "$__INTERNAL_ILIB_PREFIX" "$__INTERNAL_ILIB_PATH" "Fail for invalid ID 1" 1 + templateTest "$__INTERNAL_ILIB_ID/bad" "$__INTERNAL_ILIB_PREFIX" "$__INTERNAL_ILIB_PATH" "Fail for invalid ID 2" 1 +} + +test_OutsideTestRun(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + + assertTrue "Checking rlImport: test run from outside its directory" $ROOT/$__INTERNAL_TEST_PATH/test.sh + + genericTeardown "$ROOT" +} + +test_ImportAllNoLib(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "--all" "$__INTERNAL_ILIB_PREFIX" '' true + echo "" > "$ROOT/$__INTERNAL_TEST_PATH/Makefile" + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + assertTrue "Checking rlImport --all" ./test.sh + popd >/dev/null + + genericTeardown "$ROOT" +} + +test_ImportAll(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "--all" "$__INTERNAL_ILIB_PREFIX" + echo "RhtsRequires: library($__INTERNAL_ILIB_ID)" > "$ROOT/$__INTERNAL_TEST_PATH/Makefile" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + assertTrue "Checking rlImport --all" ./test.sh + popd >/dev/null + + genericTeardown "$ROOT" +} + +test_ImportAllAfterPushd(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "--all" "$__INTERNAL_ILIB_PREFIX" "pushd $(mktemp -d)" + echo "RhtsRequires: library($__INTERNAL_ILIB_ID)" > "$ROOT/$__INTERNAL_TEST_PATH/Makefile" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + assertTrue "Checking rlImport --all: test does pushd before rlImport --all" ./test.sh + popd >/dev/null + + genericTeardown "$ROOT" +} + +test_DifferentRoot(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + + local DIFFERENT_ROOT=$(mktemp -d) # no-reboot + mkdir -p $DIFFERENT_ROOT/tested/Library + mv $ROOT/$__INTERNAL_ILIB_PATH $DIFFERENT_ROOT/tested/Library/ + + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + export BEAKERLIB_LIBRARY_PATH=$DIFFERENT_ROOT + assertTrue "Checking rlImport: Library in BEAKERLIB_LIBRARY_PATH is found" ./test.sh + unset BEAKERLIB_LIBRARY_PATH + + popd >/dev/null + genericTeardown "$ROOT" + rm -rf $DIFFERENT_ROOT +} + +test_DifferentRootWithNamespace(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + + local DIFFERENT_ROOT=$(mktemp -d) # no-reboot + mkdir -p $DIFFERENT_ROOT/CoreOS/tested/Library + mv $ROOT/$__INTERNAL_ILIB_PATH $DIFFERENT_ROOT/CoreOS/tested/Library/ + + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + export BEAKERLIB_LIBRARY_PATH=$DIFFERENT_ROOT + assertTrue "Checking rlImport: Namespaced library in BEAKERLIB_LIBRARY_PATH is found" ./test.sh + unset BEAKERLIB_LIBRARY_PATH + + popd >/dev/null + genericTeardown "$ROOT" + rm -rf $DIFFERENT_ROOT +} + +test_MissingLibraryLoadedInLib(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + sed -i -e 's/LibraryLoaded/NoLibraryLoaded/g' $ROOT/$__INTERNAL_ILIB_PATH/lib.sh + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + assertFalse "Checking rlImport: Fail if prefixLibraryLoaded function is missing" ./test.sh + + popd >/dev/null + genericTeardown "$ROOT" +} + +test_MultipleImports(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID $__INTERNAL_ELIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ELIB_PATH" "$__INTERNAL_ELIB_PREFIX" + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + assertTrue "Checking rlImport: Multiple correct imports" ./test.sh + + popd >/dev/null + genericTeardown "$ROOT" + + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID $__INTERNAL_ELIB_ID" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + spawnLibrary "$ROOT/$__INTERNAL_ELIB_PATH" "$__INTERNAL_ELIB_PREFIX" + sed -i -e 's/LibraryLoaded/NoLibraryLoaded/g' $ROOT/$__INTERNAL_ILIB_PATH/lib.sh + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + assertFalse "Checking rlImport: Fail for multiple imports, one of them bad" ./test.sh + + popd >/dev/null + genericTeardown "$ROOT" + +} + + +test_LibraryDir(){ + local ROOT=$(mktemp -d) # no-reboot + local TESTFILE="$ROOT/$__INTERNAL_TEST_PATH/test.sh" + genericSetup "$ROOT" + spawnTest "$TESTFILE" "$(pwd)/.." "$__INTERNAL_ILIB_ID" "$__INTERNAL_ILIB_PREFIX" '' "[[ -n \\\\\"\$${__INTERNAL_ILIB_PREFIX}LibraryDir\\\\\" \\\\\&\\\\\& \\\\\"\$${__INTERNAL_ILIB_PREFIX}LibraryDir\\\\\" == \\\\\"$ROOT/$__INTERNAL_ILIB_PATH\\\\\" ]]" + spawnLibrary "$ROOT/$__INTERNAL_ILIB_PATH" "$__INTERNAL_ILIB_PREFIX" + pushd $ROOT/$__INTERNAL_TEST_PATH >/dev/null + + assertTrue "Checking rlImport: prepares PREFIXLibraryDir variable pointing to library's folder" ./test.sh + + popd >/dev/null + genericTeardown "$ROOT" +} + + diff --git a/src/test/loggingTest.sh b/src/test/loggingTest.sh new file mode 100644 index 0000000..debe33f --- /dev/null +++ b/src/test/loggingTest.sh @@ -0,0 +1,465 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Jan Hutar + +__testLogFce() { + # This should help us to test various logging functions + # which takes and optional parameters + local log=$( mktemp ) # no-reboot + local myfce=$1 + $myfce "MessageABC" &>/dev/null + assertTrue "$myfce to OUTPUTFILE" "grep -q 'MessageABC' $OUTPUTFILE" + rm -f $log # remove the log, so it have to be created + $myfce "MessageDEF" $log &>/dev/null + assertTrue "$myfce to nonexisting log" "grep -q 'MessageDEF' $log" + touch $log # create the log if it still do not exists + $myfce "MessageGHI" $log &>/dev/null + assertTrue "$myfce to existing log" "grep -q 'MessageGHI' $log" + $myfce "MessageJKL" $log &>/dev/null + assertTrue "$myfce only adds to the log (do not overwrite it)" "grep -q 'MessageGHI' $log" + assertTrue "$myfce adds to the log" "grep -q 'MessageJKL' $log" + assertTrue "$myfce logs to STDERR" "$myfce $myfce-MNO 2>&1 >&- |grep -q '$myfce-MNO'" + assertTrue "$myfce creates journal entry" "rlJournalPrint |grep -q '$myfce-MNO'" + $myfce "-test messages beginning with '-'" $log &>/dev/null + assertTrue "$myfce test messages beginnig with '-'" "grep -q -- '-test messages beginning with' $log" + rm -f $log +} + +test_rlHeadLog() { + __testLogFce rlHeadLog +} + +test_rlLog() { + __testLogFce rlLog + local log=$( mktemp ) # no-reboot + rlLog "test" $log "prio" &>/dev/null + silentIfNotDebug "cat $log" + assertTrue "rlLog \"test\" \$log \"prio\" produces :: [ ([0-9]{2}:){2}[0-9]{2} ] :: [ prio ] :: test" "grep -qP -- ':: \[ ([0-9]{2}:){2}[0-9]{2} \] :: \[ prio \] :: test' $log" + rm -f $log +} +test_rlLogDebug() { + #only works when DEBUG is set + local tmp=$DEBUG + DEBUG=1 + __testLogFce rlLogDebug + DEBUG=$tmp +} +test_rlLogInfo() { + __testLogFce rlLogInfo +} +test_rlLogWarning() { + __testLogFce rlLogWarning +} +test_rlLogError() { + __testLogFce rlLogError +} +test_rlLogFatal() { + __testLogFce rlLogFatal +} + +test_rlDie(){ + #dunno how to test this - it contains untestable helpers like rlBundleLogs and rlReport + assertLog "rlDie skipped" "SKIP" +} + +test_rlPhaseStartEnd(){ + silentIfNotDebug "rlPhaseStart FAIL" + #counting passes and failures + silentIfNotDebug 'rlAssert0 "failed assert #1" 1' + silentIfNotDebug 'rlAssert0 "successfull assert #1" 0' + silentIfNotDebug 'rlAssert0 "failed assert #2" 1' + silentIfNotDebug 'rlAssert0 "successfull assert #2" 0' + silentIfNotDebug "rlPhaseEnd" + assertTrue "passed asserts were stored" "rlJournalPrintText |grep '2 good'" + assertTrue "failed asserts were stored" "rlJournalPrintText |grep '2 bad'" + #new phase resets score + silentIfNotDebug "rlPhaseStart FAIL" + silentIfNotDebug "rlPhaseEnd" + assertTrue "passed asserts were reseted" "rlJournalPrintText |grep '0 good'" + assertTrue "failed asserts were reseted" "rlJournalPrintText |grep '0 bad'" + silentIfNotDebug "rlPhaseEnd" + + # check phase names are properly mangled to Beaker result names + silentIfNotDebug "rlPhaseStart FAIL 'Phase 2: Electric Boogaloo'" + export BEAKERLIB_COMMAND_REPORT_RESULT=rhts-report-result # fake function + local out="$(rlPhaseEnd 2>&1)" + unset BEAKERLIB_COMMAND_REPORT_RESULT + assertTrue "phase end reported correct Beaker result" "grep -q 'ANCHOR NAME: Phase-2-Electric-Boogaloo' <<<\"$out\"" + + assertFalse "creating phase without type doesn't succeed" "rlPhaseEnd ; silentIfNotDebug 'rlPhaseStart'" + assertFalse "phase without type is not inserted into journal" "rlJournalPrint | grep -q ' /dev/null + assertTrue "setup phase with WARN type found in journal" "rlJournalPrint |grep -q ' /dev/null + assertTrue "test phase with FAIL type found in journal" "rlJournalPrint |grep -q ' /dev/null + assertTrue "clean-up phase with WARN type found in journal" "rlJournalPrint |grep -q ' /dev/null + assertTrue "rlLogHighMetric is marked as deprecated" \ + "rlLogHighMetric MTR-HIGH-OLD 1 2>&1 >&- |grep -q deprecated" + assertTrue "rlLogLowMetric is marked as deprecated" \ + "rlLogLowMetric MTR-LOW-OLD 1 2>&1 >&- |grep -q deprecated" +} +test_rlShowPkgVersion(){ + assertTrue "rlShowPkgVersion is marked as deprecated" \ + "rlShowPkgVersion 2>&1 >&- |grep -q obsoleted" + rlPhaseEnd +} + + +test_LogMetricLowHigh(){ + rlPhaseStart FAIL &> /dev/null + assertTrue "low metric inserted to journal" "rlLogMetricLow metrone 123 " + assertTrue "high metric inserted to journal" "rlLogMetricHigh metrtwo 567" + silentIfNotDebug "__INTERNAL_JournalXMLCreate" + assertTrue "low metric found in journal" "cat $__INTERNAL_BEAKERLIB_JOURNAL | xmllint --format - | grep ' /dev/null + silentIfNotDebug "rlLogMetricLow metrone 345" + rlPhaseEnd &> /dev/null ; rlPhaseStartTest phase-2 &> /dev/null + silentIfNotDebug "rlLogMetricLow metrone 345" + silentIfNotDebug "__INTERNAL_JournalXMLCreate" + assertTrue "metric insertion succeeds when name's not unique but phases differ" \ + "[ $(cat $__INTERNAL_BEAKERLIB_JOURNAL | xmllint --format - | grep -c ' /dev/null + rlShowRunningKernel &> /dev/null + assertTrue "kernel version is logged" "__INTERNAL_JournalXMLCreate; cat $__INTERNAL_BEAKERLIB_JOURNAL | xmllint --format - |grep -q $(uname -r)" +} + +__checkLoggedPkgInfo() { + local log=$1 + local msg=$2 + local name=$3 + local version=$4 + local release=$5 + local arch=$6 + assertTrue "rlShowPackageVersion logs name $msg" "grep -q '$name' $log" + assertTrue "rlShowPackageVersion logs version $msg" "grep -q '$version' $log" + assertTrue "rlShowPackageVersion logs release $msg" "grep -q '$release' $log" + assertTrue "rlShowPackageVersion logs arch $msg" "grep -q '$arch' $log" +} + +test_rlShowPackageVersion() { + local log=$( mktemp ) # no-reboot + local list=$( mktemp ) # no-reboot + + # Exit value shoud be defined + assertFalse "rlShowPackageVersion calling without options" "rlShowPackageVersion" + : >$OUTPUTFILE + + rpm -qa --qf "%{NAME}\n" > $list + local first=$( tail -n 1 $list ) + local first_n=$( rpm -q $first --qf "%{NAME}\n" | tail -n 1 ) + local first_v=$( rpm -q $first --qf "%{VERSION}\n" | tail -n 1 ) + local first_r=$( rpm -q $first --qf "%{RELEASE}\n" | tail -n 1 ) + local first_a=$( rpm -q $first --qf "%{ARCH}\n" | tail -n 1 ) + + # Test with 1 package + rlShowPackageVersion $first &>/dev/null + __checkLoggedPkgInfo $OUTPUTFILE "of 1 pkg" $first_n $first_v $first_r $first_a + : >$OUTPUTFILE + + # Test with package this_package_do_not_exist + assertTrue 'rlShowPackageVersion returns 1 when package do not exists' 'rlShowPackageVersion this_package_do_not_exist; [ $? -eq 1 ]' # please use "'" - we do not want "$?" to be expanded too early + assertTrue 'rlShowPackageVersion logs warning about this_package_do_not_exist' "grep -q 'this_package_do_not_exist' $OUTPUTFILE" + : >$OUTPUTFILE + + # Test with few packages + local few=$( tail -n 10 $list ) + rlShowPackageVersion $few &>/dev/null + for one in $few; do + local one_n=$( rpm -q $one --qf "%{NAME}\n" | tail -n 1 ) + local one_v=$( rpm -q $one --qf "%{VERSION}\n" | tail -n 1 ) + local one_r=$( rpm -q $one --qf "%{RELEASE}\n" | tail -n 1 ) + local one_a=$( rpm -q $one --qf "%{ARCH}\n" | tail -n 1 ) + __checkLoggedPkgInfo $OUTPUTFILE "of few pkgs" $one_n $one_v $one_r $one_a + done + : >$OUTPUTFILE + + # Test with package this_package_do_not_exist + assertFalse 'rlShowPackageVersion returns 1 when some packages do not exists' "rlShowPackageVersion this_package_do_not_exist $(echo $few) this_package_do_not_exist_too" + : >$OUTPUTFILE + + rm -f $list +} + + + +test_rlGetArch() { + local out=$(rlGetArch 2>/dev/null) + assertTrue 'rlGetArch returns 0' "[ $? -eq 0 ]" + [ "$out" = 'i386' ] && out='i.8.' # funny reg exp here + uname -a | grep -q "$out" + assertTrue 'rlGetArch returns correct arch' "[ $? -eq 0 ]" + assertTrue 'rlGetArch warns about deprecation' "rlJournalPrintText | grep 'This function is deprecated'" + assertTrue 'rlGetArch suggests use rlGetPrimaryArch' "rlJournalPrintText | grep 'Update test to use rlGetPrimaryArch/rlGetSecondaryArch'" +} + +test_rlGetDistroRelease() { + local out=$(rlGetDistroRelease) + assertTrue 'rlGetDistroRelease returns 0' "[ $? -eq 0 ]" + if [ -e /etc/redhat-release ] + then + grep -q -i "$out" /etc/redhat-release + assertTrue 'rlGetDistroRelease returns release which is in the /etc/redhat-release' "[ $? -eq 0 ]" + fi +} + +test_rlGetDistroVariant() { + local out=$(rlGetDistroVariant) + assertTrue 'rlGetDistroVariant returns 0' "[ $? -eq 0 ]" + if [ -e /etc/redhat-release ] + then + grep -q -i "$out" /etc/redhat-release + assertTrue 'rlGetDistroRelease returns variant which is in the /etc/redhat-release' "[ $? -eq 0 ]" + fi +} + +test_rlBundleLogs() { + local prefix=rlBundleLogs-unittest + rm -rf $prefix* CP-$prefix*.tar.gz + # Prepare files which will be backed up + mkdir $prefix + mkdir $prefix/first + echo "hello" > $prefix/first/greet + echo "world" > $prefix/first_greet + export BEAKERLIB_COMMAND_SUBMIT_LOG=rhts_submit_log + # Prepare fake rhts_submit_log utility + + cat <$prefix/rhts_submit_log +#!/bin/sh +while [ \$# -gt 0 ]; do + case "\$1" in + -S|-T) shift; ;; + -l) shift; cp "\$1" "CP-\$( basename \$1 )";; + esac + shift +done +EOF + chmod +x $prefix/rhts_submit_log + PATH_orig="$PATH" + export PATH="$( pwd )/$prefix:$PATH" + # Run the rlBundleLogs function + rlBundleLogs $prefix $prefix/first/greet $prefix/first_greet &> /dev/null + assertTrue 'rlBundleLogs returns 0' "[ $? -eq 0 ]" + export PATH="$PATH_orig" + # Check if it did everithing it should + assertTrue 'rlBundleLogs creates *.tar.gz file' \ + "ls CP-tmp-$prefix*.tar.gz" + mkdir $prefix-extracted + tar xzf CP-tmp-$prefix*.tar.gz -C $prefix-extracted + assertTrue 'rlBundleLogs included first/greet file' \ + "grep -qr 'hello' $prefix-extracted/*" + assertTrue 'rlBundleLogs included first_greet file' \ + "grep -qr 'world' $prefix-extracted/*" + # Cleanup + rm -rf $prefix* CP-tmp-$prefix*.tar.gz +} + +test_LOG_LEVEL(){ + unset LOG_LEVEL + unset DEBUG + + assertTrue "rlLogInfo msg in journal dump with default LOG_LEVEL" \ + "rlLogInfo 'lllll' ; rlJournalPrintText |grep 'lllll'" + + assertTrue "rlLogWarning msg in journal dump with default LOG_LEVEL" \ + "rlLogWarning 'wwwwww' ; rlJournalPrintText |grep 'wwwww'" + + DEBUG=1 + assertTrue "rlLogInfo msg in journal dump with default LOG_LEVEL but DEBUG turned on" \ + "rlLogInfo 'lllll' &>/dev/null ; rlJournalPrintText | grep -q 'lllll'" + unset DEBUG + + local LOG_LEVEL="INFO" + assertTrue "rlLogInfo msg in journal dump with LOG_LEVEL=INFO" \ + "rlLogInfo 'lllll' ; rlJournalPrintText |grep 'lllll'" + + local LOG_LEVEL="WARNING" + assertFalse "rlLogInfo msg not in journal dump with LOG_LEVEL higher than INFO" \ + "rlLogInfo 'lllll' ; rlJournalPrintText |grep 'lllll'" + + unset LOG_LEVEL + unset DEBUG +} + +test_rlFileSubmit() { + local main_dir=$(pwd) + local prefix=rlFileSubmit-unittest + local upload_to=$prefix/uploaded + local hlp_files=$prefix/hlp_files + mkdir -p $upload_to + mkdir -p $hlp_files + sync + # Prepare fake rhts_submit_log utility + cat <$prefix/rhts_submit_log +#!/bin/sh +while [ \$# -gt 0 ]; do + case "\$1" in + -S|-T) shift; ;; + -l) shift; cp "\$1" "$main_dir/$upload_to/";; + esac + shift +done +EOF + export BEAKERLIB_COMMAND_SUBMIT_LOG=rhts_submit_log + chmod +x $prefix/rhts_submit_log + ln -s rhts_submit_log $prefix/rhts-submit-log + PATH_orig="$PATH" + export PATH="$( pwd )/$prefix:$PATH" + + # TEST 1: No relative or absolute path specified + local orig_file="$hlp_files/rlFileSubmit_test1.file" + local alias="rlFileSubmit_test1.file" + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test1" > $orig_file + + cd $hlp_files + rlFileSubmit rlFileSubmit_test1.file &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs file without relative or absolute path specified' "[ $? -eq 0 ]" + + # TEST 2: Relative path ./ + local orig_file="$hlp_files/rlFileSubmit_test2.file" + local alias=$(echo "$main_dir/$orig_file" | tr '/' "-" | sed "s/^-*//") + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test2" > $orig_file + + cd $hlp_files + rlFileSubmit ./rlFileSubmit_test2.file &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs relative path ./' "[ $? -eq 0 ]" + + # TEST 3: Relative path ../ + mkdir -p $hlp_files/directory/ + local orig_file="$hlp_files/rlFileSubmit_test3.file" + local alias=$(echo "$main_dir/$orig_file" | tr '/' "-" | sed "s/^-*//") + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test3" > $orig_file + + cd $hlp_files/directory + rlFileSubmit ../rlFileSubmit_test3.file &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs relative path ../' "[ $? -eq 0 ]" + + # TEST 4: Absolute path + local orig_file="$hlp_files/rlFileSubmit_test4.file" + local alias=$(echo "$main_dir/$orig_file" | tr '/' "-" | sed "s/^-*//") + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test4" > $orig_file + + rlFileSubmit $main_dir/$orig_file &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs absolute path' "[ $? -eq 0 ]" + + # TEST 5: Custom alias + local orig_file="$hlp_files/rlFileSubmit_test5file" + local alias="alias_rlFileSubmit_test5.file" + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test5" > $orig_file + + rlFileSubmit $orig_file $alias &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs custom alias' "[ $? -eq 0 ]" + + # TEST 6: Custom separator + local orig_file="$hlp_files/rlFileSubmit_test6.file" + local alias=$(echo "$main_dir/$orig_file" | tr '/' "_" | sed "s/^_*//") + local expected_file="$upload_to/$alias" + + echo "rlFileSubmit_test6" > $orig_file + + rlFileSubmit -s '_' $main_dir/$orig_file &> /dev/null + + cd $main_dir + local sum1=$(md5sum $orig_file | cut -d " " -f 1) + local sum2=$(md5sum $expected_file | cut -d " " -f 1) + + [ -e $expected_file -a "$sum1" = "$sum2" ] + assertTrue 'rlBundleLogs absolute path' "[ $? -eq 0 ]" + + rm -f /var/tmp/BEAKERLIB_STORED_rlFileSubmit_test1.file # no-reboot + unset BEAKERLIB_COMMAND_SUBMIT_LOG + cd $hlp_files + + if [[ $UID -eq 0 ]]; then + rlFileSubmit rlFileSubmit_test1.file &> /dev/null + assertTrue "rlFileSubmit default function RC" "[ $? -eq 0 ]" + assertTrue "rlFileSubmit default function file submitted" "[ -e /var/tmp/BEAKERLIB_${TESTID}_STORED_rlFileSubmit_test1.file ]" # no-reboot + else + assertLog "rlFileSubmit default function RC is not meant to be executed under non-priviledged user" SKIP + fi + cd $main_dir + + # Cleanup + export PATH="$PATH_orig" + rm -rf $prefix* +} diff --git a/src/test/otherTest.sh b/src/test/otherTest.sh new file mode 100644 index 0000000..d14173a --- /dev/null +++ b/src/test/otherTest.sh @@ -0,0 +1,30 @@ +# Copyright (c) 2012 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Jakub Prokes + +test_allFunctionsHighlight() { + local hiFunctions="$(sed -n \ + '/syn keyword/{s/syn\s\+keyword\s\+bl[[:alnum:]]\+\s\+//; s/\s\+/\n/gp}' \ + ../vim/syntax/beakerlib.vim)" + + while read fName; do + [[ $fName =~ EOF|^[[:space:]]*$ ]] && continue; + for hiFunction in $hiFunctions; do + [[ $fName == $hiFunction ]] && break; + done; + assertTrue "Function $fName has a vim highligthting defined." "[[ $fName == $hiFunction ]]"; + done < <(bash -c "source ../beakerlib.sh; declare -f | \ + perl -e 'map { s/.*(obsolete|deprecate|^rlj).*//s; s/ .*/\n/s; print } \ + (join \"\", <>) =~ m/^rl.*?^}/msg;'"); +} diff --git a/src/test/rpmsTest.sh b/src/test/rpmsTest.sh new file mode 100644 index 0000000..9629354 --- /dev/null +++ b/src/test/rpmsTest.sh @@ -0,0 +1,190 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Jan Hutar + +test_rlAssertRpm() { + local first=$( rpm -qa --qf "%{NAME}.%{ARCH}\n" | tail -n 1 ) + local first_n=$( rpm -q $first --qf "%{NAME}\n" | tail -n 1 ) + local first_v=$( rpm -q $first --qf "%{VERSION}\n" | tail -n 1 ) + local first_r=$( rpm -q $first --qf "%{RELEASE}\n" | tail -n 1 ) + local first_a=$( rpm -q $first --qf "%{ARCH}\n" | tail -n 1 ) + + assertTrue "rlAssertRpm returns 0 on installed 'N' package" \ + "rlAssertRpm $first_n" + assertTrue "rlAssertRpm returns 0 on installed 'NV' package" \ + "rlAssertRpm $first_n $first_v" + assertTrue "rlAssertRpm returns 0 on installed 'NVR' package" \ + "rlAssertRpm $first_n $first_v $first_r" + assertTrue "rlAssertRpm returns 0 on installed 'NVRA' package" \ + "rlAssertRpm $first_n $first_v $first_r $first_a" + + assertRun "rlAssertRpm" 100 \ + "rlAssertRpm returns 100 when invoked without parameters" + + assertFalse "rlAssertRpm returns non-0 on not-installed 'N' package" \ + "rlAssertRpm $first_n-not-installed-package" + assertFalse "rlAssertRpm returns non-0 on not-installed 'NV' package" \ + "rlAssertRpm $first_n $first_v.1.2.3" + assertFalse "rlAssertRpm returns non-0 on not-installed 'NVR' package" \ + "rlAssertRpm $first_n $first_v $first_r.1.2.3" + assertFalse "rlAssertRpm returns non-0 on not-installed 'NVRA' package" \ + "rlAssertRpm $first_n $first_v $first_r ${first_a}xyz" + + assertGoodBad "rlAssertRpm ahsgqyrg" 0 1 + + : > $OUTPUTFILE + local PACKAGES=$( rpm -qa --qf "%{NAME} " | awk '{ print $1 " " $2 }' ) + local PACKAGES2=$PACKAGES + local COLLECTIONS=$( rpm -qa --qf "%{NAME} " | awk '{ print $3 " " $4 }' ) + local REQUIRES=$( rpm -qa --qf "%{NAME} " | awk '{ print $5 " " $6 }' ) + + assertTrue "Running rlAssertRpm --all with PACKAGES=$PACKAGES COLLECTIONS=$COLLECTIONS REQUIRES=$REQUIRES" \ + "rlAssertRpm --all >$OUTPUTFILE" + + for pkg in $PACKAGES $COLLECTIONS $REQUIRES ; do + assertTrue "Checking log for $pkg" \ + "grep -q '$pkg' $OUTPUTFILE" + done + + unset PACKAGES + assertTrue "Running rlAssertRpm --all with PACKAGES=$PACKAGES COLLECTIONS=$COLLECTIONS REQUIRES=$REQUIRES" \ + "rlAssertRpm --all >$OUTPUTFILE" + + for pkg in $PACKAGES $COLLECTIONS $REQUIRES ; do + assertTrue "Checking log for $pkg" \ + "grep -q '$pkg' $OUTPUTFILE" + done + for pkg in $PACKAGES2 ; do + assertFalse "Checking log for not containing $pkg" \ + "grep -q '$pkg' $OUTPUTFILE" + done +} + +test_rlAssertNotRpm() { + local first=$( rpm -qa --qf "%{NAME}.%{ARCH}\n" | tail -n 1 ) + local first_n=$( rpm -q $first --qf "%{NAME}\n" | tail -n 1 ) + local first_v=$( rpm -q $first --qf "%{VERSION}\n" | tail -n 1 ) + local first_r=$( rpm -q $first --qf "%{RELEASE}\n" | tail -n 1 ) + local first_a=$( rpm -q $first --qf "%{ARCH}\n" | tail -n 1 ) + + assertFalse "rlAssertNotRpm returns non-0 on installed 'N' package" \ + "rlAssertNotRpm $first_n" + assertFalse "rlAssertNotRpm returns non-0 on installed 'NV' package" \ + "rlAssertNotRpm $first_n $first_v" + assertFalse "rlAssertNotRpm returns non-0 on installed 'NVR' package" \ + "rlAssertNotRpm $first_n $first_v $first_r" + assertFalse "rlAssertNotRpm returns non-0 on installed 'NVRA' package" \ + "rlAssertNotRpm $first_n $first_v $first_r $first_a" + + assertRun "rlAssertNotRpm" 100 \ + "rlAssertNotRpm returns 100 when run without parameters" + + assertTrue "rlAssertNotRpm returns 0 on not-installed 'N' package" \ + "rlAssertNotRpm $first_n-not-installed-package" + assertTrue "rlAssertNotRpm returns 0 on not-installed 'NV' package" \ + "rlAssertNotRpm $first_n $first_v.1.2.3" + assertTrue "rlAssertNotRpm returns 0 on not-installed 'NVR' package" \ + "rlAssertNotRpm $first_n $first_v $first_r.1.2.3" + assertTrue "rlAssertNotRpm returns 0 on not-installed 'NVRA' package" \ + "rlAssertNotRpm $first_n $first_v $first_r ${first_a}xyz" + + assertGoodBad "rlAssertNotRpm $first_n" 0 1 +} + +test_rlCheckRpm() { + local first=$( rpm -qa --qf "%{NAME}.%{ARCH}\n" | tail -n 1 ) + local first_n=$( rpm -q $first --qf "%{NAME}\n" | tail -n 1 ) + local first_v=$( rpm -q $first --qf "%{VERSION}\n" | tail -n 1 ) + local first_r=$( rpm -q $first --qf "%{RELEASE}\n" | tail -n 1 ) + local first_a=$( rpm -q $first --qf "%{ARCH}\n" | tail -n 1 ) + + : > $OUTPUTFILE + assertTrue "rlCheckRpm returns 0 on installed 'N' package" \ + "rlCheckRpm $first_n" + assertTrue "rlCheckRpm returns 0 on installed 'NV' package" \ + "rlCheckRpm $first_n $first_v" + assertTrue "rlCheckRpm returns 0 on installed 'NVR' package" \ + "rlCheckRpm $first_n $first_v $first_r" + assertTrue "rlCheckRpm returns 0 on installed 'NVRA' package" \ + "rlCheckRpm $first_n $first_v $first_r $first_a" + assertTrue "Checking log for $first_n" \ + "grep -q '$first_n' $OUTPUTFILE" + + assertRun "rlCheckRpm" 100 "rlCheckRpm returns non-0 when run without parameters" + + : > $OUTPUTFILE + assertFalse "rlCheckRpm returns non-0 on not-installed 'N' package" \ + "rlCheckRpm $first_n-not-installed-package" + assertFalse "rlCheckRpm returns non-0 on not-installed 'NV' package" \ + "rlCheckRpm $first_n $first_v.1.2.3" + assertFalse "rlCheckRpm returns non-0 on not-installed 'NVR' package" \ + "rlCheckRpm $first_n $first_v $first_r.1.2.3" + assertFalse "rlCheckRpm returns non-0 on not-installed 'NVRA' package" \ + "rlCheckRpm $first_n $first_v $first_r ${first_a}xyz" + assertTrue "Checking log for $first_n" "grep -q '$first_n' $OUTPUTFILE" + + assertGoodBad "rlCheckRpm ahsgqyrg" 0 0 +} + +test_rlRpmPresent(){ + assertTrue "rlrpmPresent is reported to be obsoleted" "rlRpmPresent abcdefg 2>&1 >&- |grep -q obsolete" +} + +test_rlAssertBinaryOrigin(){ + rlPhaseStartTest &>/dev/null + #existing binary command + assertTrue "rlAssertBinaryOrigin returns 0 on existing command owned by the package (param)" \ + "rlAssertBinaryOrigin bash bash" + + #existing binary command + assertTrue "rlAssertBinaryOrigin returns 0 on existing command owned by the package (env)" \ + "PACKAGES='bash' rlAssertBinaryOrigin bash" + + #existing binary command in more packages + assertTrue "rlAssertBinaryOrigin returns 0 on existing command owned by one of the packages" \ + "rlAssertBinaryOrigin bash bash ksh pdksh" + + #existing binary full path + assertTrue "rlAssertBinaryOrigin returns 0 on existing full path command owned by the package" \ + "rlAssertBinaryOrigin /bin/bash bash" + + #exisiting alternative + local PKG=$(rpm -qf --qf="%{name}\n" $( ls -l $( ls -l /usr/bin | grep alternatives | head -n1 | awk '{ print $NF }' ) | awk '{ print $NF }' )) + local BIN1=$( ls -l /usr/bin | grep alternatives | head -n1 | awk '{ print $8 }' ) + local BIN2=$( ls -l /usr/bin | grep alternatives | head -n1 | awk '{ print $9 }' ) + if [ -e "/usr/bin/$BIN1" ] + then + BIN=$BIN1 + elif [ -e "/usr/bin/$BIN2" ] + then + BIN=$BIN2 + fi + + assertTrue "rlAssertBinaryOrigin returns 0 on existing alternative command owned by the packages" \ + "rlAssertBinaryOrigin $BIN $PKG" + + #binary not in package + assertRun "rlAssertBinaryOrigin bash glibc" 1 \ + "rlAssertBinaryOrigin returns 1 on existing full path command owned by different package" + #non-existing package + assertRun "rlAssertBinaryOrigin bash rpm-not-found" 1 \ + "rlAssertBinaryOrigin returns 1 on non-existing package" + #non-existing binary + assertRun "rlAssertBinaryOrigin command-not-found bash" 2 \ + "rlAssertBinaryOrigin returns 2 on non-existing command" + #no params + assertRun "rlAssertBinaryOrigin" 100 \ + "rlAssertBinaryOrigin returns 100 when invoked without parameters" + rlPhaseEnd &> /dev/null +} diff --git a/src/test/storageTest.sh b/src/test/storageTest.sh new file mode 100644 index 0000000..b7b58f6 --- /dev/null +++ b/src/test/storageTest.sh @@ -0,0 +1,100 @@ +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Petr Muller + +test_storageBasics(){ + local VALUEKEY="$( __INTERNAL_ST_GET key )" + assertTrue "GET of non-PUT value is empty string" "[ '$VALUEKEY' == '' ]" + + __INTERNAL_ST_PUT key value + VALUEKEY="$( __INTERNAL_ST_GET key )" + assertTrue "GET of PUT value is correct" "[ '$VALUEKEY' == 'value' ]" + + __INTERNAL_ST_PUT key newvalue + VALUEKEY="$( __INTERNAL_ST_GET key )" + assertTrue "GET of PUT value can be overwritten" "[ '$VALUEKEY' == 'newvalue' ]" + + __INTERNAL_ST_PUT newkey value + VALUEKEY="$( __INTERNAL_ST_GET key )" + local VALUENEWKEY="$( __INTERNAL_ST_GET newkey )" + assertTrue "PUTs of different keys do not interfere" "[ '$VALUEKEY' == 'newvalue' ]" + assertTrue "PUTs of different keys do not interfere" "[ '$VALUENEWKEY' == 'value' ]" + + __INTERNAL_ST_PRUNE newkey + VALUEKEY="$( __INTERNAL_ST_GET key )" + local VALUENEWKEY="$( __INTERNAL_ST_GET newkey )" + assertTrue "PRUNE does not delete unrelated records" "[ '$VALUEKEY' == 'newvalue' ]" + assertTrue "PRUNE deletes an appropriate record" "[ '$VALUENEWKEY' == '' ]" +} + +test_storageSections(){ + local KEY="key" + local SEC1="section1" + local SEC2="section2" + + local V1="value1" + local V2="value2" + + __INTERNAL_ST_PUT $KEY foo + __INTERNAL_ST_PUT $KEY $V1 --section="$SEC1" + __INTERNAL_ST_PUT $KEY $V2 --section="$SEC2" + + assertTrue "Same key, different section: SEC1" "[ '$( __INTERNAL_ST_GET $KEY --section=$SEC1)' == '$V1' ]" + assertTrue "Same key, different section: SEC2" "[ '$( __INTERNAL_ST_GET $KEY --section=$SEC2)' == '$V2' ]" + assertTrue "PUT with --section do not interfere with generic section" "[ '$( __INTERNAL_ST_GET $KEY )' == 'foo' ]" + + __INTERNAL_ST_PRUNE $KEY --section=$SEC1 + assertTrue "PRUNE with --section clears appropriate record" "[ '$( __INTERNAL_ST_GET $KEY --section=$SEC1)' == '' ]" + assertTrue "PRUNE with --section does not interfere with other sections" "[ '$( __INTERNAL_ST_GET $KEY --section=$SEC2)' == '$V2' ]" + assertTrue "PRUNE with --section do not interfere with generic section" "[ '$( __INTERNAL_ST_GET $KEY )' == 'foo' ]" +} + +test_storageNamespaces(){ + local KEY="key" + local SECTION="section" + + local NS1="namespace1" + local NS2="namespace2" + + local V1="value1" + local V2="value2" + local V3="value3" + local V4="value4" + + __INTERNAL_ST_PUT $KEY foo + __INTERNAL_ST_PUT $KEY $V1 --namespace=$NS1 + __INTERNAL_ST_PUT $KEY $V2 --namespace=$NS2 + __INTERNAL_ST_PUT $KEY $V3 --namespace=$NS1 --section=$SECTION + __INTERNAL_ST_PUT $KEY $V4 --namespace=$NS2 --section=$SECTION + + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [GENERIC]" "[ '$(__INTERNAL_ST_GET $KEY)' == 'foo' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1)' == '$V1' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2)' == '$V2' ]" + assertTrue "Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1 --section=$SECTION)' == '$V3' ]" + assertTrue "Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2 --section=$SECTION)' == '$V4' ]" + + __INTERNAL_ST_PRUNE $KEY --namespace=$NS1 + assertTrue "!PRUNED! Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1)' == '' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [GENERIC]" "[ '$(__INTERNAL_ST_GET $KEY)' == 'foo' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2)' == '$V2' ]" + assertTrue "Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1 --section=$SECTION)' == '$V3' ]" + assertTrue "Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2 --section=$SECTION)' == '$V4' ]" + + __INTERNAL_ST_PRUNE $KEY --namespace=$NS2 --section=$SECTION + assertTrue "!PRUNED! Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1)' == '' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [GENERIC]" "[ '$(__INTERNAL_ST_GET $KEY)' == 'foo' ]" + assertTrue "Key: [$KEY] | Section: [GENERIC] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2)' == '$V2' ]" + assertTrue "Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS1]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS1 --section=$SECTION)' == '$V3' ]" + assertTrue "!PRUNED! Key: [$KEY] | Section: [$SECTION] | Namespace: [$NS2]" "[ '$(__INTERNAL_ST_GET $KEY --namespace=$NS2 --section=$SECTION)' == '' ]" +} diff --git a/src/test/synchronisationTest.sh b/src/test/synchronisationTest.sh new file mode 100644 index 0000000..6681741 --- /dev/null +++ b/src/test/synchronisationTest.sh @@ -0,0 +1,321 @@ +#!/bin/bash +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Hubert Kario + +test_rlWaitForSocketPositive() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 5; nc -l 12345 > $test_dir/out) & + local bg_pid=$! + + silentIfNotDebug "rlWaitForSocket 12345" + local ret=$? + assertTrue "Check if rlWaitForSocket return 0 when socket is opened" "[[ $ret -eq 0 ]]" + + silentIfNotDebug "echo 'hello world' | nc localhost 12345" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + + assertTrue "Check if data was transferred" "grep 'hello world' $test_dir/out" + + rm -rf $test_dir +} + +test_rlWaitForSocketClose() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (nc -l 12345 > $test_dir/out) & + local bg_pid=$! + + (sleep 5; kill $bg_pid)& + + silentIfNotDebug "rlWaitForSocket 12345" + local ret=$? + assertTrue "Check if rlWaitForSocket return 0 when socket is opened" "[[ $ret -eq 0 ]]" + + silentIfNotDebug "echo 'hello world' | nc localhost 12345" + + silentIfNotDebug "rlWaitForSocket 12345 --close" + local ret=$? + assertTrue "Check if rlWaitForSocket return 0 when socket is closed" "[[ $ret -eq 0 ]]" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + + assertTrue "Check if data was transferred" "grep 'hello world' $test_dir/out" + + rm -rf $test_dir +} + +test_rlWaitForSocketTimeoutReached() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 10; nc -l 12345 > $test_dir/out) & + local bg_pid=$! + + silentIfNotDebug "rlWaitForSocket -t 2 12345" + local ret=$? + assertTrue "Check if rlWaitForSocket returns 1 on reaching timeout" "[[ $ret -eq 1 ]]" + + silentIfNotDebug "echo 'hello world' | nc localhost 12345" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + + assertFalse "Check if data was not transferred" "grep 'hello world' $test_dir/out || false" + + rm -rf $test_dir +} + +test_rlWaitForSocketPIDKilled() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 10) & + local bg_pid=$! + (sleep 15; touch $test_dir/mark) & + local bg2_pid=$! + + silentIfNotDebug "rlWaitForSocket -p $bg_pid 12345" + local ret=$? + assertTrue "Check if rlWaitForSocket returns 1 on PID exit" "[[ $ret -eq 1 ]]" + + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if rlWaitForSocket returned quickly after PID died" "[[ ! -e $test_dir/mark ]]" + + silentIfNotDebug "echo 'hello world' | nc localhost 12345" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + + assertFalse "Check if data was not transferred" "grep 'hello world' $test_dir/out || false" + + rm -rf $test_dir +} + +test_rlWaitForFilePositive() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 5; touch ${test_dir}/file)& + local bg_pid=$! + (sleep 10; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + silentIfNotDebug "rlWaitForFile $test_dir/file" + local ret=$? + assertTrue "Check if rlWaitForFile returned 0" "[[ $ret -eq 0 ]]" + + assertTrue "Check if file exists" "[[ -e $test_dir/file ]]" + + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if rlWaitForFile returned quickly after file was created" "[[ ! -e $test_dir/mark ]]" + + rm -rf $test_dir +} + +test_rlWaitForFileNegative() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 5; touch ${test_dir}/file)& + local bg_pid=$! + (sleep 10; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + silentIfNotDebug "rlWaitForFile -t 2 $test_dir/file" + local ret=$? + assertTrue "Check if rlWaitForFile returned 1" "[[ $ret -eq 1 ]]" + + assertTrue "Check if file does not exists" "[[ ! -e $test_dir/file ]]" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if rlWaitForFile returned quickly after file was created" "[[ ! -e $test_dir/mark ]]" + + rm -rf $test_dir +} + +test_rlWaitForFilePIDKilled() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 2)& + local bg_pid=$! + (sleep 5; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + silentIfNotDebug "rlWaitForFile -p $bg_pid $test_dir/file" + local ret=$? + assertTrue "Check if rlWaitForFile returned 1" "[[ $ret -eq 1 ]]" + + assertTrue "Check if file does not exists" "[[ ! -e $test_dir/file ]]" + + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if rlWaitForFile returned quickly after file was created" "[[ ! -e $test_dir/mark ]]" + + rm -rf $test_dir +} + +test_rlWaitForCmdPositive() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + touch ${test_dir}/file + (sleep 5; echo mark > ${test_dir}/file)& + local bg_pid=$! + (sleep 10; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file exists" "[[ -e $test_dir/file ]]" + assertFalse "Check if doesn't contain 'mark' string" "grep mark $test_dir/file" + + silentIfNotDebug "rlWaitForCmd 'grep mark $test_dir/file'" + local ret=$? + assertTrue "Check if rlWaitForCmd returned 0" "[[ $ret -eq 0 ]]" + + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if file contains 'mark' string" "grep mark $test_dir/file" + assertTrue "Check if rlWaitForCmd returned quickly" "[[ ! -e $test_dir/mark ]]" + + rm -rf $test_dir +} + +test_rlWaitForCmdMaxInvoc() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 10; echo mark > ${test_dir}/file)& + local bg_pid=$! + (sleep 15; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + silentIfNotDebug "rlWaitForCmd 'echo line >> $test_dir/counter; grep mark $test_dir/file 2>/dev/null' -m 4" + local ret=$? + assertTrue "Check if rlWaitForCmd returned 1" "[[ $ret -eq 1 ]]" + + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + kill -s SIGKILL $bg_pid 2>/dev/null 1>&2 + wait $bg_pid 2>/dev/null 1>&2 + + assertTrue "Check if WaitForCmd returned quickly" "[[ ! -e $test_dir/mark ]]" + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + local lines=$(wc -l < $test_dir/counter) + assertTrue "Check if the command was executed 4 times" "[[ $lines -eq 4 ]]" + + rm -rf $test_dir +} + +test_rlWaitForCmdDelay() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 4; echo mark > ${test_dir}/file)& + local bg_pid=$! + (sleep 15; touch ${test_dir}/mark)& + local bg2_pid=$! + + assertTrue "Check if file does not exist" "[[ ! -e $test_dir/file ]]" + + silentIfNotDebug "rlWaitForCmd 'echo line >> $test_dir/counter; grep mark $test_dir/file 2>/dev/null' -d 6" + local ret=$? + assertTrue "Check if rlWaitForCmd returned 0" "[[ $ret -eq 0 ]]" + + kill -s SIGKILL $bg2_pid 2>/dev/null 1>&2 + wait $bg2_pid 2>/dev/null 1>&2 + + assertTrue "Check if WaitForCmd returned quickly" "[[ ! -e $test_dir/mark ]]" + assertTrue "Check if file does exist" "[[ -e $test_dir/file ]]" + + local lines=$(wc -l < $test_dir/counter) + assertTrue "Check if the command was executed 2 times" "[[ $lines -eq 2 ]]" + # two executions because the command is executed first, then sleep + + rm -rf $test_dir +} + +test_rlWaitPositive() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 4; touch ${test_dir}/file; exit 4)& + + rlWait $! + ret=$? + + assertTrue "Check if background task executed correctly" "[[ -e ${test_dir}/file ]]" + assertTrue "Check if returned value comes from background task" "[[ $ret -eq 4 ]]" + + rm -rf $test_dir +} + +test_rlWaitNoPIDs() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 4; touch ${test_dir}/file; exit 4)& + + rlWait + ret=$? + + assertTrue "Check if background task executed correctly" "[[ -e ${test_dir}/file ]]" + # when you `wait' for all tasks (no id specified), then wait always returns 0 + assertTrue "Check if returned value is correct" "[[ $ret -eq 0 ]]" + + rm -rf $test_dir +} + +test_rlWaitNegative() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 20; touch ${test_dir}/file; exit 4)& + + rlWait $! -t 1 + ret=$? + + assertTrue "Check if background task didn't execute" "[[ ! -e ${test_dir}/file ]]" + assertTrue "Check if returned value indicates the task was killed" "[[ $ret -eq $((128+15)) ]]" + + rm -rf $test_dir +} + +test_rlWaitKill() { + local test_dir=$(mktemp -d /tmp/beakerlib-test-XXXXXX) + + (sleep 20; touch ${test_dir}/file; exit 4)& + + rlWait $! -t 1 -s SIGKILL + ret=$? + + assertTrue "Check if background task didn't execute" "[[ ! -e ${test_dir}/file ]]" + assertTrue "Check if returned value indicates the task was killed by custom signal" "[[ $ret -eq $((128+9)) ]]" + + rm -rf $test_dir +} diff --git a/src/test/test.sh b/src/test/test.sh new file mode 100755 index 0000000..56887af --- /dev/null +++ b/src/test/test.sh @@ -0,0 +1,376 @@ +#!/bin/bash +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Unit testing library for BeakerLib +# Author: Ales Zelinka +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# This is a simple unit testing library for BeakerLib. +# Have a look at the README file to learn more about it. + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Global variables +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TotalFailed="0" +TotalPassed="0" +FileList="" +TestList="" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertLog comment [result] --- log a comment (with optional result) +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertLog() { + local comment="$1" + local result="${2:-INFO}" + + # colorify known results if run on terminal + if [ -t 1 ]; then + case $result in + INFO) result="\033[0;34mINFO\033[0m";; + PASS) result="\033[0;32mPASS\033[0m";; + FAIL) result="\033[0;31mFAIL\033[0m";; + WARN) result="\033[0;33mWARN\033[0m";; + SKIP) result="\033[0;37mSKIP\033[0m" + ((__INTERNAL_ASSERT_SKIPPED++)) + ((TotalSkipped++)) + ;; + esac + fi + + # echo! + echo -e " [ $result ] $comment" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertRun command [status] [comment] --- run command, check status, log +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertRun() { + local command="$1" + local expected="${2:-0}" + local comment="${3:-Running $command}" + + # no output unless in debug mode + if [ "$DEBUG" == "1" ]; then + eval "$command" + else + eval "$command" &> /dev/null + fi + local status=$? + + # check status + if [[ "$status" =~ ^$expected$ ]]; then + assertLog "$comment" 'PASS' + ((__INTERNAL_ASSERT_PASSED++)) + ((TotalPassed++)) + else + assertLog "$comment" 'FAIL' + ((__INTERNAL_ASSERT_FAILED++)) + ((TotalFailed++)) + [ "$DEBUG" == "1" ] && assertLog "Expected $expected, got $status" + fi +} + +silentIfNotDebug() { + local command="$1" + if [ "$DEBUG" == "1" ] + then + eval "$command" + else + eval "$command" &> /dev/null + fi +} + +journalReset() { + rm -f $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL $__INTERNAL___INTERNAL_BEAKERLIB_METAFILE $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_TXT $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_COLORED $__INTERNAL_PRESISTENT_DATA + [ -e "$BEAKERLIB_DIR" ] && ( chmod -R 777 $BEAKERLIB_DIR ; rm -rf $BEAKERLIB_DIR; ) + unset __INTERNAL_RPM_ASSERTED_PACKAGES + silentIfNotDebug 'rlJournalStart' +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertStart name --- start an assert phase +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertStart() { + local phase="$1" + echo + assertLog "Testing $phase" + __INTERNAL_ASSERT_PHASE="$phase" + __INTERNAL_ASSERT_PASSED="0" + __INTERNAL_ASSERT_FAILED="0" + __INTERNAL_ASSERT_SKIPPED="0" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertEnd --- short phase summary (returns number of failed asserts) +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertEnd() { + local failed="$__INTERNAL_ASSERT_FAILED" + local passed="$__INTERNAL_ASSERT_PASSED" + local skipped="$__INTERNAL_ASSERT_SKIPPED" + local name="$__INTERNAL_ASSERT_PHASE" + + if [ "$failed" -gt "0" ]; then + assertLog "Testing $name finished: $passed passed, $failed failed, $skipped skipped" "FAIL" + elif [ "$passed" -gt "0" ]; then + assertLog "Testing $name finished: $passed passed, $failed failed, $skipped skipped" "PASS" + else + assertLog "Testing $name finished: No assserts run" "WARN" + fi + + printf "%i:%i:%i\n" $__INTERNAL_ASSERT_PASSED $__INTERNAL_ASSERT_FAILED $__INTERNAL_ASSERT_SKIPPED>> $SCOREFILE +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertTrue comment command --- check that command succeeded +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertTrue() { + local comment="$1" + local command="$2" + + assertRun "$command" 0 "$comment" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertFalse comment command --- check that command failed +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertFalse() { + local comment="$1" + local command="$2" + local expects="${3:-1}" + + assertRun "$command" "$expects" "$comment" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertGoodBad command good bad --- check for good/bad asserts in journal +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertGoodBad() { + local command="$1" + local good="$2" + local bad="$3" + + if [[ -n "$good" ]]; then + rm -f $__INTERNAL_BEAKERLIB_JOURNAL $__INTERNAL_BEAKERLIB_METAFILE $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_TXT $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_COLORED $__INTERNAL_PRESISTENT_DATA; rlJournalStart + assertTrue "$good good logged for '$command'" \ + "rlPhaseStart FAIL; $command; rlPhaseEnd; + rlJournalPrintText | egrep 'Assertions: *$good *good, *[0-9]+ *bad'" + fi + + if [[ -n "$bad" ]]; then + rm -f $__INTERNAL_BEAKERLIB_JOURNAL $__INTERNAL_BEAKERLIB_METAFILE $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_TXT $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_COLORED $__INTERNAL_PRESISTENT_DATA; rlJournalStart + assertTrue "$bad bad logged for '$command'" \ + "rlPhaseStart FAIL; $command; rlPhaseEnd; + rlJournalPrintText | egrep 'Assertions: *[0-9]+ *good, *$bad *bad'" + fi + rm -f $__INTERNAL_BEAKERLIB_JOURNAL $__INTERNAL_BEAKERLIB_METAFILE $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_TXT $__INTERNAL___INTERNAL_BEAKERLIB_JOURNAL_COLORED $__INTERNAL_PRESISTENT_DATA +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# assertParameters assert --- check missing parameters +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +assertParameters() { + journalReset + assertTrue "running '$1' (all parameters) must succeed" \ + "rlPhaseStart FAIL; $1 ; rlPhaseEnd ; rlJournalPrintText |grep '1 *good'" + local CMD="" + for i in $1 ; do + CMD="${CMD}${i} " + if [ "x$CMD" == "x$1 " ] ; then break ; fi + #echo "--$1-- --$CMD--" + journalReset + assertFalse "running just '$CMD' (missing parameters) must not succeed" \ + "rlPhaseStart FAIL; $CMD ; rlPhaseEnd ; rlJournalPrintText |grep '1 *good'" + done +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Fake rhts-report-result +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +rhts-report-result(){ + echo -e "ANCHOR NAME: $1\nRESULT: $2\nLOGFILE: $3\nSCORE: $4" +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Self test --- run a simple self test if called as 'test.sh test' +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if [ "$1" == "test" ]; then + assertStart "logging" + assertLog "Some comment with a pass" "PASS" + assertLog "Some comment with a fail" "FAIL" + assertEnd + + assertStart "passing asserts" + assertRun "true" + assertRun "true" 0 + assertRun "true" 0 "Checking true with assertRun" + assertRun "false" 1 + assertRun "false" 1 "Checking false with assertRun" + assertTrue "Checking true with assertTrue" "true" + assertFalse "Checking false with assertFalse" "false" + assertEnd + + assertStart "failing asserts" + assertRun "false" + assertRun "false" 0 + assertRun "false" 0 "Checking false with assertRun" + assertRun "true" 1 + assertRun "true" 1 "Checking true with assertRun" + assertTrue "Checking false with assertTrue" "false" + assertFalse "Checking true with assertFalse" "true" + assertEnd + + [ $TotalPassed == 7 -a $TotalFailed == 7 ] && exit 0 || exit 1 +fi + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Run the tests +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# set important variables & start journal +export BEAKERLIB="$PWD/.." +export TESTID='123456' +export TEST='beakerlib-unit-tests' +. ../beakerlib.sh +export __INTERNAL_JOURNALIST="$BEAKERLIB/python/journalling.py" +export OUTPUTFILE=$(mktemp) # no-reboot +export SCOREFILE=$(mktemp) # no-reboot +rlJournalStart + +# check parameters for test list +for arg in "$@"; do + # selected test function + if [[ "$arg" =~ 'test_' ]]; then + TestList="$TestList $arg" + # test file + elif [[ "$arg" =~ 'Test.sh' ]]; then + FileList="$FileList $arg" + else + echo "What do you mean by $arg?" + exit 1 + fi +done + +# unless test files specified run all available +[[ -z "$FileList" ]] && FileList="$(ls *Test.sh)" + +# load all test functions +for file in $FileList; do + . $file || { echo "Could not load $file"; exit 1; } +done + +assessFile(){ + local file="$1" + assertStart ${file%Test.sh} + for test in $(grep --text -o '^test_[^ (]*' $file); do + assertLog "Running $test" + silentIfNotDebug "journalReset" + $test + done + assertEnd +} + +export TIMEFORMAT="System: %S seconds; User: %U seconds" +TIMEFILE=$( mktemp -u ) # no-reboot +# run all tests +if [[ -z "$TestList" ]]; then + for file in $FileList; do + (time ( { assessFile $file; } 2>&3 ) ) 3>&2 2>>$TIMEFILE.$( basename $file ) + OLDTIMEFILE=".$( basename $file)-perf.old" + if [ -e $OLDTIMEFILE ] + then + OLDPERF="$( cat $OLDTIMEFILE )" + fi + assertLog "Measurement: $( cat $TIMEFILE.$( basename $file ) )" + if [ -n "$OLDPERF" ] + then + assertLog " Was: $OLDPERF" + fi + done +# run selected tests only +else + for test in $TestList; do + assertStart "$test" + silentIfNotDebug "journalReset" + $test + assertEnd + done +fi + +# clean up +rm -rf $BEAKERLIB_DIR + +# print summary +echo +for file in $( ls ${TIMEFILE}* 2>/dev/null ) +do + OLDTIMEFILE=".${file#$TIMEFILE.}-perf.old" + assertLog "${file#$TIMEFILE.} performance: $( cat $file )" + if [ -e $OLDTIMEFILE ] + then + assertLog "${file#$TIMEFILE.} Was: $( cat $OLDTIMEFILE )" + fi + cat $file > $OLDTIMEFILE +done + +while read line +do + PASS=$( echo $line | cut -d ':' -f 1) + FAIL=$( echo $line | cut -d ':' -f 2 ) + SKIP=$( echo $line | cut -d ':' -f 3 ) + TotalPassed=$(( $TotalPassed+$PASS )) + TotalFailed=$(( $TotalFailed+$FAIL )) + TotalSkipped=$(( $TotalSkipped+$SKIP )) +done < $SCOREFILE + +rm -rf $TIMEFILE* $SCOREFILE + +if [ $TotalPassed -gt 0 -a $TotalFailed == 0 ]; then + assertLog "Total summary: $TotalPassed passed, $TotalFailed failed, $TotalSkipped skipped\n" "PASS" + exit 0 +else + assertLog "Total summary: $TotalPassed passed, $TotalFailed failed, $TotalSkipped skipped\n" "FAIL" + exit 1 +fi diff --git a/src/test/testingTest.sh b/src/test/testingTest.sh new file mode 100644 index 0000000..5830db1 --- /dev/null +++ b/src/test/testingTest.sh @@ -0,0 +1,691 @@ +# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material +# is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. +# +# Author: Ales Zelinka + +test_rlAssertDiffer() { + local FILE1="$(mktemp)" # no-reboot + local FILE2="$(mktemp)" # no-reboot + local FILE3="$(mktemp '/tmp/test rlAssertDiffer3-XXXXXX')" # no-reboot + + echo "AAA" > "$FILE1" + echo "AAA" > "$FILE2" + echo "AAA" > "$FILE3" + + assertFalse "rlAssertDiffer does not return 0 for the identical files"\ + "rlAssertDiffer $FILE1 $FILE2" + assertGoodBad "rlAssertDiffer $FILE1 $FILE2" 0 1 + + assertFalse "rlAssertDiffer does not return 0 for the identical files with spaces in name"\ + "rlAssertDiffer \"$FILE1\" \"$FILE3\"" + assertGoodBad "rlAssertDiffer \"$FILE1\" \"$FILE3\"" 0 1 + + assertFalse "rlAssertDiffer does not return 0 for the same file"\ + "rlAssertDiffer $FILE1 $FILE1" + assertGoodBad "rlAssertDiffer $FILE1 $FILE1" 0 1 + + assertRun "rlAssertDiffer" 2 "rlAssertDiffer returns 2 when called without parameters" + + assertRun "rlAssertDiffer $FILE1" 2 "rlAssertDiffer returns 2 when called with only one parameter" + + echo "BBB" > "$FILE3" + echo "BBB" > "$FILE2" + + assertTrue "rlAssertDiffer returns 0 for different files"\ + "rlAssertDiffer $FILE1 $FILE2" + assertGoodBad "rlAssertDiffer $FILE1 $FILE2" 1 0 + + assertTrue "rlAssertDiffer returns 0 for different files with space in name"\ + "rlAssertDiffer \"$FILE1\" \"$FILE3\"" + assertGoodBad "rlAssertDiffer \"$FILE1\" \"$FILE3\"" 1 0 + rm -f "$FILE1" "$FILE2" "$FILE3" +} + +test_rlAssertNotDiffer() { + local FILE1="$(mktemp)" # no-reboot + local FILE2="$(mktemp)" # no-reboot + local FILE3="$(mktemp '/tmp/test rlAssertNotDiffer3-XXXXXX')" # no-reboot + + echo "AAA" > "$FILE1" + echo "AAA" > "$FILE2" + echo "AAA" > "$FILE3" + + assertTrue "rlAssertNotDiffer returns 0 for the identical files"\ + "rlAssertNotDiffer $FILE1 $FILE2" + assertGoodBad "rlAssertNotDiffer $FILE1 $FILE2" 1 0 + + assertTrue "rlAssertNotDiffer returns 0 for the identical files with spaces in name"\ + "rlAssertNotDiffer \"$FILE1\" \"$FILE3\"" + assertGoodBad "rlAssertNotDiffer \"$FILE1\" \"$FILE3\"" 1 0 + + assertTrue "rlAssertNotDiffer returns 0 for the same file"\ + "rlAssertNotDiffer $FILE1 $FILE1" + assertGoodBad "rlAssertNotDiffer $FILE1 $FILE1" 1 0 + + assertRun "rlAssertNotDiffer" 2 "rlAssertNotDiffer returns 2 when called without parameters" + + assertRun "rlAssertNotDiffer $FILE1" 2 \ + "rlAssertNotDiffer returns 2 when called with only one parameter" + + echo "BBB" > "$FILE3" + echo "BBB" > "$FILE2" + + assertFalse "rlAssertNotDiffer does not return 0 for different files"\ + "rlAssertNotDiffer $FILE1 $FILE2" + assertGoodBad "rlAssertNotDiffer $FILE1 $FILE2" 0 1 + + assertFalse "rlAssertNotDiffer does not return 0 for different files with space in name"\ + "rlAssertNotDiffer \"$FILE1\" \"$FILE3\"" + assertGoodBad "rlAssertNotDiffer \"$FILE1\" \"$FILE3\"" 0 1 + rm -f "$FILE1" "$FILE2" "$FILE3" +} + + +test_rlAssertExists() { + journalReset + rlPhaseStartTest &> /dev/null + local FILE="/tmp/test_rlAssertExists" # no-reboot + + touch $FILE + assertTrue "rlAssertExists returns 0 on existing file" \ + "rlAssertExists $FILE" + assertGoodBad "rlAssertExists $FILE" 1 0 + + rm -f $FILE + assertFalse "rlAssertExists returns 1 on non-existant file" \ + "rlAssertExists $FILE" + assertGoodBad "rlAssertExists $FILE" 0 1 + assertFalse "rlAssertExists returns 1 when called without arguments" \ + "rlAssertExists" + + local FILE="/tmp/test rlAssertExists filename with spaces" # no-reboot + touch "$FILE" + assertTrue "rlAssertExists returns 0 on existing file with spaces in its name" \ + "rlAssertExists \"$FILE\"" + rm -f "$FILE" + rlPhaseEnd &> /dev/null +} +test_rlAssertNotExists() { + local FILE="/tmp/test_rlAssertNotExists filename with spaces" # no-reboot + local FILE2="/tmp/test_rlAssertNotExists" # no-reboot + touch "$FILE" + assertFalse "rlAssertNotExists returns 1 on existing file" \ + "rlAssertNotExists \"$FILE\"" + assertGoodBad "rlAssertNotExists \"$FILE\"" 0 1 + assertFalse "rlAssertNotExists returns 1 when called without arguments" \ + "rlAssertNotExists" + + rm -f "$FILE" + touch "$FILE2" + assertTrue "rlAssertNotExists returns 0 on non-existing file" \ + "rlAssertNotExists \"$FILE\"" + assertGoodBad "rlAssertNotExists \"$FILE\"" 1 0 + rm -f "$FILE2" + +} + +test_rlAssertGrep() { + echo yes > grepfile + assertTrue "rlAssertGrep should pass when pattern present" \ + 'rlAssertGrep yes grepfile; [ $? == 0 ]' + assertGoodBad 'rlAssertGrep yes grepfile' 1 0 + assertGoodBad 'rlAssertGrep no grepfile' 0 1 + assertParameters 'rlAssertGrep yes grepfile' + assertTrue "rlAssertGrep should return 1 when pattern is not present" \ + 'rlAssertGrep no grepfile; [ $? == 1 ]' + assertTrue "rlAssertGrep should return 2 when file does not exist" \ + 'rlAssertGrep no badfile; [ $? == 2 ]' + assertGoodBad 'rlAssertGrep yes badfile' 0 1 + # without optional parameter + assertTrue "rlAssertGrep without optional arg should not ignore case" \ + 'rlAssertGrep YES grepfile; [ $? == 1 ]' + assertTrue "rlAssertGrep without optional arg should ignore extended regexp" \ + 'rlAssertGrep "e{1,3}" grepfile; [ $? == 1 ]' + assertTrue "rlAssertGrep without optional arg should ignore perl regexp" \ + 'rlAssertGrep "\w+" grepfile; [ $? == 1 ]' + # with optional parameter + assertTrue "rlAssertGrep with -i should ignore case" \ + 'rlAssertGrep YES grepfile -i; [ $? == 0 ]' + assertTrue "rlAssertGrep with -E should use extended regexp" \ + 'rlAssertGrep "e{1,3}" grepfile -E; [ $? == 0 ]' + assertTrue "rlAssertGrep with -P should use perl regexp" \ + 'rlAssertGrep "\w+" grepfile -P; [ $? == 0 ]' + rm -f grepfile +} + +test_rlAssertNotGrep() { + echo yes > grepfile + assertTrue "rlAssertNotGrep should pass when pattern is not present" \ + 'rlAssertNotGrep no grepfile; [ $? == 0 ]' + assertGoodBad 'rlAssertNotGrep no grepfile' 1 0 + assertGoodBad 'rlAssertNotGrep yes grepfile' 0 1 + assertParameters 'rlAssertNotGrep no grepfile' + assertTrue "rlAssertNotGrep should return 1 when pattern present" \ + 'rlAssertNotGrep yes grepfile; [ $? == 1 ]' + assertTrue "rlAssertNotGrep should return 2 when file does not exist" \ + 'rlAssertNotGrep no badfile; [ $? == 2 ]' + assertGoodBad 'rlAssertNotGrep yes badfile' 0 1 + # without optional parameter + assertTrue "rlAssertNotGrep without optional arg should not ignore case" \ + 'rlAssertNotGrep YES grepfile; [ $? == 0 ]' + assertTrue "rlAssertNotGrep without optional arg should ignore extended regexp" \ + 'rlAssertNotGrep "e{1,3}" grepfile; [ $? == 0 ]' + assertTrue "rlAssertNotGrep without optional arg should ignore perl regexp" \ + 'rlAssertNotGrep "\w+" grepfile; [ $? == 0 ]' + # with optional parameter + assertTrue "rlAssertNotGrep with -i should ignore case" \ + 'rlAssertNotGrep YES grepfile -i; [ $? == 1 ]' + assertTrue "rlAssertNotGrep with -E should use extended regexp" \ + 'rlAssertNotGrep "e{1,3}" grepfile -E; [ $? == 1 ]' + assertTrue "rlAssertNotGrep with -P should use perl regexp" \ + 'rlAssertNotGrep "\w+" grepfile -P; [ $? == 1 ]' + rm -f grepfile +} + + +test_rlAssert0() { + assertGoodBad 'rlAssert0 "abc" 0' 1 0 + assertGoodBad 'rlAssert0 "abc" 1' 0 1 + assertParameters 'rlAssert0 "comment" 0' +} + +test_rlAssertEquals(){ + assertGoodBad 'rlAssertEquals "abc" "hola" "hola"' 1 0 + assertGoodBad 'rlAssertEquals "abc" "hola" "Hola"' 0 1 + assertParameters 'rlAssertEquals comment hola hola' +} +test_rlAssertNotEquals(){ + assertGoodBad 'rlAssertNotEquals "abc" "hola" "hola"' 0 1 + assertGoodBad 'rlAssertNotEquals "abc" "hola" "Hola"' 1 0 + assertParameters 'rlAssertNotEquals comment hola Hola' +} + +test_rlAssertGreater(){ + assertGoodBad 'rlAssertGreater "comment" 999 1' 1 0 + assertGoodBad 'rlAssertGreater "comment" 1 -1' 1 0 + assertGoodBad 'rlAssertGreater "comment" 999 999' 0 1 + assertGoodBad 'rlAssertGreater "comment" 10 100' 0 1 + assertParameters 'rlAssertGreater comment -1 -2' +} +test_rlAssertGreaterOrEqual(){ + assertGoodBad 'rlAssertGreaterOrEqual "comment" 999 1' 1 0 + assertGoodBad 'rlAssertGreaterOrEqual "comment" 1 -1' 1 0 + assertGoodBad 'rlAssertGreaterOrEqual "comment" 999 999' 1 0 + assertGoodBad 'rlAssertGreaterOrEqual "comment" 10 100' 0 1 + assertParameters 'rlAssertGreaterOrEqual comment 10 10' +} + +test_rlAssertLesser(){ + assertGoodBad 'rlAssertLesser "comment" 1 999' 1 0 + assertGoodBad 'rlAssertLesser "comment" -1 1' 1 0 + assertGoodBad 'rlAssertLesser "comment" 1000 999' 0 1 + assertGoodBad 'rlAssertLesser "comment" 100 10' 0 1 + assertParameters 'rlAssertLesser comment -2 -1' +} + +test_rlAssertLesserOrEqual(){ + assertGoodBad 'rlAssertLesserOrEqual "comment" 1 999' 1 0 + assertGoodBad 'rlAssertLesserOrEqual "comment" -1 1' 1 0 + assertGoodBad 'rlAssertLesserOrEqual "comment" 1000 999' 0 1 + assertGoodBad 'rlAssertLesserOrEqual "comment" 100 10' 0 1 + assertParameters 'rlAssertLesserOrEqual comment 10 10' +} + +test_rlRun(){ + assertGoodBad 'rlRun /bin/true 0 comment' 1 0 + assertGoodBad 'rlRun /bin/true 3 comment' 0 1 + assertLog "rlRun with empty command should fail" + assertFalse "rlRun with empty command should fail" "rlRun ''" + assertGoodBad "rlRun ''" 0 1 + assertFalse "rlRun with just space in command should fail" "rlRun ' '" + assertGoodBad "rlRun ' '" 0 1 + assertFalse "rlRun with just tab in command should fail" "rlRun ' '" + assertGoodBad "rlRun ' '" 0 1 + assertTrue "rlRun with 1st parameter only assumes status = 0" \ + 'rlRun /bin/true' + #more than one status + assertGoodBad 'rlRun /bin/true 0,1,2 comment' 1 0 + assertGoodBad 'rlRun /bin/true 1,0,2 comment' 1 0 + assertGoodBad 'rlRun /bin/true 1,2,0 comment' 1 0 + assertGoodBad 'rlRun /bin/true 10,100,1000 comment' 0 1 + # more than one status with interval + assertGoodBad 'rlRun /bin/false 0-2 comment' 1 0 + assertGoodBad 'rlRun /bin/false 5,0-2 comment' 1 0 + assertGoodBad 'rlRun /bin/false 0-2,5 comment' 1 0 + assertGoodBad 'rlRun /bin/false 5,0-2,7 comment' 1 0 + assertGoodBad 'rlRun /bin/false 5-10,0-2 comment' 1 0 + assertGoodBad 'rlRun /bin/false 0-2,5-10 comment' 1 0 + + rlRun -t 'echo "foobar1"' 2>&1 | grep "^STDOUT: foobar1" --quiet + assertTrue "rlRun tagging (stdout)" "[ $? -eq 0 ]" + + rlRun -t 'echo "foobar2" 1>&2' 2>&1 | grep "^STDERR: foobar2" --quiet + assertTrue "rlRun tagging (stderr)" "[ $? -eq 0 ]" + + OUTPUTFILE_orig="$OUTPUTFILE" + export OUTPUTFILE="$(mktemp)" # no-reboot + + PREFIX_REGEXP='^:: \[ [0-9]{2}:[0-9]{2}:[0-9]{2} \] :: \[ LOG \] ::[[:space:]]+' + + silentIfNotDebug "rlRun -l 'echo \"foobar3\"'" + grep 'echo "foobar3"' "$OUTPUTFILE" --quiet && egrep "${PREFIX_REGEXP}"'foobar3' "$OUTPUTFILE" --quiet + assertTrue "rlRun logging plain" "[ $? -eq 0 ]" + + rm -f foobar3 + assertLog "try to cat non-existing file" + assertGoodBad "rlRun \"cat 'foobar3'\"" 0 1 + assertGoodBad "rlRun -l \"cat 'foobar3'\"" 0 1 + silentIfNotDebug "rlRun -l 'cat \"foobar3\"'" + assertTrue "rlRun logging plain with bad exit code" "[ $? -eq 1 ]" + + silentIfNotDebug "rlRun -l -t 'echo \"foobar4\"'" + grep 'echo "foobar4"' "$OUTPUTFILE" --quiet && egrep "${PREFIX_REGEXP}"'STDOUT: foobar4' "$OUTPUTFILE" --quiet + assertTrue "rlRun logging with tagging (stdout)" "[ $? -eq 0 ]" + + silentIfNotDebug "rlRun -l -t 'echo \"foobar5\" 1>&2'" + grep 'echo "foobar5" 1>&2' "$OUTPUTFILE" --quiet && egrep "${PREFIX_REGEXP}"'STDERR: foobar5' "$OUTPUTFILE" --quiet + assertTrue "rlRun logging with tagging (stderr)" "[ $? -eq 0 ]" + + silentIfNotDebug "rlRun -s 'echo \"foobar6_stdout\"; echo \"foobar6_stderr\" 1>&2'" + + rlAssertGrep "foobar6_stdout" "$rlRun_LOG" &>/dev/null && rlAssertGrep "foobar6_stderr" "$rlRun_LOG" &>/dev/null + assertTrue "rlRun -s - rlRun_LOG OK" "[ $? -eq 0 ]" + rm -f $rlRun_LOG + + rm -f foobar7 + silentIfNotDebug "rlRun -c 'cat \"foobar7\"'" + grep 'cat "foobar7"' "$OUTPUTFILE" --quiet && egrep "${PREFIX_REGEXP}"'cat: foobar7: No such file or directory' "$OUTPUTFILE" --quiet + assertTrue "rlRun conditional logging plain" "[ $? -eq 0 ]" + + echo 'foobar8_content' > foobar8 + silentIfNotDebug "rlRun -c 'cat \"foobar8\"'" + grep 'cat "foobar8"' "$OUTPUTFILE" --quiet + assertTrue "rlRun conditional logging records command" "[ $? -eq 0 ]" + grep 'foobar8_content' "$OUTPUTFILE" --quiet + assertTrue "rlRun conditional logging do not record output when all is OK" "[ $? -ne 0 ]" + rm -f foobar8 + + rm -f foobar9 + silentIfNotDebug "rlRun -c -t 'cat \"foobar9\" 1>&2'" + grep 'cat "foobar9" 1>&2' "$OUTPUTFILE" --quiet && egrep "${PREFIX_REGEXP}"'STDERR: cat: foobar9: No such file or directory' "$OUTPUTFILE" --quiet + assertTrue "rlRun conditional logging with tagging (stderr)" "[ $? -eq 0 ]" + + assertGoodBad 'rlRun "false|true"' 1 0 + assertGoodBad 'rlRun -s "false|true"; rm -f $rlRun_LOG' 1 0 + + #cleanup + rm -rf "$OUTPUTFILE" + export OUTPUTFILE="$OUTPUTFILE_orig" +} + +watchdogCallback() { + echo "$1" >> /tmp/watchdogCallback +} + +watchdogCallbackTest() { + local res=0 + rm -f /tmp/watchdogCallback + rlWatchdog "sleep 10" "4" KILL "watchdogCallback" & + sleep 3 + [ -e /tmp/watchdogCallback ] && res=1 + echo "res='$res'" + sleep 3 + [ -s /tmp/watchdogCallback ] || res=1 + echo "res='$res'" + rm -f /tmp/watchdogCallback + + return $res +} + +test_rlWatchdog(){ + assertTrue "rlWatchDog detects when command end itself" 'rlWatchdog "sleep 3" 10' + assertFalse "rlWatchDog kills command when time is up" 'rlWatchdog "sleep 10" 3' + assertFalse "running rlWatchdog without timeout must not succeed" 'rlWatchdog "sleep 3"' + assertFalse "running rlWatchdog without any parameters must not succeed" 'rlWatchdog ' + + assertTrue "Callback functionality" "watchdogCallbackTest" +} + +test_rlFail(){ + assertFalse "This should always fail" "rlFail 'sometext'" + assertGoodBad "rlFail 'sometext'" 0 1 +} + +test_rlPass(){ + assertTrue "This should always pass" "rlPass 'sometext'" + assertGoodBad "rlPass 'sometext'" 1 0 +} + +test_rlReport(){ + export BEAKERLIB_COMMAND_REPORT_RESULT=rhts-report-result + journalReset + silentIfNotDebug "rlPhaseStartSetup" + + for res in PASS FAIL WARN + do + OUT="$(rlReport TEST $res | grep 'ANCHOR NAME: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing basic rlReport functionality" "[ \"$OUT\" == \"ANCHOR NAME: TEST\" ]" + OUT="$(rlReport TEST $res | grep 'RESULT: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing basic rlReport functionality" "[ \"$OUT\" == \"RESULT: $res\" ]" + OUT="$(rlReport TEST $res | grep 'LOGFILE: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing basic rlReport functionality" "[ \"$OUT\" == \"LOGFILE: $OUTPUTFILE\" ]" + OUT="$(rlReport TEST $res | grep 'SCORE: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing basic rlReport functionality" "[ \"$OUT\" == \"SCORE: \" ]" + OUT="$(rlReport "TEST TEST" $res | grep 'ANCHOR NAME: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name" "[ \"$OUT\" == \"ANCHOR NAME: TEST TEST\" ]" + OUT="$(rlReport "TEST TEST" $res | grep 'RESULT: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name" "[ \"$OUT\" == \"RESULT: $res\" ]" + OUT="$(rlReport "TEST TEST" $res | grep 'LOGFILE: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name" "[ \"$OUT\" == \"LOGFILE: $OUTPUTFILE\" ]" + OUT="$(rlReport "TEST TEST" $res | grep 'SCORE: ')" + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name" "[ \"$OUT\" == \"SCORE: \" ]" + OUT="$(rlReport "TEST" $res 5 "/tmp/logname" | grep 'ANCHOR NAME: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle all arguments" "[ \"$OUT\" == \"ANCHOR NAME: TEST\" ]" # no-reboot + OUT="$(rlReport "TEST" $res 5 "/tmp/logname" | grep 'RESULT: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle all arguments" "[ \"$OUT\" == \"RESULT: $res\" ]" # no-reboot + OUT="$(rlReport "TEST" $res 5 "/tmp/logname" | grep 'LOGFILE: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle all arguments" "[ \"$OUT\" == \"LOGFILE: /tmp/logname\" ]" # no-reboot + OUT="$(rlReport "TEST" $res 5 "/tmp/logname" | grep 'SCORE: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle all arguments" "[ \"$OUT\" == \"SCORE: 5\" ]" # no-reboot + OUT="$(rlReport "TEST TEST" $res 8 "/tmp/log name" | grep 'ANCHOR NAME: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name and log file" "[ \"$OUT\" == \"ANCHOR NAME: TEST TEST\" ]" # no-reboot + OUT="$(rlReport "TEST TEST" $res 8 "/tmp/log name" | grep 'RESULT: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name and log file" "[ \"$OUT\" == \"RESULT: $res\" ]" # no-reboot + OUT="$(rlReport "TEST TEST" $res 8 "/tmp/log name" | grep 'LOGFILE: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name and log file" "[ \"$OUT\" == \"LOGFILE: /tmp/log name\" ]" # no-reboot + OUT="$(rlReport "TEST TEST" $res 8 "/tmp/log name" | grep 'SCORE: ')" # no-reboot + silentIfNotDebug 'echo "$OUT"' + assertTrue "testing if rlReport can handle spaces in test name and log file" "[ \"$OUT\" == \"SCORE: 8\" ]" # no-reboot + done + silentIfNotDebug "rlPhaseEnd" +} + +test_rlAssert_OutsidePhase(){ + silentIfNotDebug "journalReset" + + silentIfNotDebug 'rlAssert0 "Good assert outside phase" 0' + + silentIfNotDebug 'rlPhaseStartSetup' + silentIfNotDebug 'rlPass "Weeee"' + silentIfNotDebug 'rlPhaseEnd' + + silentIfNotDebug 'rlAssert0 "Bad assert outside phase" 1' + + local TXTJRNL="$( mktemp )" + rlJournalPrintText > "$TXTJRNL" + + assertTrue "Good assert outside phase is printed" "grep 'Good assert outside phase' '$TXTJRNL' | grep PASS" + assertTrue "Bad assert outside phase is printed" "grep 'Bad assert outside phase' '$TXTJRNL' | grep FAIL" + + local rlfails="$(grep 'TEST BUG' $TXTJRNL | grep 'FAIL' | wc -l)" + assertTrue "rlFail raised twice (once for both assertions outside a phase)" "[ '2' == '$rlfails' ]" + + local pseudophases="$(grep 'Asserts collected outside of a phase' $TXTJRNL | grep RESULT | wc -l)" + assertTrue "Two phases created for asserts outside phases" "[ '2' == '$pseudophases' ]" + + rm -f "$TXTJRNL" + silentIfNotDebug 'rlJournalEnd' + + silentIfNotDebug 'journalReset' +} + +test_rlCmpVersion() { + local exp_res=0 res res_part ver1 ver2 op op2 + while read -r exp_res ver1 op ver2; do + assertLog "testing rlCmpVersion '$ver1' '$ver2'" + op2=$(rlCmpVersion "$ver1" "$ver2") + res=$? + assertTrue "test exit code" "[[ '$res' == '$exp_res' ]]" + assertTrue "test printed character" "[[ '$op' == '$op2' ]]" + done << 'EOF' +0 1 = 1 +2 2.1 < 2.2 +1 3.0.4.10 > 3.0.4.2 +2 4.08 < 4.08.01 +1 3.2.1.9.8144 > 3.2 +2 3.2 < 3.2.1.9.8144 +2 1.2 < 2.1 +1 2.1 > 1.2 +0 5.6.7 = 5.6.7 +0 1.01.1 = 1.1.1 +0 1.1.1 = 1.01.1 +0 1 = 1.0 +0 1.0 = 1 +0 1.0.2.0 = 1.0.2 +0 1..0 = 1.0 +0 1.0 = 1..0 +0 0.0 = 0 +2 0.1 < 1 +2 4.8 < 4.08.01 +0 5.5-49.el6_5.3 = 5.5-49.el6_5.3 +1 5.5-50.el6 > 5.5-49.el6_5.3 +2 5.5-49.el6_5.3 < 5.5-49.el6_5.4 +1 5.6-49.el6_5.3 > 5.5-49.el6_5.4 +2 5.5-49.el6_5.3 < 5.5-49.el7_5.3 +EOF +} + +test_rlTestVersion() { + local exp_res=0 res res_part ver1 op ver2 + while read -r exp_res ver1 op ver2; do + assertRun "rlTestVersion '$ver1' '$op' '$ver2'" $exp_res + done << 'EOF' +0 1 = 1 +0 2.1 < 2.2 +0 3.0.4.10 > 3.0.4.2 +0 4.08 < 4.08.01 +0 3.2.1.9.8144 > 3.2 +0 3.2 < 3.2.1.9.8144 +0 1.2 < 2.1 +0 2.1 > 1.2 +0 5.6.7 = 5.6.7 +0 1.01.1 = 1.1.1 +0 1.1.1 = 1.01.1 +0 1 = 1.0 +0 1.0 = 1 +0 1.0.2.0 = 1.0.2 +0 1..0 = 1.0 +0 1.0 = 1..0 +1 1 > 1 +1 0.0 != 0 +0 0.1 != 1 +0 4.8 < 4.08.01 +0 5.5-49.el6_5.3 = 5.5-49.el6_5.3 +0 5.5-50.el6 > 5.5-49.el6_5.3 +0 5.5-49.el6_5.3 < 5.5-49.el6_5.4 +0 5.6-49.el6_5.3 > 5.5-49.el6_5.4 +0 5.5-49.el6_5.3 < 5.5-49.el7_5.3 +0 5.3.2.2-22.el5_10.1 < 5.5-49.el6_5.3 +0 5.5-49.el6_5.3 < 5.7.2-18.el7 +EOF +} + +# fake beakerlib-lsb_release so we can control what rlIsRHEL and others sees +fake_lsb_release(){ + cat >beakerlib-lsb_release <<-EOF +#!/bin/bash +DISTRO="$1" +RELEASE="$2" +[ \$1 = "-ds" ] && { + echo "\$DISTRO \$RELEASE (fakeone)" + exit 0 +} +[ \$1 = "-rs" ] && { echo "\$RELEASE" ; exit 0 ; } +echo invalid input, this stub might be out of date, please +echo update according to __INTERNAL_rlIsDistro usage of beakerlib-lsb_release +exit 1 +EOF + chmod a+x ./beakerlib-lsb_release +} + +test_rlIsRHEL(){ + # pretend we're RHEL6.5 + local OLD_PATH=$PATH + PATH="./:"$PATH + fake_lsb_release "Red Hat Enterprise Linux Server" "6.5" + + assertTrue "major.minor detected correctly" "rlIsRHEL 6.5" + assertTrue "major detected correctly" "rlIsRHEL 6" + assertFalse "incorrect minor" "rlIsRHEL 6.3" + assertFalse "incorrect major" "rlIsRHEL 5.5" + assertTrue "multiple majors, one correct" "rlIsRHEL 4.5 5.5 6.5 7.5" + assertTrue "multiple minors, one correct" "rlIsRHEL 6.3 6.4 6.5 6.6" + assertFalse "multiple args, none correct" "rlIsRHEL 4.5 6.4 7.0" + assertFalse "syntax error: superfluous space #1" "rlIsRHEL '>= 6.3'" + assertFalse "syntax error: superfluous space #2" "rlIsRHEL '>=' '6.3'" + assertFalse "syntax error: operators only" "rlIsRHEL '<='" + assertFalse "syntax error: unknown operator" "rlIsRHEL '*6.5'" + assertTrue "syntax error: no input - checking RHEL only" "rlIsRHEL" + + fake_lsb_release "Red Hat Enterprise Linux Server" "5.10" + assertFalse "<5" "rlIsRHEL '<5'" + assertFalse "<5.0" "rlIsRHEL '<5.0'" + assertFalse "<5.1" "rlIsRHEL '<5.1'" + assertFalse "<5.10" "rlIsRHEL '<5.10'" + assertTrue "<5.11" "rlIsRHEL '<5.11'" + assertFalse ">5" "rlIsRHEL '>5'" + assertTrue ">5.0" "rlIsRHEL '>5.0'" + assertTrue ">5.1" "rlIsRHEL '>5.1'" + assertTrue ">5.9" "rlIsRHEL '>5.9'" + assertFalse ">5.10" "rlIsRHEL '>5.10'" + assertTrue "<6" "rlIsRHEL '<6'" + assertTrue ">4" "rlIsRHEL '>4'" + + assertTrue "<=5" "rlIsRHEL '<=5'" + assertFalse "<=5.0" "rlIsRHEL '<=5.0'" + assertFalse "<=5.1" "rlIsRHEL '<=5.1'" + assertTrue "<=5.10" "rlIsRHEL '<=5.10'" + assertTrue "<=5.11" "rlIsRHEL '<=5.11'" + assertTrue ">=5" "rlIsRHEL '>=5'" + assertTrue ">=5.0" "rlIsRHEL '>=5.0'" + assertTrue ">=5.1" "rlIsRHEL '>=5.1'" + assertTrue ">=5.9" "rlIsRHEL '>=5.9'" + assertTrue ">=5.10" "rlIsRHEL '>=5.10'" + assertFalse ">=5.11" "rlIsRHEL '>=5.11'" + assertTrue "<=6" "rlIsRHEL '<=6'" + assertTrue ">=4" "rlIsRHEL '>=4'" + + #clean up the fake command + PATH=$OLD_PATH + rm -f "./beakerlib-lsb_release" +} + +# just test that CentOS is recognized, operators are tested in rlIsRHEL +test_rlIsCentOS(){ + # pretend we're CentOS7.1 + local OLD_PATH=$PATH + PATH="./:"$PATH + # yup, centos 7.1 has the build number (or what that 1503 is) in release + fake_lsb_release "CentOS" "7.1.1503" + + assertTrue "major.minor CentOS7.1 detected correctly" "rlIsCentOS 7.1" + assertTrue "major CentOS7 detected correctly" "rlIsCentOS 7" + + assertFalse "CentOS7.1 not mistaken for RHEL7.1" "rlIsRHEL 7.1" + assertFalse "CentOS7 not mistaken for RHEL7" "rlIsRHEL 7" + + + fake_lsb_release "CentOS" "5.11" + assertTrue "major.minor CentOS5.11 detected correctly" "rlIsCentOS 5.11" + + fake_lsb_release "CentOS" "6.6" + assertTrue "major.minor CentOS6.6 detected correctly" "rlIsCentOS 6.6" + + #clean up the fake command + PATH=$OLD_PATH + rm -f "./beakerlib-lsb_release" +} + +# just test that Fedora is recognized, operators are tested in rlIsRHEL +test_rlIsFedora(){ + # pretend we're CentOS7.1 + local OLD_PATH=$PATH + PATH="./:"$PATH + + fake_lsb_release "Fedora" "25" + assertTrue "major Fedora 25 detected correctly" "rlIsFedora 25" + + fake_lsb_release "Fedora" "26" + assertTrue "major Fedora 26 detected correctly" "rlIsFedora 26" + + #clean up the fake command + PATH=$OLD_PATH + rm -f "./beakerlib-lsb_release" +} + +test_rlHash(){ + local STRING="string to be hashed" + local STRING_HEX=$(echo -n $STRING | od -A n -t x1 -v | tr -d ' \n\t') + local STRING_BASE64=$(echo -n $STRING | base64) + local STRING_BASE64_=$(echo -n $STRING | base64 | tr '=' '_') + + # rlHash + local rlHashed=$(rlHash "$STRING") + assertTrue "rlHash default algorithm" "[[ $rlHashed == $STRING_HEX ]]" + + local rlHashed=$(rlHash --algorithm=hex "$STRING") + assertTrue "rlHash hex algorithm" "[[ $rlHashed == $STRING_HEX ]]" + + local rlHashed=$(rlHash --algorithm=base64 "$STRING") + assertTrue "rlHash base64 algorithm" "[[ $rlHashed == $STRING_BASE64 ]]" + + local rlHashed=$(rlHash --algorithm=base64_ "$STRING") + assertTrue "rlHash base64_ algorithm" "[[ $rlHashed == $STRING_BASE64_ ]]" + + # rlHash --decode + local rlHashed=$(rlHash --decode "$STRING_HEX") + assertTrue "rlHash --decode default algorithm" "[[ \"$rlHashed\" == \"$STRING\" ]]" + + local rlHashed=$(rlHash --decode --algorithm=hex "$STRING_HEX") + assertTrue "rlHash --decode hex algorithm" "[[ \"$rlHashed == $STRING\" ]]" + + local rlHashed=$(rlHash --decode --algorithm=base64 "$STRING_BASE64") + assertTrue "rlHash --decode base64 algorithm" "[[ \"$rlHashed\" == \"$STRING\" ]]" + + local rlHashed=$(rlHash --decode --algorithm=base64_ "$STRING_BASE64_") + assertTrue "rlHash --decode base64_ algorithm" "[[ \"$rlHashed\" == \"$STRING\" ]]" +} + +test_rlUnhash(){ + local STRING="string to be unhashed" + local STRING_HEX=$(echo -n $STRING | od -A n -t x1 -v | tr -d ' \n\t') + local STRING_BASE64=$(echo -n $STRING | base64) + local STRING_BASE64_=$(echo -n $STRING | base64 | tr '=' '_') + + local rlUnhashed=$(rlUnhash "$STRING_HEX") + assertTrue "rlUnhash default algorithm" "[[ \"$rlUnhashed\" == \"$STRING\" ]]" + + local rlUnhashed=$(rlUnhash --algorithm=hex "$STRING_HEX") + assertTrue "rlUnhash hex algorithm" "[[ \"$rlUnhashed == $STRING\" ]]" + + local rlUnhashed=$(rlUnhash --algorithm=base64 "$STRING_BASE64") + assertTrue "rlUnhash base64 algorithm" "[[ \"$rlUnhashed\" == \"$STRING\" ]]" + + local rlUnhashed=$(rlUnhash --algorithm=base64_ "$STRING_BASE64_") + assertTrue "rlUnhash base64_ algorithm" "[[ \"$rlUnhashed\" == \"$STRING\" ]]" +} diff --git a/src/test/testwatcher.sh b/src/test/testwatcher.sh new file mode 100755 index 0000000..66537ad --- /dev/null +++ b/src/test/testwatcher.sh @@ -0,0 +1,281 @@ +#!/bin/bash +# +# Authors: Jiri Jaburek +# +# Description: A one-file test suite for testwatcher.py +# +# Copyright (c) 2013 Red Hat, Inc. All rights reserved. This copyrighted +# material is made available to anyone wishing to use, modify, copy, or +# redistribute it subject to the terms and conditions of the GNU General +# Public License v.2. +# +# 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. + +# +# this is a testing script for the test watcher tool (python/testwatcher.sh), +# the goal being verification in a standalone, beakerlib-independent fashion, +# since the tool is also standalone by design +# +# beakerlib side is covered by infrastructureTest.sh to some extent, the TODO +# being combination of test watcher with the beakerlib rlCleanup* functions, +# somehow +# +# also note that while testwatcher.py has a certain logic of detecting whether +# the beaker harness (beah) is running, this logic is only used for very beah +# specific things like reportning WARN to journal and hooking the LWD, neither +# of which is directly related to handling the test/cleanup pair +# therefore, we don't stimulate that beah logic here, at least for now + +error() +{ + echo "error: $@" 1>&2 + exit 1 +} +FAILED=0 +fail() +{ + (( FAILED++ )) + echo -e "\e[1;31m:::::::: FAILED ::::::::\e[0m" +} +testcase() +{ + echo + echo -e "\e[1;34m--------------------------------------------------------------------------------\e[0m" + echo -e "\e[1;34mtestcase:\e[0m $1" + echo +} +mktest() +{ + filename="$1" + shift + { + echo "#!/bin/bash" + while [ $# -gt 0 ]; do + echo "$1" + shift + done; + } > "$filename" + chmod +x "$filename" +} ++() +{ + local rc= + local PS4="\e[1;33m+\e[0m " + { set -x; } 2>/dev/null + "$@" + { rc=$?; set +x; } 2>/dev/null + return $rc +} + + +# be strict during setup +set -e + +tmpdir=$(mktemp -d) +trap "rm -rf \"$tmpdir\"" EXIT + +#copy testwatcher.py +watcherloc="../python/testwatcher.py" +[ -f "$watcherloc" ] || watcherloc="python/testwatcher.py" +[ -f "$watcherloc" ] || error "could not find testwatcher.py" +cp "$watcherloc" "$tmpdir/." +cd "$tmpdir" + +chmod +x testwatcher.py + +set +e + + +################################################################################ + +# +# basic sanity success operations: +# - test successful, cleanup not set up +# - test successful, cleanup successful +######## +testcase "sanity: test successful, cleanup not set up" +mktest test.sh 'echo end > test.log' ++ ./testwatcher.py ./test.sh ++ grep 'end' test.log || fail +rm -f test.sh test.log + +######## +testcase "sanity: test successful, cleanup successful" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo end > test.log' +mktest cleanup.sh 'echo end > cleanup.log' ++ ./testwatcher.py ./test.sh ++ grep 'end' test.log || fail ++ grep 'end' cleanup.log || fail +rm -f test.sh test.log cleanup.sh cleanup.log + +# +# extended sanity testing: +# - arguments with spaces +# - ... +# TODO more sanity here (no argument passed, ...) +######## +testcase "sanity: successful execution with passed arguments" +mktest test.sh 'while [ $# -gt 0 ]; do echo "> $1" >> test.log; shift; done;' ++ ./testwatcher.py ./test.sh "first argument" "second argument" ++ grep '^> first argument' test.log || fail ++ grep '^> second argument' test.log || fail +rm -f test.sh test.log + +# +# user-controlled (no beah) scenarios (SIGINT): +# - test interrupted, cleanup not set up +# - test interrupted, cleanup successful +# - test successful, cleanup interrupted +# - test interrupted, cleanup interrupted +######## +testcase "user(SIGINT): test interrupted, cleanup not set up" +mktest test.sh 'echo start > test.log; sleep 10; echo end >> test.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -INT -P $! testwatcher.py # $! is the '+' function, parent of tw.py +wait ++ grep 'start' test.log || fail ++ grep 'end' test.log && fail # test is supposed to be killed before end! +rm -f test.sh test.log + +######## +testcase "user(SIGINT): test interrupted, cleanup successful" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo start > test.log; sleep 10; echo end >> test.log' +mktest cleanup.sh 'echo end > cleanup.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -INT -P $! testwatcher.py +wait ++ grep 'start' test.log || fail ++ grep 'end' test.log && fail ++ grep 'end' cleanup.log || fail +rm -f test.sh test.log cleanup.sh cleanup.log + +######## +testcase "user(SIGINT): test successful, cleanup interrupted" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo end > test.log' +mktest cleanup.sh 'echo start > cleanup.log; sleep 10; echo end >> cleanup.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -INT -P $! testwatcher.py +wait ++ grep 'end' test.log || fail ++ grep 'start' cleanup.log || fail ++ grep 'end' cleanup.log && fail # cleanup is supposed to be killed +rm -f test.sh test.log cleanup.sh cleanup.log + +######## +testcase "user(SIGINT): test interrupted, cleanup interrupted" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo start > test.log; sleep 10; echo end >> test.log' +mktest cleanup.sh 'echo start > cleanup.log; sleep 10; echo end >> cleanup.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -INT -P $! testwatcher.py +sleep 1 ++ pkill -INT -P $! testwatcher.py +wait ++ grep 'start' test.log || fail ++ grep 'end' cleanup.log && fail ++ grep 'start' cleanup.log || fail ++ grep 'end' cleanup.log && fail +rm -f test.sh test.log cleanup.sh cleanup.log + +# +# beaker-controlled (beah) scenarios (SIGHUP/SIGTERM): +# - test successful, cleanup successful, LWD expired during cleanup +# - test interrupted (LWD), cleanup not set up +# - test interrupted (LWD), cleanup successful +# - test successful, cleanup interrupted (EWD) +# - test interrupted (LWD), cleanup interrupted (EWD) +######## +testcase "beah(SIGHUP): test successful, cleanup successful, LWD during cleanup" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo end > test.log' +mktest cleanup.sh 'echo start > cleanup.log; sleep 2; echo end >> cleanup.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -HUP -P $! testwatcher.py # only schedules EWD kill in 1 more sec +wait ++ grep 'end' test.log || fail ++ grep 'start' cleanup.log || fail ++ grep 'end' cleanup.log || fail +rm -f test.sh test.log cleanup.sh cleanup.log + +######## +testcase "beah(SIGHUP): test interrupted (LWD), cleanup not set up" +mktest test.sh 'echo start > test.log; sleep 10; echo end >> test.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -HUP -P $! testwatcher.py +wait ++ grep 'start' test.log || fail ++ grep 'end' test.log && fail +rm -f test.sh test.log + +######## +testcase "beah(SIGHUP): test interrupted (LWD), cleanup successful" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo start > test.log; sleep 10; echo end >> test.log' +mktest cleanup.sh 'echo end > cleanup.log' ++ ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -HUP -P $! testwatcher.py +wait ++ grep 'start' test.log || fail ++ grep 'end' test.log && fail ++ grep 'end' cleanup.log || fail +rm -f test.sh test.log cleanup.sh cleanup.log + +######## +testcase "beah(SIGHUP): test successful, cleanup interrupted (EWD)" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo end > test.log' +mktest cleanup.sh 'echo start > cleanup.log; sleep 10; echo end >> cleanup.log' +TESTWATCHER_EWD_SECS=1 + ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -HUP -P $! testwatcher.py # only schedules EWD kill in 1 more sec +wait ++ grep 'end' test.log || fail ++ grep 'start' cleanup.log || fail ++ grep 'end' cleanup.log && fail +rm -f test.sh test.log cleanup.sh cleanup.log + +######## +testcase "beah(SIGHUP): test interrupted (LWD), cleanup interrupted (EWD)" +mktest test.sh 'echo ./cleanup.sh > "$TESTWATCHER_CLPATH"' \ + 'echo start > test.log; sleep 10; echo end >> test.log' +mktest cleanup.sh 'echo start > cleanup.log; sleep 10; echo end >> cleanup.log' +TESTWATCHER_EWD_SECS=1 + ./testwatcher.py ./test.sh & +sleep 1 ++ pkill -HUP -P $! testwatcher.py +#sleep 1 +#+ pkill -HUP -P $! testwatcher.py # not needed, first HUP scheduled EWD kill +wait ++ grep 'start' test.log || fail ++ grep 'end' cleanup.log && fail ++ grep 'start' cleanup.log || fail ++ grep 'end' cleanup.log && fail +rm -f test.sh test.log cleanup.sh cleanup.log + + + +################################################################################ + +echo +echo +echo "==========" +echo "FAILED: $FAILED" +exit $FAILED + +# vim: sts=4 sw=4 et : diff --git a/src/testing.sh b/src/testing.sh new file mode 100644 index 0000000..04df9dd --- /dev/null +++ b/src/testing.sh @@ -0,0 +1,1328 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: testing.sh - part of the BeakerLib project +# Description: Asserting functions, watchdog and report +# +# Author: Ondrej Hudlicky +# Author: Petr Muller +# Author: Jan Hutar +# Author: Petr Splichal +# Author: Ales Zelinka +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +export __INTERNAL_DEFAULT_REPORT_RESULT=/bin/true + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - testing - asserting functions, watchdog and report + +=head1 DESCRIPTION + +This file contains functions related directly to testing. These functions are +various asserts affecting final result of the phase. Watchdog and the report +result function is included as well. + +=head1 FUNCTIONS + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +. $BEAKERLIB/logging.sh +. $BEAKERLIB/journal.sh + +__INTERNAL_LogAndJournalPass() { + rljAddTest "$1 $2" "PASS" "$3" +} + +__INTERNAL_LogAndJournalFail() { + rljAddTest "$1 $2" "FAIL" "$3" +} + +# __INTERNAL_ConditionalAssert comment status [failed-comment] [executed command-line] +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +__INTERNAL_ConditionalAssert() { + if [ "$2" == "0" ]; then + __INTERNAL_LogAndJournalPass "$1" "$3" "$4" + return 0 + else + __INTERNAL_LogAndJournalFail "$1" "$3" "$4" + return 1 + fi +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlPass +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Manual Asserts + +=head3 rlPass + +Manual assertion, asserts and logs PASS. + + rlPass comment + +=over + +=item comment + +Short test summary. + +=back + +Returns 0 and asserts PASS. + +=cut + +rlPass() { + __INTERNAL_LogAndJournalPass "$1" + return 0 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlFail +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlFail + +Manual assertion, asserts and logs FAIL. + + rlFail comment + +=over + +=item comment + +Short test summary. + +=back + +Returns 1 and asserts FAIL. + +=cut + +rlFail() { + __INTERNAL_LogAndJournalFail "$1" + return 1 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssert0 +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Arithmetic Asserts + +=head3 rlAssert0 + +Assertion checking for the equality of parameter to zero. + + rlAssert0 comment value + +=over + +=item comment + +Short test summary, e.g. "Test if compilation ended successfully". + +=item value + +Integer value (usually return code of a command). + +=back + +Returns 0 and asserts PASS when C. + +=cut + +rlAssert0() { + __INTERNAL_ConditionalAssert "$1" "$2" "(Assert: expected 0, got $2)" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertEquals +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertEquals + +Assertion checking for the equality of two parameters. + + rlAssertEquals comment value1 value2 + +=over + +=item comment + +Short test summary, e.g. "Test if all 3 packages have been downloaded". + +=item value1 + +First parameter to compare, can be a number or a string + +=item value2 + +Second parameter to compare, can be a number or a string + +=back + +Returns 0 and asserts PASS when C. + +=cut + +rlAssertEquals() { + if [ $# -lt 3 ] ; then + __INTERNAL_LogAndJournalFail "rlAssertEquals called without all needed parameters" "" + return 1 + fi + __INTERNAL_ConditionalAssert "$1" "$([ "$2" == "$3" ]; echo $?)" "(Assert: '$2' should equal '$3')" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertNotEquals +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertNotEquals + +Assertion checking for the non-equality of two parameters. + + rlAssertNotEquals comment value1 value2 + +=over + +=item comment + +Short test summary, e.g. "Test if return code is not 139". + +=item value1 + +First parameter to compare, can be a number or a string + +=item value2 + +Second parameter to compare, can be a number or a string + +=back + +Returns 0 and asserts PASS when C. + +=cut + +rlAssertNotEquals() { + if [ $# -lt 3 ] ; then + __INTERNAL_LogAndJournalFail "rlAssertNotEquals called without all needed parameters" "" + return 1 + fi + __INTERNAL_ConditionalAssert "$1" "$([ "$2" != "$3" ]; echo $?)" "(Assert: \"$2\" should not equal \"$3\")" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertGreater +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertGreater + +Assertion checking whether first parameter is greater than the second one. + + rlAssertGreater comment value1 value2 + +=over + +=item comment + +Short test summary, e.g. "Test whether there are running more instances of program." + +=item value1 + +Integer value. + +=item value2 + +Integer value. + +=back + +Returns 0 and asserts PASS when C value2>. + +=cut + +rlAssertGreater() { + __INTERNAL_ConditionalAssert "$1" "$([ "$2" -gt "$3" ]; echo $?)" "(Assert: \"$2\" should be greater than \"$3\")" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertGreaterOrEqual +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertGreaterOrEqual + +Assertion checking whether first parameter is greater or equal to the second one. + + rlAssertGreaterOrEqual comment value1 value2 + +=over + +=item comment + +Short test summary (e.g. "There should present at least one...") + +=item value1 + +Integer value. + +=item value2 + +Integer value. + +=back + +Returns 0 and asserts PASS when C= value2>. + +=cut + +rlAssertGreaterOrEqual() { + __INTERNAL_ConditionalAssert "$1" "$([ "$2" -ge "$3" ]; echo $?)" "(Assert: \"$2\" should be >= \"$3\")" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertLesser +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertLesser + +Assertion checking whether first parameter is lesser than the second one. + + rlAssertLesser comment value1 value2 + +=over + +=item comment + +Short test summary, e.g. "Test whether there are running more instances of program." + +=item value1 + +Integer value. + +=item value2 + +Integer value. + +=back + +Returns 0 and asserts PASS when C value2>. + +=cut + +rlAssertLesser() { + __INTERNAL_ConditionalAssert "$1" "$([ "$2" -le "$3" ]; echo $?)" "(Assert: \"$2\" should be lesser than \"$3\")" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertLesserOrEqual +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertLesserOrEqual + +Assertion checking whether first parameter is lesser or equal to the second one. + + rlAssertLesserOrEqual comment value1 value2 + +=over + +=item comment + +Short test summary (e.g. "There should present at least one...") + +=item value1 + +Integer value. + +=item value2 + +Integer value. + +=back + +Returns 0 and asserts PASS when C= value2>. + +=cut + +rlAssertLesserOrEqual() { + __INTERNAL_ConditionalAssert "$1" "$([ "$2" -le "$3" ]; echo $?)" "(Assert: \"$2\" should be <= \"$3\")" + return $? +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertExists +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 File Asserts + +=head3 rlAssertExists + +Assertion checking for the existence of a file or a directory. + + rlAssertExists file|directory + +=over + +=item file|directory + +Path to the file or directory. + +=back + +Returns 0 and asserts PASS when C exists. + +=cut + +rlAssertExists(){ + if [ -z "$1" ] ; then + __INTERNAL_LogAndJournalFail "rlAssertExists called without parameter" "" + return 1 + fi + local FILE="File" + if [ -d "$1" ] ; then + FILE="Directory" + fi + __INTERNAL_ConditionalAssert "$FILE $1 should exist" "$([ -e "$1" ]; echo $?)" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertNotExists +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head3 rlAssertNotExists + +Assertion checking for the non-existence of a file or a directory. + + rlAssertNotExists file|directory + +=over + +=item file|directory + +Path to the file or directory. + +=back + +Returns 0 and asserts PASS when C does not exist. + +=cut + +rlAssertNotExists(){ + if [ -z "$1" ] ; then + __INTERNAL_LogAndJournalFail "rlAssertNotExists called without parameter" "" + return 1 + fi + local FILE="File" + if [ -d "$1" ] ; then + FILE="Directory" + fi + __INTERNAL_ConditionalAssert "$FILE $1 should not exist" "$([ ! -e "$1" ]; echo $?)" + return $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertGrep +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertGrep + +Assertion checking if the file contains a pattern. + + rlAssertGrep pattern file [options] + +=over + +=item pattern + +Regular expression to be searched for. + +=item file + +Path to the file. + +=item options + +Optional parameters to be passed to grep, default is C<-q>. Can be +used to perform case insensitive matches (-i), or using +extended (-E) or perl (-P) regular expressions. + +=back + +Returns 0 and asserts PASS when C exists and contains given +C. + +=cut + +rlAssertGrep(){ + if [ ! -e "$2" ] ; then + __INTERNAL_LogAndJournalFail "rlAssertGrep: failed to find file $2" + return 2 + fi + local options=${3:--q} + grep $options -- "$1" "$2" + __INTERNAL_ConditionalAssert "File '$2' should contain '$1'" $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertNotGrep +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertNotGrep + +Assertion checking that the file does not contain a pattern. + + rlAssertNotGrep pattern file [options] + +=over + +=item pattern + +Regular expression to be searched for. + +=item file + +Path to the file. + +=item options + +Optional parameters to be passed to grep, default is C<-q>. Can be +used to perform case insensitive matches (-i), or using +extended (-E) or perl (-P) regular expressions. + +=back + +Returns 0 and asserts PASS when C exists and does not +contain given C. + +=cut +rlAssertNotGrep(){ + if [ ! -e "$2" ] ; then + __INTERNAL_LogAndJournalFail "rlAssertNotGrep: failed to find file $2" + return 2 + fi + local options=${3:--q} + ! grep $options -- "$1" "$2" + __INTERNAL_ConditionalAssert "File '$2' should not contain '$1'" $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertDiffer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertDiffer + +Assertion checking that two files differ (are not identical). + + rlAssertDiffer file1 file2 + +=over + +=item file1 + +Path to first file1 + +=item file2 + +Path to second file + +=back + +Returns 0 and asserts PASS when C and C differs. + +=cut + +rlAssertDiffer(){ + local file + for file in "$1" "$2"; do + if [ ! -e "$file" ]; then + __INTERNAL_LogAndJournalFail "rlAssertDiffer: file $file was not found" + return 2 + fi + done + ! cmp -s "$1" "$2" + __INTERNAL_ConditionalAssert "Files $1 and $2 should differ" $? +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlAssertNotDiffer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlAssertNotDiffer + +Assertion checking that two files do not differ (are identical). + + rlAssertNotDiffer file1 file2 + +=over + +=item file1 + +Path to first file1 + +=item file2 + +Path to second file + +=back + +Returns 0 and asserts PASS when C and C do not differ. + +=cut + +rlAssertNotDiffer() { + local file + for file in "$1" "$2"; do + if [ ! -e "$file" ]; then + __INTERNAL_LogAndJournalFail "rlAssertNotDiffer: file $file was not found" + return 2 + fi + done + + cmp -s "$1" "$2" + __INTERNAL_ConditionalAssert "Files $1 and $2 should not differ" $? +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlRun +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Run, Watch, Report + +=head3 rlRun + +Run command with optional comment and make sure its exit code +matches expectations. + + rlRun [-t] [-l] [-c] [-s] command [status[,status...] [comment]] + +=over + +=item -t + +If specified, stdout and stderr of the command output will be tagged +with strigs 'STDOUT: ' and 'STDERR: '. + +=item -l + +If specified, output of the command (tagged, if -t was specified) is +logged using rlLog function. This is intended for short outputs, and +therefore only last 50 lines are logged this way. Longer outputs should +be analysed separately, or uploaded via rlFileSubmit or rlBundleLogs. + +=item -c + +Same as C<-l>, but only log the commands output if it failed. + +=item -s + +Store stdout and stderr to a file (mixed together, as the user would see +it on a terminal) and set $rlRun_LOG variable to name of the file. Caller +is responsible for removing the file. When -t option is used, the content +of the file becomes tagged too. + +If the -s option is not used, $rlRun_LOG is not modified and keeps its +content from the last "rlRun -s". + +=item command + +Command to run. + +=item status + +Expected exit code(s). Optional, default 0. If you expect more +exit codes, separate them with comma (e.g. "0,1" when both 0 and 1 +are OK and expected), or use from-to notation (i.e. "2-5" for "2,3,4,5"), +or combine them (e.g. "2-4,26" for "2,3,4,26"). + +=item comment + +Short summary describing the action (optional, but recommended - +explain what are you doing here). + +=back + +Returns the exit code of the command run. Asserts PASS when +command\'s exit status is in the list of expected exit codes. + +Note: + +=over + +=item + +The output of rlRun is buffered when using C<-t>, C<-l> or C<-s> +option (they use unix pipes, which are buffered by nature). If you +need an unbuffered output just make sure that C package is +installed on your system (its "unbuffer" tool will automatically +be used to produce unbuffered output). + +=item + +Be aware that there are some variables which can collide with your code executed +within rlRun. You should avoid using __INTERNAL_rlRun_* variables. + +=back + +B using C tool is now disabled because of bug 547686. + +=cut +#' + +rlRun() { + local __INTERNAL_rlRun_GETOPT=$(getopt -q -o lcts -- "$@") + eval set -- "$__INTERNAL_rlRun_GETOPT" + + local __INTERNAL_rlRun_DO_LOG=false + local __INTERNAL_rlRun_DO_TAG=false + local __INTERNAL_rlRun_DO_KEEP=false + local __INTERNAL_rlRun_DO_CON=false + local __INTERNAL_rlRun_TAG_OUT='' + local __INTERNAL_rlRun_TAG_ERR='' + local __INTERNAL_rlRun_LOG_FILE='' + + while true ; do + case "$1" in + -l) + __INTERNAL_rlRun_DO_LOG=true; + shift;; + -c) + __INTERNAL_rlRun_DO_LOG=true; + __INTERNAL_rlRun_DO_CON=true; + shift;; + -t) + __INTERNAL_rlRun_DO_TAG=true; + __INTERNAL_rlRun_TAG_OUT='STDOUT: ' + __INTERNAL_rlRun_TAG_ERR='STDERR: ' + shift;; + -s) + __INTERNAL_rlRun_DO_KEEP=true + shift;; + --) + shift; + break;; + *) + shift;; + esac + done + + local __INTERNAL_rlRun_command=$1 + local __INTERNAL_rlRun_expected_orig=${2:-0} + local __INTERNAL_rlRun_expected=${2:-0} + local __INTERNAL_rlRun_comment + local __INTERNAL_rlRun_comment_begin + if [[ -z "$3" ]]; then + __INTERNAL_rlRun_comment_begin="Running '$__INTERNAL_rlRun_command'" + __INTERNAL_rlRun_comment="Command '$__INTERNAL_rlRun_command'" + else + __INTERNAL_rlRun_comment_begin="$3 :: actually running '$__INTERNAL_rlRun_command'" + __INTERNAL_rlRun_comment="$3" + fi + + # here we can do various sanity checks of the $command + if [[ "$__INTERNAL_rlRun_command" =~ ^[[:space:]]*$ ]] ; then + rlFail "rlRun: got empty or blank command '$__INTERNAL_rlRun_command'!" + return 1 + elif false ; then + # this an example check + rlFail "rlRun: sanity check of command '$__INTERNAL_rlRun_command' failed!" + return 1 + fi + + # create __INTERNAL_rlRun_LOG_FILE if needed + if $__INTERNAL_rlRun_DO_LOG || $__INTERNAL_rlRun_DO_KEEP + then + __INTERNAL_rlRun_LOG_FILE=$( mktemp --tmpdir=$__INTERNAL_PERSISTENT_TMP rlRun_LOG.XXXXXXXX ) + if [ ! -e "$__INTERNAL_rlRun_LOG_FILE" ] + then + rlFail "rlRun: Internal file creation failed" + rlLogError "rlRun: Please report this issue to RH Bugzilla for Beakerlib component" + rlLogError "rlRun: Turning off any -l, -c or -s options of rlRun" + rlLogError "rlRun: Unless the test relies on them, rest of the test can be trusted." + __INTERNAL_rlRun_DO_LOG=false + __INTERNAL_rlRun_DO_KEEP=false + __INTERNAL_rlRun_LOG_FILE=/dev/null + fi + fi + + # in case expected exit code is provided as "2-5,26", expand it to "2,3,4,5,26" + while echo "$__INTERNAL_rlRun_expected" | grep -q '[0-9]-[0-9]'; do + local __INTERNAL_rlRun_interval=$(echo "$__INTERNAL_rlRun_expected" | sed "s/.*\(\<[0-9]\+-[0-9]\+\>\).*/\1/") + if [ -z "$__INTERNAL_rlRun_interval" ]; then + rlLogWarning "rlRun: Something happened when getting interval, using '0-0'" + __INTERNAL_rlRun_interval='0-0' + fi + local __INTERNAL_rlRun_interval_a=$(echo "$__INTERNAL_rlRun_interval" | cut -d '-' -f 1) + local __INTERNAL_rlRun_interval_b=$(echo "$__INTERNAL_rlRun_interval" | cut -d '-' -f 2) + if [ -z "$__INTERNAL_rlRun_interval_a" -o -z "$__INTERNAL_rlRun_interval_b" ]; then + rlLogWarning "rlRun: Something happened when getting boundaries of interval, using '0' and '0'" + __INTERNAL_rlRun_interval_a=0 + __INTERNAL_rlRun_interval_b=0 + fi + if [ $__INTERNAL_rlRun_interval_a -gt $__INTERNAL_rlRun_interval_b ]; then + rlLogWarning "rlRun: First boundary have to be smaller then second one, using '$__INTERNAL_rlRun_interval_b' and '$__INTERNAL_rlRun_interval_b'" + __INTERNAL_rlRun_interval_a=$__INTERNAL_rlRun_interval_b + fi + local __INTERNAL_rlRun_replacement="$__INTERNAL_rlRun_interval_a" + let __INTERNAL_rlRun_interval_a=$__INTERNAL_rlRun_interval_a+1 + + local __INTERNAL_rlRun_i + for __INTERNAL_rlRun_i in $(seq $__INTERNAL_rlRun_interval_a $__INTERNAL_rlRun_interval_b); do + __INTERNAL_rlRun_replacement="$__INTERNAL_rlRun_replacement,$__INTERNAL_rlRun_i" + done + __INTERNAL_rlRun_expected="${__INTERNAL_rlRun_expected//$__INTERNAL_rlRun_interval/$__INTERNAL_rlRun_replacement/}" + done + + rlLogDebug "rlRun: Running command: $__INTERNAL_rlRun_command" + + __INTERNAL_PrintText "$__INTERNAL_rlRun_comment_begin" "BEGIN" + + if $__INTERNAL_rlRun_DO_LOG || $__INTERNAL_rlRun_DO_TAG || $__INTERNAL_rlRun_DO_KEEP; then + eval "$__INTERNAL_rlRun_command" 2> >(sed -u -e "s/^/$__INTERNAL_rlRun_TAG_ERR/g" | + tee -a $__INTERNAL_rlRun_LOG_FILE) 1> >(sed -u -e "s/^/$__INTERNAL_rlRun_TAG_OUT/g" | tee -a $__INTERNAL_rlRun_LOG_FILE) + local __INTERNAL_rlRun_exitcode=$? + else + eval "$__INTERNAL_rlRun_command" + local __INTERNAL_rlRun_exitcode=$? + fi + rlLogDebug "rlRun: command = '$__INTERNAL_rlRun_command'; exitcode = $__INTERNAL_rlRun_exitcode; expected = $__INTERNAL_rlRun_expected" + if $__INTERNAL_rlRun_DO_LOG || $__INTERNAL_rlRun_DO_TAG || $__INTERNAL_rlRun_DO_KEEP; then + sync + fi + + echo "$__INTERNAL_rlRun_expected" | grep -q "\<$__INTERNAL_rlRun_exitcode\>" # symbols \< and \> match the empty string at the beginning and end of a word + local __INTERNAL_rlRun_result=$? + + if $__INTERNAL_rlRun_DO_LOG && ( ! $__INTERNAL_rlRun_DO_CON || ( $__INTERNAL_rlRun_DO_CON && [ $__INTERNAL_rlRun_result -ne 0 ] ) ); then + rlLog "Output of '$__INTERNAL_rlRun_command':" + rlLog "--------------- OUTPUT START ---------------" + local __INTERNAL_rlRun_line + tail -n 50 "$__INTERNAL_rlRun_LOG_FILE" | while read __INTERNAL_rlRun_line + do + rlLog "$__INTERNAL_rlRun_line" + done + rlLog "--------------- OUTPUT END ---------------" + fi + if $__INTERNAL_rlRun_DO_KEEP; then + rlRun_LOG=$__INTERNAL_rlRun_LOG_FILE + export rlRun_LOG + elif $__INTERNAL_rlRun_DO_LOG; then + rm $__INTERNAL_rlRun_LOG_FILE + fi + + rlLogDebug "rlRun: Command finished with exit code: $__INTERNAL_rlRun_exitcode, expected: $__INTERNAL_rlRun_expected_orig" + __INTERNAL_ConditionalAssert "$__INTERNAL_rlRun_comment" $__INTERNAL_rlRun_result "(Expected $__INTERNAL_rlRun_expected_orig, got $__INTERNAL_rlRun_exitcode)" "$__INTERNAL_rlRun_command" + + return $__INTERNAL_rlRun_exitcode +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlWatchdog +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlWatchdog + +Run C. If it does not finish in specified time, then kill +it using C. + + rlWatchdog command timeout [signal] [callback] + +=over + +=item command + +Command to run. + +=item timeout + +Timeout to wait, in seconds. + +=item signal + +Signal to use (optional, default KILL). + +=item callback + +Callback function to be called before the signal is send (optional, none +by default). The callback function will have one argument available -- PGID +of the process group. + +=back + +Returns 0 if the command ends normally, without need to be killed. + +=cut + +rlWatchdog() { + set -m + local command=$1 + local timeout=$2 + local killer=${3:-"KILL"} + local callback=${4:-""} + rm -f __INTERNAL_FINISHED __INTERNAL_TIMEOUT + rlLog "Runnning $command, with $timeout seconds timeout" + eval "$command; touch __INTERNAL_FINISHED" & + local pidcmd=$! + eval "sleep $timeout; touch __INTERNAL_TIMEOUT" & + local pidsleep=$! + + while true; do + if [ -e __INTERNAL_FINISHED ]; then + rlLog "Command ended itself, I am not killing it." + /bin/kill -- -$pidsleep + sleep 1 + rm -f __INTERNAL_FINISHED __INTERNAL_TIMEOUT + return 0 + elif [ -e __INTERNAL_TIMEOUT ]; then + rlLog "Command is still running, I am killing it with $killer" + if [ -n "$callback" ] \ + && type $callback 2>/dev/null | grep -q "$callback is a function" + then + rlLog "Function $callback is present, I am calling it" + $callback $pidcmd + fi + /bin/kill -$killer -- -$pidcmd + sleep 1 + rm -f __INTERNAL_FINISHED __INTERNAL_TIMEOUT + return 1 + fi + sleep 1 + done +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlReport +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlReport + +Report test result to the test harness. The command to be used for +reporting is set by the respective plugin. You can also use the +C<$BEAKERLIB_COMMAND_REPORT_RESULT> variable to use your custom +command. + + rlReport name result [score] [log] + +=over + +=item name + +Name of the test result. + +=item result + +Result (one of PASS, WARN, FAIL). If called with something +else, will use WARN. + +=item score + +Test score (optional). + +=item log + +Optional log file to be submitted instead of default C. + +=back + +=cut + +rlReport() { + # only PASS/WARN/FAIL is allowed + local testname="$1" + local result="$(echo "$2" | tr '[:lower:]' '[:upper:]')" + local score="$3" + local logfile=${4:-$OUTPUTFILE} + case "$result" in + 'PASS' | 'PASSED' | 'PASSING') result='PASS'; ;; + 'FAIL' | 'FAILED' | 'FAILING') result='FAIL'; ;; + 'WARN' | 'WARNED' | 'WARNING') result='WARN'; ;; + *) + rlLogWarning "rlReport: Only PASS/WARN/FAIL results are possible." + result='WARN' + ;; + esac + rlLogDebug "rlReport: result: $result, score: $score, log: $logfile" + if [ -z "$BEAKERLIB_COMMAND_REPORT_RESULT" ]; then + local BEAKERLIB_COMMAND_REPORT_RESULT="$__INTERNAL_DEFAULT_REPORT_RESULT" + fi + + # report the result only if TESTID is set + if [ -n "$TESTID" ] ; then + $BEAKERLIB_COMMAND_REPORT_RESULT "$testname" "$result" "$logfile" "$score" \ + || rlLogError "rlReport: Failed to report the result" + fi +} + +__INTERNAL_version_cmp() { + if [[ "$1" == "$2" ]]; then + return 0 + fi + local i ver1="$1" ver2="$2" type="${3:--}" + + ver1=($(echo "$ver1" | tr "$type" ' ')) ver2=($(echo "$ver2" | tr "$type" ' ')) + # fill empty fields in ver1 with zeros + for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do + ver1[i]=0 + done + for ((i=0; i<${#ver1[@]}; i++)); do + if [[ -z "${ver2[i]}" ]]; then + # fill empty fields in ver2 with zeros + ver2[i]=0 + fi + case $type in + -) + __INTERNAL_version_cmp "${ver1[i]}" "${ver2[i]}" _ || return $? + ;; + _) + __INTERNAL_version_cmp "${ver1[i]}" "${ver2[i]}" . || return $? + ;; + .) + if ((36#${ver1[i]} > 36#${ver2[i]})); then + return 1 + fi + if ((36#${ver1[i]} < 36#${ver2[i]})); then + return 2 + fi + ;; + esac + done + return 0 +}; # end of __INTERNAL_version_cmp + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlCmpVersion +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlCmpVersion + +Compare two given versions composed by numbers and letters divided by dot (.), +underscore (_), or dash (-). + + rlCmpVersion ver1 ver2 + +If ver1 = ver2, sign '=' is printed to stdout and 0 is returned. +If ver1 > ver2, sign '>' is printed to stdout and 1 is returned. +If ver1 < ver2, sign '<' is printed to stdout and 2 is returned. + +=cut + +rlCmpVersion() { + __INTERNAL_version_cmp "$1" "$2" + local res=$? + case $res in + 0) + echo '=' + ;; + 1) + echo '>' + ;; + 2) + echo '<' + ;; + *) + echo "!" + esac + return $res +}; # end of rlCmpVersion + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlTestVersion +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlTestVersion + +Test releation between two given versions based on given operator. + + rlTestVersion ver1 op ver2 + +=over + +=item op + +Operator definitng the logical expression. +It can be '=', '==', '!=', '<', '<=', '=<', '>', '>=', or '=>'. + +=back + +Returns 0 if the expresison ver1 op ver2 is true; 1 if the expression is false +and 2 if something went wrong. + +=cut + +rlTestVersion() { + [[ " = == != < <= =< > >= => " =~ \ $2\ ]] || return 2 + local res=$(rlCmpVersion $1 $3) + if [[ "$2" == "!=" ]]; then + if [[ "$res" == "=" ]]; then + return 1 + else + return 0 + fi + elif [[ "$2" =~ $res ]]; then + return 0 + else + return 1 + fi +}; # end of rlTestVersion + +__INTERNAL_rlIsDistro(){ + local distro="$(beakerlib-lsb_release -ds)" + local whole="$(beakerlib-lsb_release -rs)" + local major="$(beakerlib-lsb_release -rs | cut -d '.' -f 1)" + + rlLogDebug "distro='$distro'" + rlLogDebug "major='$major'" + rlLogDebug "whole='$whole'" + + echo $distro | grep -q "$1" || return 1 + shift + + [[ -z "$1" ]] && return 0 + + local arg sign res + for arg in "$@" + do + rlLogDebug "arg='$arg'" + # sanity check - version needs to consist of numbers/dots/<=> + [[ "$arg" =~ ^([\<\>\!]?=?)([0-9][0-9\.]*)$ ]] || { + rlLogError "unexpected argument format '$arg'" + return 1 + } + + sign="${BASH_REMATCH[1]}" + arg="${BASH_REMATCH[2]}" + rlLogDebug "sign='$sign'" + rlLogDebug "arg='$arg'" + if [[ -z "$sign" ]]; then + # shorten whole version so it matches arg in dots count + local whole_shorten="$(echo "$whole" | sed -r "s/([^.]+(\.[^.]+){$(echo "$arg" | grep -oF . | wc -w)}).*/\1/")" + rlLogDebug "whole_shorten='$whole_shorten'" + if [[ "$whole_shorten" == "$arg" ]] + then + return 0 + fi + else + if [[ "$arg" =~ [.] ]]; then + rlLogDebug 'evaluation whole version (including minor)' + rlLogDebug "executing rlTestVersion \"$whole\" \"$sign\" \"$arg\"" + rlTestVersion "$whole" "$sign" "$arg" + else + rlLogDebug 'evaluation major version part only' + rlLogDebug "executing rlTestVersion \"$major\" \"$sign\" \"$arg\"" + rlTestVersion "$major" "$sign" "$arg" + fi + res=$? + rlLogDebug "result of rlTestVersion is '$res'" + return $res + fi + done + return 1 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlIsRHEL +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head2 Release Info + +=head3 rlIsRHEL + +Check whether we're running on RHEL. +With given number of version as parameter returns 0 if the particular +RHEL version is running. Multiple arguments can be passed separated +with space as well as any particular release (5.1 5.2 5.3). +Each version can have a prefix consisting of '<', '<=', '=', '>=', '>', +matching whenever the currently installed version is lesser, lesser or equal, +equal, equal or greater, greater than the version specified as argument. +Note that ie. '=5' (unlike just '5') matches exactly 5 (5.0), +not 5.N, where N > 0. + + rlIsRHEL + +Returns 0 if we are running on RHEL. + + rlIsRHEL 4.8 5 + +Returns 0 if we are running RHEL 4.8 or any RHEL 5. + +=cut +#' + +rlIsRHEL(){ + __INTERNAL_rlIsDistro "Red Hat Enterprise Linux" "$@" \ + || __INTERNAL_rlIsDistro "Red Hat Desktop release" "$@" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlIsFedora +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head3 rlIsFedora + +Check whether we're running on Fedora. +With given number of version as parameter returns 0 if the particular Fedora +version is running. +Range matching can be used in the form used by rlIsRHEL. + + rlIsFedora + +Returns 0 if we are running on Fedora. + + rlIsFedora 9 10 + +Returns 0 if we are running Fedora 9 or 10. + +=cut +#' + +rlIsFedora(){ + __INTERNAL_rlIsDistro "Fedora" "$@" +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlIsCentOS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<=cut +=pod + +=head2 Release Info + +=head3 rlIsCentOS + +Check whether we're running on CentOS. +With given number of version as parameter returns 0 if the particular +CentOS version is running. Multiple arguments can be passed separated +with space as well as any particular release (5.1 5.2 5.3). +Each version can have a prefix consisting of '<', '<=', '=', '>=', '>', +matching whenever the currently installed version is lesser, lesser or equal, +equal, equal or greater, greater than the version specified as argument. +Note that ie. '=5' (unlike just '5') matches exactly 5 (5.0), +not 5.N, where N > 0. + + rlIsCentOS + +Returns 0 if we are running on CentOS. + + rlIsCentOS 7.1 6 + +Returns 0 if we are running CentOS 7.1 or any CentOS 6. + +=cut +#' + +rlIsCentOS(){ + __INTERNAL_rlIsDistro "CentOS" "$@" +} + +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Ondrej Hudlicky + +=item * + +Petr Muller + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=item * + +Ales Zelinka + +=back + +=cut diff --git a/src/vim/ftdetect/beakerlib.vim b/src/vim/ftdetect/beakerlib.vim new file mode 100644 index 0000000..cc10ba4 --- /dev/null +++ b/src/vim/ftdetect/beakerlib.vim @@ -0,0 +1,15 @@ + +"" temporary disable auto-load +"au BufNewFile,BufRead *.sh call s:FTbeakerlib() + +function! s:FTbeakerlib() + let s:lnum = 1 + while s:lnum < 100 && s:lnum < line("$") + if getline(s:lnum) =~ '^.*\(rlJournalStart\|rlPhaseStart\|rlRun\|rlLog\)' + set filetype=beakerlib + break + endif + let s:lnum += 1 + endwhile + unlet s:lnum +endfunction diff --git a/src/vim/syntax/beakerlib.vim b/src/vim/syntax/beakerlib.vim new file mode 100644 index 0000000..19474f2 --- /dev/null +++ b/src/vim/syntax/beakerlib.vim @@ -0,0 +1,80 @@ +" Vim syntax file +" Application: BeakerLib +" Maintainer: Jakub Prokeš +" Latest Revision: 13 Oct 2015 + +if exists("b:current_syntax") + finish +endif + +runtime! syntax/sh.vim + +" Define keywords +syn keyword blJournalKeyword rlJournalStart rlJournalEnd rlJournalPrint rlJournalPrintText nextgroup=syntaxElement2 rlGetPhaseState rlGetTestState +syn keyword blPhasesKeyword rlPhaseStart rlPhaseEnd rlPhaseStartSetup rlPhaseStartTest rlPhaseStartCleanup +syn keyword blLoggingKeyword rlLog rlLogDebug rlLogInfo rlLogWarning rlLogError rlLogFatal rlDie rlBundleLogs rlFileSubmit +syn keyword blMainKeyword rlWatchdog rlReport rlCmpVersion rlTestVersion +syn keyword blBackupKeyword rlFileBackup rlFileRestore +syn keyword blAssertKeyword rlAssert0 rlAssertEquals rlAssertNotEquals rlAssertGreater rlAssertGreaterOrEqual rlAssertExists rlAssertNotExists rlAssertGrep rlAssertNotGrep rlAssertDiffer rlAssertNotDiffer rlFail rlPass rlAssertLesser rlAssertLesserOrEqual rlAssertRequired +syn keyword blServicesKeyword rlServiceStart rlServiceStop rlServiceRestore rlServiceDisable rlServiceEnable +syn keyword blrpmKeyword rlCheckRpm rlAssertRpm rlAssertNotRpm rlAssertBinaryOrigin rlCheckMakefileRequires rlCheckRequirements rlGetMakefileRequires rlFetchSrcForInstalled rlRpmDownload rlRpmInstall +syn keyword blMountKeyword rlMount rlCheckMount rlAssertMount rlAnyMounted rlHash rlUnhash +syn keyword blInfoKeyword rlShowPackageVersion rlGetArch rlGetDistroRelease rlGetDistroVariant rlShowRunningKernel rlGetPrimaryArch rlGetSecondaryArch +syn keyword blMetricKeyword rlLogMetricLow rlLogMetricHigh +syn keyword blTimeKeyword rlPerfTime_RunsInTime rlPerfTime_AvgFromRuns +syn keyword blXserverKeyword rlVirtualXStart rlVirtualXGetDisplay rlVirtualXStop rlVirtXGetCorrectID rlVirtXGetPid rlVirtXStartDisplay +syn keyword blCleanupKeyword rlCleanupAppend rlCleanupPrepend +syn keyword blAnalyzeKeyword rlDejaSum rlImport +syn keyword blReleaseKeyword rlIsFedora rlIsRHEL rlIsCentOS +syn keyword blSELINUXKeyword rlSEBooleanOff rlSEBooleanOn rlSEBooleanRestore +syn keyword blPSyncKeyword rlWait rlWaitForCmd rlWaitForFile rlWaitForSocket rlSocketRestore rlSocketStart rlSocketStop + +syn cluster blAll contains=blJournalKeyword,blPhasesKeyword,blLoggingKeyword,blMainKeyword,blBackupKeyword,blAssertKeyword,blServicesKeyword,blrpmKeyword,blMountKeyword,blInfoKeyword,blMetricKeyword,blTimeKeyword,blXserverKeyword,blCleanupKeyword,blAnalyzeKeyword,blReleaseKeyword,blSELINUXKeyword,blPSyncKeyword + +" highlight BeakerLib kyewords in loops,if,case and function blocks too +syn cluster shLoopList add=@blAll,blrlRun +syn cluster shFunctionList add=@blAll,blrlRun +syn cluster shCaseEsacList add=@blAll,blrlRun +syn cluster shCaseList add=@blAll,blrlRun + +" highlight Journal block +syn region blJournal matchgroup=blJournalKeyword start=/rlJournalStart/ end=/rlJournalEnd/ transparent + +" highlight Phases block +syn region blPhases matchgroup=blPhasesKeyword start=/rlPhaseStart\(Setup\|Test\|Cleanup\)\?/ end=/rlPhaseEnd/ nextgroup=blPhasesType skipwhite transparent +syn match blPhasesType /\(FAIL\|WARN\)/ + +if exists("bl_rlRun_sub") + " highlight first argument of rlRun as sub-command + syn match blrlRun /rlRun/ nextgroup=blrlRunArgs skipwhite + syn match blrlRunArgs /-t\|-l\|-c\|-s\|[^"\\]\+/ nextgroup=blCommandSub skipwhite contained + syn region blCommandSub matchgroup=shCmdSubRegion start=/"/ skip='\\\\\|\\.' end=/"/ contained contains=@shCommandSubList,@blAll +else + syn keyword blAssertKeyword rlRun rlAssert0 rlAssertEquals rlAssertNotEquals rlAssertGreater rlAssertGreaterOrEqual rlAssertExists rlAssertNotExists rlAssertGrep rlAssertNotGrep rlAssertDiffer rlAssertNotDiffer rlFail rlPass +endif + + + +hi def link blCommandSub blPlain +hi def link blJournalKeyword blStatement +hi def link blJournal blPlain +hi def link blPhasesKeyword blStatement +hi def link blPhases blPlain +hi def link blrlRunArgs blrlRun +hi def link blrlRun blIdentifier +hi def link blPhasesType blType +hi def link blLoggingKeyword blIdentifier +hi def link blMainKeyword blIdentifier +hi def link blBackupKeyword blIdentifier +hi def link blAssertKeyword blIdentifier +hi def link blServicesKeyword blIdentifier +hi def link blrpmKeyword blIdentifier +hi def link blMountKeyword blIdentifier +hi def link blInfoKeyword blIdentifier +hi def link blMetricKeyword blIdentifier +hi def link blTimeKeyword blIdentifier +hi def link blXserverKeyword blIdentifier +hi def link blType Type +hi def link blPlain plain +hi def link blIdentifier Function +hi def link blStatement Statement diff --git a/src/virtualX.sh b/src/virtualX.sh new file mode 100644 index 0000000..a347766 --- /dev/null +++ b/src/virtualX.sh @@ -0,0 +1,325 @@ +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Name: virtualX.sh - part of the BeakerLib project +# Description: Helpers for handling virtual X server +# +# Author: Jan Hutar +# Author: Petr Splichal +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Copyright (c) 2008-2010 Red Hat, Inc. All rights reserved. +# +# This copyrighted material is made available to anyone wishing +# to use, modify, copy, or redistribute it subject to the terms +# and conditions of the GNU General Public License version 2. +# +# 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. +# +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head1 NAME + +BeakerLib - virtualX - Helpers for handling virtual X server + +=head1 DESCRIPTION + +This module provides a simple way to start and stop virtual X server +(framebuffer). + +=head1 FUNCTIONS + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Internal Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# Files: +# +# /tmp/$Xid-pid - contains PID for X server we are running # no-reboot +# /tmp/$Xid-display - contains DISPLAY of our X server # no-reboot + +. $BEAKERLIB/testing.sh +. $BEAKERLIB/infrastructure.sh + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtXGetCorrectID +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Generate internal ID from provided unique string, used by other +# rlVirtX* functions. You probably do not need to call this +# function and can forget about its existence. +# +# usage: rlVirtXGetCorrectID some_unique_string +# +# * some_unique_string: unique identifier, you can use e.g. $TEST +# (non-alnum chars will be stripped) + +function rlVirtXGetCorrectID() { + echo "$1" | sed "s/[^[:alnum:]]//g" +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtXGetPid +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Return PID of virtual X server +# +# usage: rlVirtXGetPid string +# +# * string: ID (e.g. $TEST variable can be used) + +function rlVirtXGetPid() { + local Xid=$( rlVirtXGetCorrectID "$1" ) + if [ -f "/tmp/$Xid-pid" ]; then # no-reboot + cat "/tmp/$Xid-pid" # no-reboot + else + return 1 + fi +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtXStartDisplay +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Start a virtual X server on display "display" +# +# usage: rlVirtXStartDisplay string display +# +# * string: ID (e.g. $TEST variable can be used) +# * display: DISPLAY number (without ':') + +function rlVirtXStartDisplay() { + local Xid=$( rlVirtXGetCorrectID "$1" ) + local Xdisplay=$( echo $2 | sed "s/[^0-9]//g" ) + rlLogDebug "rlVirtXStartDisplay: Starting a virtual X ($Xid) server on :$Xdisplay" + + Xvfb :$Xdisplay -ac -screen 0 1600x1200x24 -fbdir /tmp & # no-reboot + local Xpid=$! + sleep 3 + if ! ps | grep $Xpid >/dev/null; then + rlLogDebug "rlVirtXStartDisplay: Virtual X failed to start" + return 1 + else + rlLogDebug "rlVirtXStartDisplay: Started with PID '$Xpid' on display :$Xdisplay" + echo "$Xpid" > /tmp/$Xid-pid # no-reboot + echo ":$Xdisplay" > /tmp/$Xid-display # no-reboot + return 0 + fi +} + + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Virtual X Server --- Public Stuff +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head2 Virtual X Server + +Functions providing simple way how to start and stop virtual X +server. + +=cut + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtualXStart +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +: <<'=cut' +=pod + +=head3 rlVirtualXStart + +Start a virtual X server on a first free display. Tries only first +N displays, so you can run out of them. + + rlVirtualXStart name + +=over + +=item name + +String identifying the X server. + +=back + +Returns 0 when the server is started successfully. + +=cut + +function rlVirtualXStart() { + local Xmax=3 + local Xid=$( rlVirtXGetCorrectID "$1" ) + local Xdisplay=0 + for Xdisplay in $( seq 1 $Xmax ); do + rlLogDebug "rlVirtualXStart: Trying to start on display :$Xdisplay" + if rlVirtXStartDisplay $Xid $Xdisplay; then + rlLogDebug "rlVirtualXStart: Started on display :$Xdisplay" + return 0 + fi + done + rlLogDebug "rlVirtualXStart: Was not able to start on displays from :1 to :Xmax" + return 1 +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtualXGetDisplay +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlVirtualXGetDisplay + +Get the DISPLAY variable for specified virtual X server. + + rlVirtualXGetDisplay name + +=over + +=item name + +String identifying the X server. + +=back + +Displays the number of display where specified virtual X server is +running to standard output. Returns 0 on success. + +=cut + +function rlVirtualXGetDisplay() { + local Xid=$( rlVirtXGetCorrectID "$1" ) + if [ -f "/tmp/$Xid-display" ]; then # no-reboot + cat "/tmp/$Xid-display" # no-reboot + else + return 1 + fi +} + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# rlVirtualXStop +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 rlVirtualXStop + +Kill the specified X server. + + rlVirtualXStop name + +=over + +=item name + +String identifying the X server. + +=back + +Returns 0 when the server is stopped successfully. + +=cut + +function rlVirtualXStop() { + local Xid=$( rlVirtXGetCorrectID "$1" ) + local Xpid=$( rlVirtXGetPid "$Xid" ) + local Xdisplay=$( rlVirtualXGetDisplay "$1" ) + if [ -z "$Xpid" ]; then + rlLogDebug "rlVirtualXStop: Provide pid you want to kill" + return 1 + fi + if ps | grep $Xpid >/dev/null; then + kill "$Xpid" + fi + sleep 2; # added by koca (some servers aren't so quick :)) + if ! ps | grep $Xpid >/dev/null; then + rlLogDebug "rlVirtualXStop: Normal 'kill $Xpid' succeed" + else + rlLogWarning "rlVirtualXStop: I had to 'kill -9 $Xpid' (rc: $?) X server" + kill -9 "$Xpid" + sleep 1 + if [ -r "/tmp/.X$Xdisplay-lock" ]; then # no-reboot + rlLogDebug "rlVirtualXStop: Lock file '/tmp/.X$Xdisplay-lock' still exists, last attempt" # no-reboot + kill $( cat "/tmp/.X$Xdisplay-lock" ) &>/dev/null # no-reboot + kill -9 $( cat "/tmp/.X$Xdisplay-lock" ) &>/dev/null # no-reboot + rm -f "/tmp/.X$Xdisplay-lock" # no-reboot + sleep 1 + fi + if ps | grep $Xpid >/dev/null; then + rlLogDebug "rlVirtualXStop: I was not able to kill pid '$Xpid', sorry" + return 2 + fi + fi + rm -rf /tmp/$Xid-display /tmp/$Xid-pid # no-reboot + sleep 1 # give it some time to end + return 0 +} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Example +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head3 Example + +Below is a simple example of usage. Note that a lot of usefull +debugging information is reported on the DEBUG level, so you can +run your test with C to get them. + + rlVirtualXStart $TEST + rlAssert0 "Virtual X server started" $? + export DISPLAY="$( rlVirtualXGetDisplay $TEST )" + # ...your test which needs X... + rlVirtualXStop $TEST + rlAssert0 "Virtual X server killed" $? + +These are "Requires" lines for your scripts - note different package +names for different RHEL versions: + + @echo "Requires: xorg-x11-server-Xvfb" >> $(METADATA) # RHEL-5 + @echo "Requires: xorg-x11-Xvfb" >> $(METADATA) # RHEL-4 + @echo "Requires: XFree86-Xvfb" >> $(METADATA) # RHEL-3 + +=cut + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUTHORS +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +: <<'=cut' +=pod + +=head1 AUTHORS + +=over + +=item * + +Jan Hutar + +=item * + +Petr Splichal + +=back + +=cut diff --git a/src/xslt-templates/xunit.xsl b/src/xslt-templates/xunit.xsl new file mode 100644 index 0000000..49510c3 --- /dev/null +++ b/src/xslt-templates/xunit.xsl @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/upstream-release.sh b/upstream-release.sh new file mode 100644 index 0000000..4660ccf --- /dev/null +++ b/upstream-release.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +UPLOAD_URL="ssh://fedorahosted.org/beakerlib" + +doOrDie(){ + local MESSAGE="$1" + local COMMAND="$2" + local STDOUT="$(mktemp)" # no-reboot + local STDERR="$(mktemp)" # no-reboot + + echo -n "$MESSAGE: " + + if eval "$COMMAND" >"$STDOUT" 2>"$STDERR" + then + echo "PASS" + rm -f "$STDOUT" "$STDERR" + else + echo "FAIL" + echo "=== STDOUT ===" + cat "$STDOUT" + echo "=== STDERR ===" + cat "$STDERR" + rm -f "$STDOUT" "$STDERR" + exit 1 + fi + + return 0 +} + +checkOrDie(){ + if ! eval "$1" + then + echo -e "$2" + exit 1 + fi +} + +experimental(){ + local CHECKTAG="$1" + local BRANCH="$2" + + checkOrDie "echo '$BRANCH' | grep -q -v master" "Experimental release should not be done from master branch" + doOrDie "Creating an archive" "git archive --prefix=${CHECKTAG}/ -o ${CHECKTAG}.tar.gz HEAD" +} + +checkTag() { + local CHECKTAG="$1" + + if git tag | grep -q -w $CHECKTAG + then + echo "Tag $CHECKTAG already exists: update VERSION accordingly" + exit 1 + else + echo "Tag $CHECKTAG does not exist: proceeding further" + fi +} + +testing(){ + local CHECKTAG="$1" + local BRANCH="$2" + + checkOrDie "echo '$CHECKTAG' | grep -q '\.99'" "Version for testing should contain .99 substring\nGot: $CHECKTAG" + checkOrDie "echo '$BRANCH' | grep -q master" "Testing release should be done from master branch\nGot: $BRANCH" + + doOrDie "Pulling" "git pull" + + checkTag "$CHECKTAG" + + doOrDie "Creating an archive" "git archive --prefix=$CHECKTAG/ -o $CHECKTAG.tar.gz HEAD" + doOrDie "Tagging commit as $CHECKTAG" "git tag $CHECKTAG" + doOrDie "Pushing tags out there" "git push --tags" +} + +upstream(){ + local CHECKTAG="$1" + local BRANCH="$2" + + checkOrDie "echo '$CHECKTAG' | grep -q -v '\.99'" "Version for testing should not contain .99 substring\nGot: $CHECKTAG" + checkOrDie "echo '$BRANCH' | grep -q master" "Testing release should be done from master branch\nGot: $BRANCH" + + doOrDie "Pulling" "git pull" + + checkTag "$CHECKTAG" + + doOrDie "Creating an archive" "git archive --prefix=$CHECKTAG/ -o $CHECKTAG.tar.gz HEAD" + # TODO: update the main page with new version + # TODO: create release notes and put it online + doOrDie "Attempting to publish the tarball" "scp $CHECKTAG.tar.gz fedorahosted.org:beakerlib" + doOrDie "Tagging commit as $CHECKTAG" "git tag $CHECKTAG" + doOrDie "Pushing tags out there" "git push --tags" + rm -f "$CHECKTAG.tar.gz" +} + +CHECKTAG="$1" +RELEASE="$2" +BRANCH="$( git rev-parse --abbrev-ref HEAD)" + +case "$RELEASE" in + "experimental") + experimental "$CHECKTAG" "$BRANCH" + ;; + "testing") + testing "$CHECKTAG" "$BRANCH" + ;; + *) + upstream "$CHECKTAG" "$BRANCH" + ;; +esac