summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pactest/COPYING340
-rw-r--r--pactest/ChangeLog29
-rw-r--r--pactest/README304
-rw-r--r--pactest/TODO8
-rwxr-xr-xpactest/pactest.py73
-rwxr-xr-xpactest/pmdb.py363
-rwxr-xr-xpactest/pmenv.py118
-rwxr-xr-xpactest/pmfile.py65
-rwxr-xr-xpactest/pmpkg.py180
-rwxr-xr-xpactest/pmrule.py133
-rwxr-xr-xpactest/pmtest.py253
-rw-r--r--pactest/tests/TESTS61
-rw-r--r--pactest/tests/add001.py13
-rw-r--r--pactest/tests/add002.py18
-rw-r--r--pactest/tests/add003.py23
-rw-r--r--pactest/tests/add004.py12
-rw-r--r--pactest/tests/add010.py15
-rw-r--r--pactest/tests/add011.py14
-rw-r--r--pactest/tests/add012.py20
-rw-r--r--pactest/tests/add013.py23
-rw-r--r--pactest/tests/add020.py15
-rw-r--r--pactest/tests/add021.py16
-rw-r--r--pactest/tests/add030.py18
-rw-r--r--pactest/tests/add031.py18
-rw-r--r--pactest/tests/add032.py18
-rw-r--r--pactest/tests/add040.py14
-rw-r--r--pactest/tests/add041.py15
-rw-r--r--pactest/tests/add042.py29
-rw-r--r--pactest/tests/add050.py16
-rw-r--r--pactest/tests/add060.py15
-rw-r--r--pactest/tests/dummy001.py19
-rw-r--r--pactest/tests/query001.py9
-rw-r--r--pactest/tests/remove010.py12
-rw-r--r--pactest/tests/remove011.py12
-rw-r--r--pactest/tests/remove020.py12
-rw-r--r--pactest/tests/remove021.py12
-rw-r--r--pactest/tests/remove030.py12
-rw-r--r--pactest/tests/smoke001.py19
-rw-r--r--pactest/tests/sync001.py12
-rw-r--r--pactest/tests/sync002.py17
-rw-r--r--pactest/tests/sync003.py12
-rw-r--r--pactest/tests/sync009.py12
-rw-r--r--pactest/tests/sync010.py26
-rw-r--r--pactest/tests/sync020.py19
-rw-r--r--pactest/tests/sync021.py21
-rw-r--r--pactest/tests/sync040.py15
-rw-r--r--pactest/tests/sync041.py16
-rw-r--r--pactest/tests/sync042.py14
-rw-r--r--pactest/tests/sync043.py14
-rw-r--r--pactest/tests/sync050.py10
-rw-r--r--pactest/tests/sync100.py12
-rw-r--r--pactest/tests/sync101.py12
-rw-r--r--pactest/tests/sync102.py12
-rw-r--r--pactest/tests/sync103.py14
-rw-r--r--pactest/tests/sync110.py22
-rw-r--r--pactest/tests/sync120.py21
-rw-r--r--pactest/tests/sync130.py16
-rw-r--r--pactest/tests/sync131.py19
-rw-r--r--pactest/tests/sync132.py18
-rw-r--r--pactest/tests/sync133.py18
-rw-r--r--pactest/tests/sync134.py21
-rw-r--r--pactest/tests/sync135.py31
-rw-r--r--pactest/tests/sync200.py15
-rw-r--r--pactest/tests/sync890.py20
-rw-r--r--pactest/tests/sync891.py22
-rw-r--r--pactest/tests/sync892.py24
-rw-r--r--pactest/tests/sync893.py20
-rw-r--r--pactest/tests/sync897.py26
-rw-r--r--pactest/tests/sync898.py18
-rw-r--r--pactest/tests/sync899.py18
-rw-r--r--pactest/tests/sync990.py20
-rw-r--r--pactest/tests/sync992.py23
-rw-r--r--pactest/tests/sync999.py21
-rw-r--r--pactest/tests/upgrade001.py18
-rw-r--r--pactest/tests/upgrade002.py18
-rw-r--r--pactest/tests/upgrade003.py18
-rw-r--r--pactest/tests/upgrade004.py12
-rw-r--r--pactest/tests/upgrade010.py17
-rw-r--r--pactest/tests/upgrade020.py17
-rw-r--r--pactest/tests/upgrade021.py17
-rw-r--r--pactest/tests/upgrade022.py17
-rw-r--r--pactest/tests/upgrade030.py22
-rw-r--r--pactest/tests/upgrade040.py25
-rwxr-xr-xpactest/util.py259
84 files changed, 3417 insertions, 0 deletions
diff --git a/pactest/COPYING b/pactest/COPYING
new file mode 100644
index 00000000..96e45911
--- /dev/null
+++ b/pactest/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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
+
+ Appendix: 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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.
+
+ <signature of Ty Coon>, 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/pactest/ChangeLog b/pactest/ChangeLog
new file mode 100644
index 00000000..fec0eb23
--- /dev/null
+++ b/pactest/ChangeLog
@@ -0,0 +1,29 @@
+
+Release 0.3 (pending)
+-------------
+- added detection for fakeroot enabled pacman binaries and leftover lock
+and core files.
+- fixed number format for statistics line
+- display elapsed times for each test cases
+- added COPYING file
+- added patch to disable fakeroot support for pacman 2.9.x
+- added "--gdb" command line options to ease debugging (as suggested by VMiklos)
+- added "--valgrind" option to run tests through valgrind memory checker
+- added more test cases
+- added gensync archives
+
+Release 0.2 (06/02/13)
+-----------
+- added support for directories, symlinks and altered files
+- removed hardcoded references to package names in testcase scripts
+- splited pactest.py in several modules
+- lots of code optimizations
+- created a home page to host the project
+- added README, TODO and ChangeLog files
+
+Release 0.1 (06/01/30)
+-----------
+Initial release.
+
+Annoucement on the ArchLinux pacman-dev mailing list:
+http://www.archlinux.org/pipermail/pacman-dev/2006-January/000186.html
diff --git a/pactest/README b/pactest/README
new file mode 100644
index 00000000..7ef24b1d
--- /dev/null
+++ b/pactest/README
@@ -0,0 +1,304 @@
+README
+======
+
+pactest is a test suite for the ArchLinux package manager: pacman.
+
+It has a rather high level view of operations performed by pacman: it
+automatically creates a test environment based on a test case file
+description, the run pacman, and finally check the results of test according
+to a set of rules defined in the test case.
+
+It is written in Python and makes available most of what can be found in
+pacman's code to create ArchLinux packages or read and write databases entries.
+
+Each test case is defined in a separate file that is sourced in order to set
+the environment.
+
+pactest creates the environment in the subdirectory "root" created in the
+current directory.
+The following directory structure is used:
+ - var/lib/pacman: databases path (local and sync ones)
+ - etc/pacman.conf for pacman configuration file
+ - var/cache/pkg: sync packages cache
+ - var/log/pactest.log: log file
+ - var/pub: location for pseudo sync repositories
+ - tmp: hold all local package archives (to be used with pacman -A or -U)
+
+Note: the logfile is used to capture all pacman outputs.
+
+Test case example:
+ self.description = "Install a package"
+
+ p = pmpkg("dummy", "1.0-3")
+ p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+ self.addpkg(p)
+
+ self.args = "-A dummy-1.0-1.pkg.tar.gz"
+
+ self.addrule("PACMAN_RETCODE=0")
+ self.addrule("PKG_EXIST=dummy")
+ for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
+
+Basically, the above test case will try to install a package (dummy-1.0-3),
+including two files, from a local archive, by calling "pacman -A"
+Upon completion, it checks that:
+ - pacman returned no error code,
+ - a "dummy" entry exists in the "local" database
+ - all files from the package exist in the filesystem.
+
+
+Installation
+============
+
+Simply extract the pactest tarball, jump into the newly created directory and
+run pactest.py. See the usage section below.
+
+Remark: pacman 3.x restrictions regarding fakeroot must be disabled.
+It can be done by configuring pacman with the --disable-fakeroot flag:
+ ./configure --disable-fakeroot
+
+For pacman 2.9.x releases, apply the patch found in the patches directory,
+then export CFLAGS as following before rebuilding pacman:
+ export CFLAGS=-DNOFAKEROOT
+
+
+Usage
+=====
+
+pactest will run the suite of tests defined by the "--test" parameter.
+
+Example:
+ ./pactest.py --test=test/*
+
+This example will run tests from the "test" directory.
+Note: several "--test" options can be passed to pactest.
+
+Use the ""help" option to get the full list of parameters:
+ ./pactest.py --help
+
+
+Parameters
+==========
+
+The test environment is described by the following basic parameters:
+
+ description
+ -----------
+
+A short string describing the aim of the test case. It is displayed on the
+standard output during test execution.
+
+ args
+ ----
+
+A string of arguments that are passed to the pacman binary when the test is
+run.
+
+Example:
+ self.args = "-S dummy"
+
+ option
+ ------
+
+A dictionnary that holds the data used in the pacman configuration file.
+It has 3 keys, each one of them pointing at a list of strings:
+ - noupgrade
+ - noextract
+ - ignorepkg
+
+Examples:
+ self.option["noupgrade"] = ["etc/X11/xorg.conf",
+ "etc/pacman.conf"]
+ self.option["noextract"] = ["etc/lilo.conf"]
+
+ filesystem
+ ----------
+
+A list of strings describing a set of files supposed to exist in the filesystem
+when the test case is run.
+Upon test startup, pactest will automatically populate the test environment
+filesystem with this list of files.
+
+Example:
+ self.filesystem = ["bin/dummy",
+ "etc/X11/xorg.conf.pacsave"]
+
+Note that all paths are relative ones, and thus file names should not start
+with a "/".
+
+
+Packages
+========
+
+The test case file description shall define a number of packages that can be
+used to either populate a database, or to feed pacman with data needed during
+its execution.
+
+This can be achieved by creating pmpkg objects, with the following constructor:
+ pmpkg(name, version)
+
+Both "name" and "version" are strings. Also, note that if not provided, the
+version defaults to "1.0-1".
+
+Example:
+ pkg1 = pmpkg("dummy", "2.1-1")
+ pkg2 = pmpkg("foobar")
+
+All fields from a ArchLinux package can be set and modified directly with no
+methods to access them.
+Note: some fields are automatically set by pactest and should preferably not
+be modified by hand (i.e. "md5sum", "size", or "csize").
+
+Examples:
+ pkg.depends = ["pkg2", "pkg3>=2.0"]
+ pkg.files = ["bin/dummy", "etc/dummy.conf", "usr/man/man1/dummy.1"]
+
+
+Databases
+=========
+
+The test environment provides a way to create and fill databases (local or
+sync ones).
+
+The following methods shall be used:
+
+ * addpkg2db(database, package)
+
+Notes: "database" is a string, and "package" shall be a previously created
+pmpkg object.
+
+Examples:
+ self.addpkg2db("local", lpkg)
+ self.addpkg2db("sync1", spkg11)
+ self.addpkg2db("sync1", spkg12)
+ self.addpkg2db("sync2", spkg21)
+
+Note: there is no need to explicitly create a database. The "local" one
+already exists (even if empty), and sync databases are created on the fly when
+a new database new is given.
+
+ * addpkg(package)
+
+package is an existing pmpkg object.
+It creates a package archive based on the given object. The resulting archive
+is located in the temporary directory of the test environment, ready to be
+supplied to pacman for test purposes.
+
+
+Files
+=====
+
+All files created by pactest are filled with a content defaulting to the file
+name, with an additional line feed.
+For instance, the content of a file "bin/dummy" created in the test environment
+file system is: "bin/dummy\n".
+
+It is possible to create directories by appending a slash "/" to the name and
+to create symlinks by appending an arrow followed by a filename " -> target".
+
+Note: only relative symlinks are supported.
+
+Example:
+ pkg = pmpkg("dummy")
+ pkg.files = ["bin/dummy",
+ "usr/local/",
+ "lib/libfoo.so.O",
+ "lib/libfoo.so -> ./libfoo.so.0"]
+
+In this example, "usr/local/" is a directory, and "libfoo.so" will be a
+symlink pointing at "libfoo.so.0". It is usually a good idea to also define
+the target of the symlink!
+
+It can be interesting for some tests to create altered files. This can be
+done by appending one or more asterisks "*" to the file name.
+
+Example:
+ lpkg = pmpkg("dummy")
+ lpkg.files = ["bin/dummy"]
+ self.addpkg2db("local", lpkg)
+
+ newpkg = pmpkg("dummy", "1.0-2")
+ newpkg.files = ["bin/dummy*"]
+ self.addpkg(newpkg)
+
+ self.args = "-U dummy-1.0-2.pkg.tar.gz"
+
+In this case, package "lpkg" will install a file "bin/dummy" with "bin/dummy\n"
+as its content. Upon package upgrade, newpkg will provide a file named
+"bin/dummy" with "bin/dummy*\n" as its content.
+This is useful to simulate that a file has been modified between two different
+releases of a same package.
+
+The same also applies to files from the "filesystem" parameter of the test
+environment, and to the "backup" attribute of a package object.
+
+
+Rules
+=====
+
+Finally, to check test success or failure, one shall define a set of rules.
+
+ addrule(rule)
+ -------------
+
+A rule is a string composed by a key and an item, joined with a "=" symbol.
+
+Examples:
+ self.addrule("PACMAN_RETCODE=0")
+ self.addrule("PKG_EXIST=dummy")
+ self.addrule("FILE_MODIFIED=bin/dummy")
+ self.addrule("PKG_DEPENDS=xorg|fontconfig")
+
+Note: an item can be divided into two arguments, as shown in the latter
+example.
+
+All rules can be prepended with a bang "!" in order to tell pactest to expect
+the exact opposite result.
+
+Example:
+ self.addrule("!FILE_MODIFIED=bin/dummy")
+
+Finally, the following rules are supported:
+
+ . PACMAN rules
+
+Possible rules are:
+
+ PACMAN_RETCODE=value
+ PACMAN_OUTPUT=value
+
+For the RETCODE one, pactest will compare pacman return code with the value
+provided as an item.
+For the OUTPUT one, pactest will grep pacman outputs for the given value.
+
+Note: PACMAN_OUTPUT should not be used. Pacman outputs are likely to change
+from one release to another, so that it's reliability is quite low.
+
+ . PKG rules
+
+For each rule, pactest will read the entry "name" from the local database and
+challenge the requested data with it.
+
+Possible rules are:
+
+ PKG_EXIST=name
+ PKG_MODIFIED=name
+ PKG_VERSION=name|version
+ PKG_DEPENDS=name|depname
+ PKG_REQUIREDBY=name|reqbyname
+
+Example:
+ PKG_DEPENDS=ncurses|glibc
+
+pactest will test the local database entry "ncurses" has "glibc" in its
+DEPENDS field.
+
+ . FILE rules
+
+ FILE_EXIST=path/to/file
+ FILE_MODIFIED=path/to/file
+ FILE_PACNEW=path/to/file
+ FILE_PACSAVE=path/to/file
+ FILE_PACORIG=path/to/file
diff --git a/pactest/TODO b/pactest/TODO
new file mode 100644
index 00000000..437bf02c
--- /dev/null
+++ b/pactest/TODO
@@ -0,0 +1,8 @@
+TODO
+====
+
+Features:
+- implement gensync support
+
+Tests:
+- add test cases for pacman -D and pacman -T
diff --git a/pactest/pactest.py b/pactest/pactest.py
new file mode 100755
index 00000000..2f7358ac
--- /dev/null
+++ b/pactest/pactest.py
@@ -0,0 +1,73 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import getopt
+import sys
+import glob
+import os
+
+import pmenv
+import util
+
+
+__author__ = "Aurelien FORET"
+__version__ = "0.3"
+
+
+def usage(retcode):
+ """
+ """
+ print "Usage: %s [options] [[--test=<path/to/testfile.py>] ...]\n\n" % __file__
+ sys.exit(retcode)
+
+if __name__ == "__main__":
+ env = pmenv.pmenv()
+ testcases = []
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:],
+ "d:hp:t:v",
+ ["debug=", "gdb", "help", "pacman=", "test=", "valgrind", "verbose", "nolog"])
+ except getopt.GetoptError:
+ usage(1)
+
+ for (cmd, param) in opts:
+ if cmd == "-v" or cmd == "--verbose":
+ util.verbose += 1
+ elif cmd == "-d" or cmd == "--debug":
+ env.pacman["debug"] = int(param)
+ elif cmd == "-t" or cmd == "--test":
+ testcases.extend(glob.glob(param))
+ elif cmd == "-p" or cmd == "--pacman":
+ env.pacman["bin"] = os.path.abspath(param)
+ elif cmd == "-h" or cmd == "--help":
+ usage(0)
+ elif cmd == "--nolog":
+ env.pacman["nolog"] = 1
+ elif cmd == "--gdb":
+ env.pacman["gdb"] = 1
+ elif cmd == "--valgrind":
+ env.pacman["valgrind"] = 1
+
+ for i in testcases:
+ env.addtest(i)
+
+ env.run()
+ env.results()
diff --git a/pactest/pmdb.py b/pactest/pmdb.py
new file mode 100755
index 00000000..ebac9372
--- /dev/null
+++ b/pactest/pmdb.py
@@ -0,0 +1,363 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import os
+import tempfile
+import shutil
+
+import pmpkg
+from util import *
+
+
+def _mkfilelist(files):
+ """Generate a list of files from the list supplied as an argument.
+
+ Each path is decomposed to generate the list of all directories leading
+ to the file.
+
+ Example with 'usr/local/bin/dummy':
+ The resulting list will be
+ usr/
+ usr/local/
+ usr/local/bin/
+ usr/local/bin/dummy
+ """
+ i = []
+ for f in files:
+ dir = getfilename(f)
+ i.append(dir)
+ while "/" in dir:
+ [dir, tmp] = dir.rsplit("/", 1)
+ if not dir + "/" in files:
+ i.append(dir + "/")
+ i.sort()
+ return i
+
+def _mkbackuplist(backup):
+ """
+ """
+ return ["%s\t%s" % (getfilename(i), mkmd5sum(i)) for i in backup]
+
+def _getsection(fd):
+ """
+ """
+ i = []
+ while 1:
+ line = fd.readline().strip("\n")
+ if not line:
+ break
+ i.append(line)
+ return i
+
+def _mksection(title, data):
+ """
+ """
+ s = ""
+ if isinstance(data, list):
+ s = "\n".join(data)
+ else:
+ s = data
+ return "%%%s%%\n" \
+ "%s\n" % (title, s)
+
+
+class pmdb:
+ """Database object
+ """
+
+ def __init__(self, treename, dbdir):
+ self.treename = treename
+ self.dbdir = dbdir
+ self.pkgs = []
+
+ def __str__(self):
+ return "%s" % self.treename
+
+ def getpkg(self, name):
+ """
+ """
+ for pkg in self.pkgs:
+ if name == pkg.name:
+ return pkg
+
+ def db_read(self, name):
+ """
+ """
+
+ path = os.path.join(self.dbdir, self.treename)
+ if not os.path.isdir(path):
+ return None
+
+ dbentry = ""
+ for roots, dirs, files in os.walk(path):
+ for i in dirs:
+ [pkgname, pkgver, pkgrel] = i.rsplit("-", 2)
+ if pkgname == name:
+ dbentry = i
+ break
+ if not dbentry:
+ return None
+ path = os.path.join(path, dbentry)
+
+ [pkgname, pkgver, pkgrel] = dbentry.rsplit("-", 2)
+ pkg = pmpkg.pmpkg(pkgname, pkgver + "-" + pkgrel)
+
+ # desc
+ filename = os.path.join(path, "desc")
+ fd = file(filename, "r")
+ while 1:
+ line = fd.readline()
+ if not line:
+ break
+ line = line.strip("\n")
+ if line == "%DESC%":
+ pkg.desc = fd.readline().strip("\n")
+ elif line == "%GROUPS%":
+ pkg.groups = _getsection(fd)
+ elif line == "%URL%":
+ pkg.url = fd.readline().strip("\n")
+ elif line == "%LICENSE%":
+ pkg.license = _getsection(fd)
+ elif line == "%ARCH%":
+ pkg.arch = fd.readline().strip("\n")
+ elif line == "%BUILDDATE%":
+ pkg.builddate = fd.readline().strip("\n")
+ elif line == "%INSTALLDATE%":
+ pkg.installdate = fd.readline().strip("\n")
+ elif line == "%PACKAGER%":
+ pkg.packager = fd.readline().strip("\n")
+ elif line == "%REASON%":
+ pkg.reason = int(fd.readline().strip("\n"))
+ elif line == "%SIZE%" or line == "%CSIZE%":
+ pkg.size = int(fd.readline().strip("\n"))
+ elif line == "%MD5SUM%":
+ pkg.md5sum = fd.readline().strip("\n")
+ elif line == "%REPLACES%":
+ pkg.replaces = _getsection(fd)
+ elif line == "%FORCE%":
+ fd.readline()
+ pkg.force = 1
+ fd.close()
+ pkg.checksum["desc"] = getmd5sum(filename)
+ pkg.mtime["desc"] = getmtime(filename)
+
+ # files
+ filename = os.path.join(path, "files")
+ fd = file(filename, "r")
+ while 1:
+ line = fd.readline()
+ if not line:
+ break
+ line = line.strip("\n")
+ if line == "%FILES%":
+ while line:
+ line = fd.readline().strip("\n")
+ if line and line[-1] != "/":
+ pkg.files.append(line)
+ if line == "%BACKUP%":
+ pkg.backup = _getsection(fd)
+ fd.close()
+ pkg.checksum["files"] = getmd5sum(filename)
+ pkg.mtime["files"] = getmtime(filename)
+
+ # depends
+ filename = os.path.join(path, "depends")
+ fd = file(filename, "r")
+ while 1:
+ line = fd.readline()
+ if not line:
+ break
+ line = line.strip("\n")
+ if line == "%DEPENDS%":
+ pkg.depends = _getsection(fd)
+ elif line == "%REQUIREDBY%":
+ pkg.requiredby = _getsection(fd)
+ elif line == "%CONFLICTS%":
+ pkg.conflicts = _getsection(fd)
+ elif line == "%PROVIDES%":
+ pkg.provides = _getsection(fd)
+ elif line == "%REPLACES%":
+ pkg.replaces = _getsection(fd)
+ elif line == "%FORCE%":
+ fd.readline()
+ pkg.force = 1
+ fd.close()
+ pkg.checksum["depends"] = getmd5sum(filename)
+ pkg.mtime["depends"] = getmtime(filename)
+
+ # install
+ filename = os.path.join(path, "install")
+ if os.path.isfile(filename):
+ pkg.checksum["install"] = getmd5sum(filename)
+ pkg.mtime["install"] = getmtime(filename)
+
+ return pkg
+
+ #
+ # db_write is used to add both 'local' and 'sync' db entries
+ #
+ def db_write(self, pkg):
+ """
+ """
+
+ path = os.path.join(self.dbdir, self.treename, pkg.fullname())
+ if not os.path.isdir(path):
+ os.makedirs(path);
+
+ # desc
+ # for local db entries: name, version, desc, groups, url, license,
+ # arch, builddate, installdate, packager,
+ # size, reason
+ # for sync entries: name, version, desc, groups, csize, md5sum,
+ # replaces, force
+ data = [_mksection("NAME", pkg.name)]
+ data.append(_mksection("VERSION", pkg.version))
+ if pkg.desc:
+ data.append(_mksection("DESC", pkg.desc))
+ if pkg.groups:
+ data.append(_mksection("GROUPS", pkg.groups))
+ if self.treename == "local":
+ if pkg.url:
+ data.append(_mksection("URL", pkg.url))
+ if pkg.license:
+ data.append(_mksection("LICENSE", pkg.license))
+ if pkg.arch:
+ data.append(_mksection("ARCH", pkg.arch))
+ if pkg.builddate:
+ data.append(_mksection("BUILDDATE", pkg.builddate))
+ if pkg.installdate:
+ data.append(_mksection("INSTALLDATE", pkg.installdate))
+ if pkg.packager:
+ data.append(_mksection("PACKAGER", pkg.packager))
+ if pkg.size:
+ data.append(_mksection("SIZE", pkg.size))
+ if pkg.reason:
+ data.append(_mksection("REASON", pkg.reason))
+ else:
+ if pkg.csize:
+ data.append(_mksection("CSIZE", pkg.csize))
+ if pkg.md5sum:
+ data.append(_mksection("MD5SUM", pkg.md5sum))
+ if data:
+ data.append("")
+ filename = os.path.join(path, "desc")
+ mkfile(filename, "\n".join(data))
+ pkg.checksum["desc"] = getmd5sum(filename)
+ pkg.mtime["desc"] = getmtime(filename)
+
+ # files
+ # for local entries, fields are: files, backup
+ # for sync ones: none
+ if self.treename == "local":
+ data = []
+ if pkg.files:
+ data.append(_mksection("FILES", _mkfilelist(pkg.files)))
+ if pkg.backup:
+ data.append(_mksection("BACKUP", _mkbackuplist(pkg.backup)))
+ if data:
+ data.append("")
+ filename = os.path.join(path, "files")
+ mkfile(filename, "\n".join(data))
+ pkg.checksum["files"] = getmd5sum(filename)
+ pkg.mtime["files"] = getmtime(filename)
+
+ # depends
+ # for local db entries: depends, requiredby, conflicts, provides
+ # for sync ones: depends, conflicts, provides
+ data = []
+ if pkg.depends:
+ data.append(_mksection("DEPENDS", pkg.depends))
+ if self.treename == "local":
+ if pkg.requiredby:
+ data.append(_mksection("REQUIREDBY", pkg.requiredby))
+ if pkg.conflicts:
+ data.append(_mksection("CONFLICTS", pkg.conflicts))
+ if pkg.provides:
+ data.append(_mksection("PROVIDES", pkg.provides))
+ if not self.treename == "local":
+ if pkg.replaces:
+ data.append(_mksection("REPLACES", pkg.replaces))
+ if pkg.force:
+ data.append(_mksection("FORCE", ""))
+ if data:
+ data.append("")
+ filename = os.path.join(path, "depends")
+ mkfile(filename, "\n".join(data))
+ pkg.checksum["depends"] = getmd5sum(filename)
+ pkg.mtime["depends"] = getmtime(filename)
+
+ # install
+ if self.treename == "local":
+ empty = 1
+ for value in pkg.install.values():
+ if value:
+ empty = 0
+ if not empty:
+ filename = os.path.join(path, "install")
+ mkinstallfile(filename, pkg.install)
+ pkg.checksum["install"] = getmd5sum(filename)
+ pkg.mtime["install"] = getmtime(filename)
+
+ def gensync(self, path):
+ """
+ """
+
+ curdir = os.getcwd()
+ tmpdir = tempfile.mkdtemp()
+ os.chdir(tmpdir)
+
+ for pkg in self.pkgs:
+ mkdescfile(pkg.fullname(), pkg)
+
+ # Generate database archive
+ os.makedirs(path, 0755)
+ archive = os.path.join(path, "%s%s" % (self.treename, PM_EXT_DB))
+ os.system("tar zcf %s *" % archive)
+
+ os.chdir(curdir)
+ shutil.rmtree(tmpdir)
+
+ def ispkgmodified(self, pkg):
+ """
+ """
+
+ modified = 0
+
+ oldpkg = self.getpkg(pkg.name)
+ if not oldpkg:
+ return 0
+
+ dbg("oldpkg.checksum : %s" % oldpkg.checksum)
+ dbg("oldpkg.mtime : %s" % oldpkg.mtime)
+
+ for key in pkg.mtime.keys():
+ if key == "install" \
+ and oldpkg.mtime[key] == (0, 0, 0) \
+ and pkg.mtime[key] == (0, 0, 0):
+ continue
+ if not oldpkg.mtime[key][1:3] == pkg.mtime[key][1:3]:
+ modified += 1
+
+ return modified
+
+
+if __name__ == "__main__":
+ db = pmdb("local")
+ print db
diff --git a/pactest/pmenv.py b/pactest/pmenv.py
new file mode 100755
index 00000000..5093612c
--- /dev/null
+++ b/pactest/pmenv.py
@@ -0,0 +1,118 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import os
+import time
+
+import pmtest
+
+
+class pmenv:
+ """Environment object
+ """
+
+ def __init__(self, root = "root"):
+ self.root = os.path.abspath(root)
+ self.pacman = {
+ "bin": "pacman",
+ "debug": 0,
+ "gdb": 0,
+ "valgrind": 0,
+ "nolog": 0
+ }
+ self.testcases = []
+
+ def __str__(self):
+ return "root = %s\n" \
+ "pacman = %s" \
+ % (self.root, self.pacman)
+
+ def addtest(self, testcase):
+ """
+ """
+ if not os.path.isfile(testcase):
+ err("file %s not found" % testcase)
+ return
+ test = pmtest.pmtest(testcase, self.root)
+ self.testcases.append(test)
+
+ def run(self):
+ """
+ """
+
+ for t in self.testcases:
+ print "=========="*8
+ print "Running '%s'" % t.name.strip(".py")
+
+ t.load()
+ print t.description
+ print "----------"*8
+
+ t.generate()
+ # Hack for mtimes consistency
+ modified = 0
+ for i in t.rules:
+ if i.rule.find("MODIFIED") != -1:
+ modified = 1
+ if modified:
+ time.sleep(3)
+
+ t.run(self.pacman)
+
+ t.check()
+ print "==> Test result"
+ if t.result["ko"] == 0:
+ print "\tPASSED"
+ else:
+ print "\tFAILED"
+ print
+
+ def results(self):
+ """
+ """
+ passed = 0
+ print "=========="*8
+ print "Results"
+ print "----------"*8
+ for test in self.testcases:
+ ok = test.result["ok"]
+ ko = test.result["ko"]
+ rules = len(test.rules)
+ if ko == 0:
+ print "[PASSED]",
+ passed += 1
+ else:
+ print "[FAILED]",
+ print test.name.strip(".py").ljust(38),
+ print "Rules:",
+ print "OK = %2u KO = %2u SKIP = %2u" % (ok, ko, rules-(ok+ko))
+ print "----------"*8
+ total = len(self.testcases)
+ failed = total - passed
+ print "TOTAL = %3u" % total
+ if total:
+ print "PASSED = %3u (%6.2f%%)" % (passed, float(passed)*100/total)
+ print "FAILED = %3u (%6.2f%%)" % (failed, float(failed)*100/total)
+ print
+
+
+if __name__ == "__main__":
+ env = pmenv("/tmp")
+ print env
diff --git a/pactest/pmfile.py b/pactest/pmfile.py
new file mode 100755
index 00000000..d78b09fa
--- /dev/null
+++ b/pactest/pmfile.py
@@ -0,0 +1,65 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import os
+
+from util import *
+
+
+class pmfile:
+ """File object
+ """
+
+ def __init__(self, root, name):
+ self.name = name
+ self.root = root
+
+ filename = os.path.join(self.root, self.name)
+ self.checksum = getmd5sum(filename)
+ self.mtime = getmtime(filename)
+
+ def __str__(self):
+ return "%s (%s / %lu)" % (self.name, self.checksum, self.mtime)
+
+ def ismodified(self):
+ """
+ """
+
+ retval = 0
+
+ filename = os.path.join(self.root, self.name)
+ checksum = getmd5sum(filename)
+ mtime = getmtime(filename)
+
+ if debug:
+ print "ismodified(%s)" % self.name
+ print "old: %s / %s" % (self.checksum, self.mtime)
+ print "new: %s / %s" % (checksum, mtime)
+
+ if not self.checksum == checksum \
+ or not (self.mtime[1], self.mtime[2]) == (mtime[1], mtime[2]):
+ retval = 1
+
+ return retval
+
+
+if __name__ == "__main__":
+ f = pmfile("/tmp", "foobar")
+ print f
diff --git a/pactest/pmpkg.py b/pactest/pmpkg.py
new file mode 100755
index 00000000..b2a28f96
--- /dev/null
+++ b/pactest/pmpkg.py
@@ -0,0 +1,180 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import os
+import tempfile
+import stat
+import shutil
+
+from util import *
+
+
+class pmpkg:
+ """Package object.
+
+ Object holding data from an ArchLinux package.
+ """
+
+ def __init__(self, name, version = "1.0-1"):
+ # desc
+ self.name = name
+ self.version = version
+ self.desc = ""
+ self.groups = []
+ self.url = ""
+ self.license = []
+ self.arch = ""
+ self.builddate = ""
+ self.installdate = ""
+ self.packager = ""
+ self.size = 0
+ self.csize = 0
+ self.reason = 0
+ self.md5sum = "" # sync only
+ self.replaces = [] # sync only (will be moved to depends)
+ self.force = 0 # sync only (will be moved to depends)
+ # depends
+ self.depends = []
+ self.requiredby = [] # local only
+ self.conflicts = []
+ self.provides = []
+ # files
+ self.files = []
+ self.backup = []
+ # install
+ self.install = {
+ "pre_install": "",
+ "post_install": "",
+ "pre_remove": "",
+ "post_remove": "",
+ "pre_upgrade": "",
+ "post_upgrade": ""
+ }
+ self.checksum = {
+ "desc": "",
+ "depends": "",
+ "files": "",
+ "install": ""
+ }
+ self.mtime = {
+ "desc": (0, 0, 0),
+ "depends": (0, 0, 0),
+ "files": (0, 0, 0),
+ "install": (0, 0, 0)
+ }
+
+ def __str__(self):
+ s = ["%s" % self.fullname()]
+ s.append("description: %s" % self.desc)
+ s.append("url: %s" % self.url)
+ s.append("depends: %s" % " ".join(self.depends))
+ s.append("files: %s" % " ".join(self.files))
+ s.append("reason: %d" % self.reason)
+ return "\n".join(s)
+
+ def fullname(self):
+ """Long name of a package.
+
+ Returns a string formatted as follows: "pkgname-pkgver".
+ """
+ return "%s-%s" % (self.name, self.version)
+
+ def filename(self):
+ """File name of a package, including its extension.
+
+ Returns a string formatted as follows: "pkgname-pkgver.PKG_EXT_PKG".
+ """
+ return "%s%s" % (self.fullname(), PM_EXT_PKG)
+
+ def install_files(self, root):
+ """Install files in the filesystem located under "root".
+
+ Files are created with content generated automatically.
+ """
+ [mkfile(os.path.join(root, f), f) for f in self.files]
+
+ def makepkg(self, path):
+ """Creates an ArchLinux package archive.
+
+ A package archive is generated in the location 'path', based on the data
+ from the object.
+ """
+ archive = os.path.join(path, self.filename())
+
+ curdir = os.getcwd()
+ tmpdir = tempfile.mkdtemp()
+ os.chdir(tmpdir)
+
+ # Generate package file system
+ for f in self.files:
+ mkfile(f, f)
+ self.size += os.stat(getfilename(f))[stat.ST_SIZE]
+
+ # .PKGINFO
+ data = ["pkgname = %s" % self.name]
+ data.append("pkgver = %s" % self.version)
+ data.append("pkgdesc = %s" % self.desc)
+ data.append("url = %s" % self.url)
+ data.append("builddate = %s" % self.builddate)
+ data.append("packager = %s" % self.packager)
+ data.append("size = %s" % self.size)
+ if self.arch:
+ data.append("arch = %s" % self.arch)
+ for i in self.license:
+ data.append("license = %s" % i)
+ for i in self.replaces:
+ data.append("replaces = %s" % i)
+ for i in self.groups:
+ data.append("group = %s" % i)
+ for i in self.depends:
+ data.append("depend = %s" % i)
+ for i in self.conflicts:
+ data.append("conflict = %s" % i)
+ for i in self.provides:
+ data.append("provides = %s" % i)
+ for i in self.backup:
+ data.append("backup = %s" % i)
+ mkfile(".PKGINFO", "\n".join(data))
+ targets = ".PKGINFO"
+
+ # .INSTALL
+ empty = 1
+ for value in self.install.values():
+ if value:
+ empty = 0
+ if not empty:
+ mkinstallfile(".INSTALL", self.install)
+ targets += " .INSTALL"
+
+ # .FILELIST
+ if self.files:
+ os.system("tar cvf /dev/null * | sort >.FILELIST")
+ targets += " .FILELIST *"
+
+ # Generate package archive
+ os.system("tar zcf %s %s" % (archive, targets))
+
+ os.chdir(curdir)
+ shutil.rmtree(tmpdir)
+
+
+if __name__ == "__main__":
+ pkg = pmpkg("dummy")
+ print pkg
diff --git a/pactest/pmrule.py b/pactest/pmrule.py
new file mode 100755
index 00000000..ad2e8930
--- /dev/null
+++ b/pactest/pmrule.py
@@ -0,0 +1,133 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+from util import *
+
+
+class pmrule:
+ """Rule object
+ """
+
+ def __init__(self, rule):
+ self.rule = rule
+ self.false = 0
+ self.result = 0
+
+ def __str__(self):
+ return "rule = %s" % self.rule
+
+ def check(self, root, retcode, localdb, files):
+ """
+ """
+
+ success = 1
+
+ [test, args] = self.rule.split("=")
+ if test[0] == "!":
+ self.false = 1
+ test = test.lstrip("!")
+ [kind, case] = test.split("_")
+ if "|" in args:
+ [key, value] = args.split("|", 1)
+ else:
+ [key, value] = [args, None]
+
+ if kind == "PACMAN":
+ if case == "RETCODE":
+ if retcode != int(key):
+ success = 0
+ elif case == "OUTPUT":
+ if not grep(os.path.join(root, LOGFILE), key):
+ success = 0
+ else:
+ success = -1
+ elif kind == "PKG":
+ newpkg = localdb.db_read(key)
+ if not newpkg:
+ success = 0
+ else:
+ dbg("newpkg.checksum : %s" % newpkg.checksum)
+ dbg("newpkg.mtime : %s" % newpkg.mtime)
+ if case == "EXIST":
+ success = 1
+ elif case == "MODIFIED":
+ if not localdb.ispkgmodified(newpkg):
+ success = 0
+ elif case == "VERSION":
+ if value != newpkg.version:
+ success = 0
+ elif case == "GROUPS":
+ if not value in newpkg.groups:
+ success = 0
+ elif case == "DEPENDS":
+ if not value in newpkg.depends:
+ success = 0
+ elif case == "REQUIREDBY":
+ if not value in newpkg.requiredby:
+ success = 0
+ elif case == "REASON":
+ if not newpkg.reason == int(value):
+ success = 0
+ elif case == "FILES":
+ if not value in newpkg.files:
+ success = 0
+ elif case == "BACKUP":
+ found = 0
+ for f in newpkg.backup:
+ name, md5sum = f.split("\t")
+ if value == name:
+ found = 1
+ if not found:
+ success = 0
+ else:
+ success = -1
+ elif kind == "FILE":
+ filename = os.path.join(root, key)
+ if case == "EXIST":
+ if not os.path.isfile(filename):
+ success = 0
+ else:
+ if case == "MODIFIED":
+ for f in files:
+ if f.name == key:
+ if not f.ismodified():
+ success = 0
+ elif case == "PACNEW":
+ if not os.path.isfile("%s%s" % (filename, PM_PACNEW)):
+ success = 0
+ elif case == "PACORIG":
+ if not os.path.isfile("%s%s" % (filename, PM_PACORIG)):
+ success = 0
+ elif case == "PACSAVE":
+ if not os.path.isfile("%s%s" % (filename, PM_PACSAVE)):
+ success = 0
+ else:
+ success = -1
+ else:
+ success = -1
+
+ if self.false and success != -1:
+ success = not success
+ self.result = success
+ return success
+
+
+if __name__ != "__main__":
+ rule = pmrule("PKG_EXIST=dummy")
diff --git a/pactest/pmtest.py b/pactest/pmtest.py
new file mode 100755
index 00000000..830202a5
--- /dev/null
+++ b/pactest/pmtest.py
@@ -0,0 +1,253 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import os
+import shutil
+import time
+
+import pmrule
+import pmdb
+import pmfile
+from pmpkg import pmpkg
+from util import *
+
+
+class pmtest:
+ """Test object
+ """
+
+ def __init__(self, name, root):
+ self.name = name
+ self.root = root
+
+ def __str__(self):
+ return "name = %s\n" \
+ "root = %s" % (self.name, self.root)
+
+ def addpkg2db(self, treename, pkg):
+ """
+ """
+ if not treename in self.db:
+ self.db[treename] = pmdb.pmdb(treename, os.path.join(self.root, PM_DBPATH))
+ self.db[treename].pkgs.append(pkg)
+
+ def addpkg(self, pkg):
+ """
+ """
+ self.localpkgs.append(pkg)
+
+ def addrule(self, rulename):
+ """
+ """
+ rule = pmrule.pmrule(rulename)
+ self.rules.append(rule)
+
+ def load(self):
+ """
+ """
+
+ # Reset test parameters
+ self.result = {
+ "ok": 0,
+ "ko": 0
+ }
+ self.args = ""
+ self.retcode = 0
+ self.db = {
+ "local": pmdb.pmdb("local", os.path.join(self.root, PM_DBPATH))
+ }
+ self.localpkgs = []
+ self.filesystem = []
+
+ self.description = ""
+ self.option = {
+ "noupgrade": [],
+ "ignorepkg": [],
+ "noextract": []
+ }
+
+ # Test rules
+ self.rules = []
+ self.files = []
+
+ if os.path.isfile(self.name):
+ execfile(self.name)
+ else:
+ err("file %s does not exist!" % self.name)
+
+ def generate(self):
+ """
+ """
+
+ print "==> Generating test environment"
+
+ # Cleanup leftover files from a previous test session
+ if os.path.isdir(self.root):
+ shutil.rmtree(self.root)
+ vprint("\t%s" % self.root)
+
+ # Create directory structure
+ vprint(" Creating directory structure:")
+ dbdir = os.path.join(self.root, PM_DBPATH)
+ cachedir = os.path.join(self.root, PM_CACHEDIR)
+ syncdir = os.path.join(self.root, SYNCREPO)
+ tmpdir = os.path.join(self.root, TMPDIR)
+ logdir = os.path.join(self.root, os.path.dirname(LOGFILE))
+ etcdir = os.path.join(self.root, os.path.dirname(PACCONF))
+ for dir in [dbdir, cachedir, syncdir, tmpdir, logdir, etcdir]:
+ if not os.path.isdir(dir):
+ vprint("\t%s" % dir[len(self.root)+1:])
+ os.makedirs(dir, 0755)
+
+ # Configuration file
+ vprint(" Creating configuration file")
+ vprint("\t%s" % PACCONF)
+ mkcfgfile(PACCONF, self.root, self.option, self.db)
+
+ # Creating packages
+ vprint(" Creating package archives")
+ for pkg in self.localpkgs:
+ vprint("\t%s" % os.path.join(TMPDIR, pkg.filename()))
+ pkg.makepkg(tmpdir)
+ for key, value in self.db.iteritems():
+ if key == "local":
+ continue
+ for pkg in value.pkgs:
+ archive = pkg.filename()
+ vprint("\t%s" % os.path.join(PM_CACHEDIR, archive))
+ pkg.makepkg(cachedir)
+ pkg.md5sum = getmd5sum(os.path.join(cachedir, archive))
+ pkg.csize = os.stat(os.path.join(cachedir, archive))[stat.ST_SIZE]
+
+ # Populating databases
+ vprint(" Populating databases")
+ for key, value in self.db.iteritems():
+ for pkg in value.pkgs:
+ vprint("\t%s/%s" % (key, pkg.fullname()))
+ if key == "local":
+ pkg.installdate = time.ctime()
+ value.db_write(pkg)
+
+ # Creating sync database archives
+ vprint(" Creating sync database archives")
+ for key, value in self.db.iteritems():
+ if key == "local":
+ continue
+ archive = value.treename + PM_EXT_DB
+ vprint("\t" + os.path.join(SYNCREPO, archive))
+ value.gensync(os.path.join(syncdir, value.treename))
+
+ # Filesystem
+ vprint(" Populating file system")
+ for pkg in self.db["local"].pkgs:
+ vprint("\tinstalling %s" % pkg.fullname())
+ pkg.install_files(self.root)
+ for f in self.filesystem:
+ vprint("\t%s" % f)
+ mkfile(os.path.join(self.root, f), f)
+
+ # Done.
+ vprint(" Taking a snapshot of the file system")
+ for roots, dirs, files in os.walk(self.root):
+ for i in files:
+ filename = os.path.join(roots, i)
+ f = pmfile.pmfile(self.root, filename.replace(self.root + "/", ""))
+ self.files.append(f)
+ vprint("\t%s" % f.name)
+
+ def run(self, pacman):
+ """
+ """
+
+ if os.path.isfile(PM_LOCK):
+ print "\tERROR: another pacman session is on-going -- skipping"
+ return
+
+ print "==> Running test"
+ vprint("\tpacman %s" % self.args)
+
+ cmd = ["fakeroot"]
+ if pacman["gdb"]:
+ cmd.append("libtool gdb --args")
+ if pacman["valgrind"]:
+ cmd.append("valgrind --tool=memcheck --leak-check=full --show-reachable=yes")
+ cmd.append("%s --noconfirm --config=%s --root=%s" \
+ % (pacman["bin"], os.path.join(self.root, PACCONF), self.root))
+ if pacman["debug"]:
+ cmd.append("--debug=%s" % pacman["debug"])
+ cmd.append("%s" % self.args)
+ if not pacman["gdb"] and not pacman["valgrind"] and not pacman["nolog"]:
+ cmd.append(">%s 2>&1" % os.path.join(self.root, LOGFILE))
+ dbg(" ".join(cmd))
+
+ # Change to the tmp dir before running pacman, so that local package
+ # archives are made available more easily.
+ curdir = os.getcwd()
+ tmpdir = os.path.join(self.root, TMPDIR)
+ os.chdir(tmpdir)
+
+ t0 = time.time()
+ self.retcode = os.system(" ".join(cmd))
+ t1 = time.time()
+ vprint("\ttime elapsed: %ds" % (t1-t0))
+
+ if self.retcode == None:
+ self.retcode = 0
+ else:
+ self.retcode /= 256
+ dbg("retcode = %s" % self.retcode)
+ os.chdir(curdir)
+
+ # Check if pacman failed because of bad permissions
+ if self.retcode \
+ and grep(os.path.join(self.root, LOGFILE),
+ "you cannot perform this operation unless you are root"):
+ print "\tERROR: pacman support for fakeroot is not disabled"
+ # Check if the lock is still there
+ if os.path.isfile(PM_LOCK):
+ print "\tERROR: %s not removed" % PM_LOCK
+ os.unlink(PM_LOCK)
+ # Look for a core file
+ if os.path.isfile(os.path.join(self.root, TMPDIR, "core")):
+ print "\tERROR: pacman dumped a core file"
+
+ def check(self):
+ """
+ """
+
+ print "==> Checking rules"
+
+ for i in self.rules:
+ success = i.check(self.root, self.retcode, self.db["local"], self.files)
+ if success == 1:
+ msg = "OK"
+ self.result["ok"] += 1
+ elif success == 0:
+ msg = "KO"
+ self.result["ko"] += 1
+ else:
+ msg = "SKIP"
+ print "\t[%s] %s" % (msg, i.rule)
+ i.result = success
+
+
+if __name__ == "__main__":
+ test = pmtest("test1", "./root")
+ print test
diff --git a/pactest/tests/TESTS b/pactest/tests/TESTS
new file mode 100644
index 00000000..67343750
--- /dev/null
+++ b/pactest/tests/TESTS
@@ -0,0 +1,61 @@
+add001: Install a package
+add002: Install a package (already installed)
+add003: Install a set of packages
+add004: Install a set of the same package at different versions
+add010: Install a package with a filesystem conflict
+add011: Install a package with a filesystem conflict (--force)
+add012: Install two packages with a conflicting file
+add013: Install two packages with a conflicting file (--force)
+add020: Install a package with an existing file
+add021: Install a package with an existing file (new modified)
+add030: Freshen a package
+add031: Freshen a package (installed is newer)
+add032: Freshen a package (installed is newer)
+add040: Install a package with a missing dependency
+add041: Install a package with a missing dependency (nodeps)
+add042: Install a package with cascaded dependencies
+add050: Install a package with a file in NoUpgrade
+add060: Install a package with a file in NoExtract
+query001: Query a package
+remove010: Remove a package, with a file marked for backup
+remove011: Remove a package, with a modified file marked for backup
+remove020: Remove a package, with a file marked for backup (--nosave)
+remove021: Remove a package, with a modified file marked for backup (--nosave)
+smoke001: Install a thousand packages in a single transaction
+sync001: Install a package from a sync db
+sync002: Upgrade a package from a sync db
+sync003: Install a package from a sync db, with a filesystem conflict
+sync010: Install a package from a sync db, with its dependencies
+sync040: Install two targets with a conflict
+sync041: Install two conflicting targets
+sync042: Install a sync package conflicting with a local one
+sync043: Install a sync package conflicting with a local one
+sync050: Install a virtual target (provided by a sync package)
+sync100: Sysupgrade with a newer sync package
+sync101: Sysupgrade with same version for local and sync packages
+sync102: Sysupgrade with a newer local package
+sync103: Sysupgrade with a local package not existing in sync db
+sync110: Sysupgrade of a package pulling new dependencies
+sync120: Sysupgrade of packages in 'IgnorePkg'
+sync130: Sysupgrade with a sync package replacing a local one
+sync131: Sysupgrade with a sync package replacing a set of local ones
+sync132: Sysupgrade with a replacement for a local package out of date
+sync133: Sysupgrade with a sync package replacing a local one in 'IgnorePkg'
+sync134: Sysupgrade with a set of sync packages replacing a set local one
+sync135: Sysupgrade with a set of sync packages replacing a set of local ones
+sync897: System upgrade
+sync898: System upgrade
+sync899: System upgrade
+sync990: Sync a package pulling a dependency conflicting with a target
+sync992: Sync a package pulling a conflicting dependency
+sync999: System upgrade
+upgrade001: Upgrade a package (newer version)
+upgrade002: Upgrade a package (same version)
+upgrade003: Upgrade a package (lesser version)
+upgrade004: Upgrade a package (not installed)
+upgrade010: Upgrade a package, with a file in NoUpgrade
+upgrade020: Upgrade a package, with a file in 'backup' (new modified)
+upgrade021: Upgrade a package, with a file in 'backup' (local modified, new unchanged)
+upgrade022: Upgrade a package, with a file in 'backup' (local and new modified)
+upgrade030: Upgrade packages with various reasons
+upgrade040: file relocation 1
diff --git a/pactest/tests/add001.py b/pactest/tests/add001.py
new file mode 100644
index 00000000..159a54a3
--- /dev/null
+++ b/pactest/tests/add001.py
@@ -0,0 +1,13 @@
+self.description = "Install a package"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-A %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/add002.py b/pactest/tests/add002.py
new file mode 100644
index 00000000..2ec90644
--- /dev/null
+++ b/pactest/tests/add002.py
@@ -0,0 +1,18 @@
+self.description = "Install a package (already installed)"
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-A %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_MODIFIED=dummy")
+for f in lp.files:
+ self.addrule("!FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/add003.py b/pactest/tests/add003.py
new file mode 100644
index 00000000..bde87649
--- /dev/null
+++ b/pactest/tests/add003.py
@@ -0,0 +1,23 @@
+self.description = "Install a set of packages"
+
+p1 = pmpkg("pkg1")
+p1.files = ["bin/pkg1",
+ "usr/man/man1/pkg1.1"]
+
+p2 = pmpkg("pkg2", "2.0-1")
+p2.files = ["bin/pkg2",
+ "usr/man/man1/pkg2.1"]
+
+p3 = pmpkg("pkg3", "3.0-1")
+p3.files = ["bin/pkg3", "usr/man/man1/pkg3.1"]
+
+for p in p1, p2, p3:
+ self.addpkg(p)
+
+self.args = "-A %s" % " ".join([p.filename() for p in p1, p2, p3])
+
+self.addrule("PACMAN_RETCODE=0")
+for p in p1, p2, p3:
+ self.addrule("PKG_EXIST=%s" % p.name)
+ for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/add004.py b/pactest/tests/add004.py
new file mode 100644
index 00000000..59ba45c6
--- /dev/null
+++ b/pactest/tests/add004.py
@@ -0,0 +1,12 @@
+self.description = "Install a set of the same package at different versions"
+
+p1 = pmpkg("dummy", "1.0-2")
+p2 = pmpkg("dummy", "2.0-1")
+p3 = pmpkg("dummy")
+for p in p1, p2, p3:
+ self.addpkg(p)
+
+self.args = "-A %s" % " ".join([p.filename() for p in p1, p2, p3])
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=dummy|2.0-1")
diff --git a/pactest/tests/add010.py b/pactest/tests/add010.py
new file mode 100644
index 00000000..a7874746
--- /dev/null
+++ b/pactest/tests/add010.py
@@ -0,0 +1,15 @@
+self.description = "Install a package with a filesystem conflict"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.filesystem = ["bin/dummy"]
+
+self.args = "-A %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_MODIFIED=bin/dummy")
+self.addrule("!FILE_EXIST=usr/man/man1/dummy.1")
diff --git a/pactest/tests/add011.py b/pactest/tests/add011.py
new file mode 100644
index 00000000..5d2ae43f
--- /dev/null
+++ b/pactest/tests/add011.py
@@ -0,0 +1,14 @@
+self.description = "Install a package with a filesystem conflict (--force)"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy", "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.filesystem = ["bin/dummy"]
+
+self.args = "-Af %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("FILE_MODIFIED=bin/dummy")
+self.addrule("FILE_EXIST=usr/man/man1/dummy.1")
diff --git a/pactest/tests/add012.py b/pactest/tests/add012.py
new file mode 100644
index 00000000..291d1fba
--- /dev/null
+++ b/pactest/tests/add012.py
@@ -0,0 +1,20 @@
+self.description = "Install two packages with a conflicting file"
+
+p1 = pmpkg("dummy")
+p1.files = ["bin/dummy",
+ "usr/man/man1/dummy.1",
+ "usr/common"]
+
+p2 = pmpkg("foobar")
+p2.files = ["bin/foobar",
+ "usr/man/man1/foobar.1",
+ "usr/common"]
+
+for p in p1, p2:
+ self.addpkg(p)
+
+self.args = "-A %s" % " ".join([p.filename() for p in p1, p2])
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!PKG_EXIST=foobar")
diff --git a/pactest/tests/add013.py b/pactest/tests/add013.py
new file mode 100644
index 00000000..547ce001
--- /dev/null
+++ b/pactest/tests/add013.py
@@ -0,0 +1,23 @@
+self.description = "Install two packages with a conflicting file (--force)"
+
+p1 = pmpkg("dummy")
+p1.files = ["bin/dummy",
+ "usr/man/man1/dummy.1",
+ "usr/common"]
+
+p2 = pmpkg("foobar")
+p2.files = ["bin/foobar",
+ "usr/man/man1/foobar.1",
+ "usr/common"]
+
+for p in p1, p2:
+ self.addpkg(p)
+
+self.args = "-Af %s" % " ".join([p.filename() for p in p1, p2])
+
+self.addrule("PACMAN_RETCODE=0")
+for p in p1, p2:
+ self.addrule("PKG_EXIST=%s" % p.name)
+ self.addrule("PKG_FILES=%s|usr/common" % p.name)
+ for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/add020.py b/pactest/tests/add020.py
new file mode 100644
index 00000000..b435ddde
--- /dev/null
+++ b/pactest/tests/add020.py
@@ -0,0 +1,15 @@
+self.description = "Install a package with an existing file"
+
+p = pmpkg("dummy")
+p.files = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.filesystem = ["etc/dummy.conf"]
+
+self.args = "-Af %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("FILE_MODIFIED=etc/dummy.conf")
+self.addrule("!FILE_PACNEW=etc/dummy.conf")
+self.addrule("!FILE_PACORIG=etc/dummy.conf")
diff --git a/pactest/tests/add021.py b/pactest/tests/add021.py
new file mode 100644
index 00000000..ab96d156
--- /dev/null
+++ b/pactest/tests/add021.py
@@ -0,0 +1,16 @@
+self.description = "Install a package with an existing file (new modified)"
+
+p = pmpkg("dummy")
+p.files = ["etc/dummy.conf*"]
+p.backup = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.filesystem = ["etc/dummy.conf"]
+
+self.args = "-Af %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("FILE_MODIFIED=etc/dummy.conf")
+self.addrule("!FILE_PACNEW=etc/dummy.conf")
+self.addrule("FILE_PACORIG=etc/dummy.conf")
diff --git a/pactest/tests/add030.py b/pactest/tests/add030.py
new file mode 100644
index 00000000..d36311c0
--- /dev/null
+++ b/pactest/tests/add030.py
@@ -0,0 +1,18 @@
+self.description = "Freshen a package"
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-F %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=dummy|1.0-2")
+for f in p.files:
+ self.addrule("FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/add031.py b/pactest/tests/add031.py
new file mode 100644
index 00000000..38b0485c
--- /dev/null
+++ b/pactest/tests/add031.py
@@ -0,0 +1,18 @@
+self.description = "Freshen a package (installed is newer)"
+
+lp = pmpkg("dummy", "1.0-2")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-F %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_MODIFIED=dummy")
+for f in p.files:
+ self.addrule("!FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/add032.py b/pactest/tests/add032.py
new file mode 100644
index 00000000..00d9a6d0
--- /dev/null
+++ b/pactest/tests/add032.py
@@ -0,0 +1,18 @@
+self.description = "Freshen a package (installed is newer)"
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-F %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_MODIFIED=dummy")
+for f in p.files:
+ self.addrule("!FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/add040.py b/pactest/tests/add040.py
new file mode 100644
index 00000000..2bb7f91b
--- /dev/null
+++ b/pactest/tests/add040.py
@@ -0,0 +1,14 @@
+self.description = "Install a package with a missing dependency"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+p.depends = ["missdep"]
+self.addpkg(p)
+
+self.args = "-A %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_EXIST=dummy")
+for f in p.files:
+ self.addrule("!FILE_EXIST=%s" % f)
diff --git a/pactest/tests/add041.py b/pactest/tests/add041.py
new file mode 100644
index 00000000..af3ffe46
--- /dev/null
+++ b/pactest/tests/add041.py
@@ -0,0 +1,15 @@
+self.description = "Install a package with a missing dependency (nodeps)"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+p.depends = ["dep1"]
+self.addpkg(p)
+
+self.args = "-Ad %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("PKG_DEPENDS=dummy|dep1")
+for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/add042.py b/pactest/tests/add042.py
new file mode 100644
index 00000000..a9b17c0f
--- /dev/null
+++ b/pactest/tests/add042.py
@@ -0,0 +1,29 @@
+self.description = "Install a package with cascaded dependencies"
+
+p1 = pmpkg("dummy", "1.0-2")
+p1.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+p1.depends = ["dep1"]
+
+p2 = pmpkg("dep1")
+p2.files = ["bin/dep1"]
+p2.depends = ["dep2"]
+
+p3 = pmpkg("dep2")
+p3.files = ["bin/dep2"]
+
+for p in p1, p2, p3:
+ self.addpkg(p)
+
+self.args = "-A %s" % " ".join([p.filename() for p in p1, p2, p3])
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=dummy|1.0-2")
+for f in p1.files:
+ self.addrule("FILE_EXIST=%s" % f)
+self.addrule("PKG_EXIST=dep1")
+self.addrule("PKG_EXIST=dep2")
+self.addrule("PKG_DEPENDS=dummy|dep1")
+self.addrule("PKG_DEPENDS=dep1|dep2")
+self.addrule("PKG_REQUIREDBY=dep1|dummy")
+self.addrule("PKG_REQUIREDBY=dep2|dep1")
diff --git a/pactest/tests/add050.py b/pactest/tests/add050.py
new file mode 100644
index 00000000..f9b3b251
--- /dev/null
+++ b/pactest/tests/add050.py
@@ -0,0 +1,16 @@
+self.description = "Install a package with a file in NoUpgrade"
+
+p = pmpkg("dummy")
+p.files = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.filesystem = ["etc/dummy.conf"]
+
+self.option["noupgrade"] = ["etc/dummy.conf"]
+
+self.args = "-Af %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("!FILE_MODIFIED=etc/dummy.conf")
+self.addrule("FILE_PACNEW=etc/dummy.conf")
diff --git a/pactest/tests/add060.py b/pactest/tests/add060.py
new file mode 100644
index 00000000..4c5f17da
--- /dev/null
+++ b/pactest/tests/add060.py
@@ -0,0 +1,15 @@
+self.description = "Install a package with a file in NoExtract"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.option["noextract"] = ["usr/man/man1/dummy.1"]
+
+self.args = "-A %s" % p.filename()
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=dummy")
+self.addrule("FILE_EXIST=bin/dummy")
+self.addrule("!FILE_EXIST=usr/man/man1/dummy.1")
diff --git a/pactest/tests/dummy001.py b/pactest/tests/dummy001.py
new file mode 100644
index 00000000..79e2bd00
--- /dev/null
+++ b/pactest/tests/dummy001.py
@@ -0,0 +1,19 @@
+self.description = "dummy test case"
+
+p1 = pmpkg("dummy")
+p1.files = ["etc/dummy.conf*",
+ "lib/libdummy.so.0",
+ "lib/libdummy.so -> ./libdummy.so.0",
+ "usr/",
+ "bin/dummy"]
+p1.backup = ["etc/dummy.conf*"]
+p1.install['post_install'] = "echo toto";
+p1.url="ze url"
+self.addpkg(p1)
+
+#p2 = pmpkg("dummy", "1.0-2")
+#p2.files = ["etc/dummy.conf**"]
+#p2.backup = ["etc/dummy.conf"]
+#self.addpkg(p2)
+
+self.args = "-U %s" % p1.filename()
diff --git a/pactest/tests/query001.py b/pactest/tests/query001.py
new file mode 100644
index 00000000..fe689104
--- /dev/null
+++ b/pactest/tests/query001.py
@@ -0,0 +1,9 @@
+self.description = "Query a package"
+
+p = pmpkg("foobar")
+p.files = ["bin/foobar"]
+self.addpkg2db("local", p)
+
+self.args = "-Q foobar"
+
+self.addrule("PACMAN_OUTPUT=foobar")
diff --git a/pactest/tests/remove010.py b/pactest/tests/remove010.py
new file mode 100644
index 00000000..aff46f0e
--- /dev/null
+++ b/pactest/tests/remove010.py
@@ -0,0 +1,12 @@
+self.description = "Remove a package, with a file marked for backup"
+
+p1 = pmpkg("dummy")
+p1.files = ["etc/dummy.conf"]
+p1.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", p1)
+
+self.args = "-R dummy"
+
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_EXIST=etc/dummy.conf")
+self.addrule("FILE_PACSAVE=etc/dummy.conf")
diff --git a/pactest/tests/remove011.py b/pactest/tests/remove011.py
new file mode 100644
index 00000000..afc03d45
--- /dev/null
+++ b/pactest/tests/remove011.py
@@ -0,0 +1,12 @@
+self.description = "Remove a package, with a modified file marked for backup"
+
+p1 = pmpkg("dummy")
+p1.files = ["etc/dummy.conf*"]
+p1.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", p1)
+
+self.args = "-R dummy"
+
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_EXIST=etc/dummy.conf")
+self.addrule("FILE_PACSAVE=etc/dummy.conf")
diff --git a/pactest/tests/remove020.py b/pactest/tests/remove020.py
new file mode 100644
index 00000000..293ad1bd
--- /dev/null
+++ b/pactest/tests/remove020.py
@@ -0,0 +1,12 @@
+self.description = "Remove a package, with a file marked for backup (--nosave)"
+
+p1 = pmpkg("dummy")
+p1.files = ["etc/dummy.conf"]
+p1.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", p1)
+
+self.args = "-Rn dummy"
+
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_EXIST=etc/dummy.conf")
+self.addrule("!FILE_PACSAVE=etc/dummy.conf")
diff --git a/pactest/tests/remove021.py b/pactest/tests/remove021.py
new file mode 100644
index 00000000..388bf018
--- /dev/null
+++ b/pactest/tests/remove021.py
@@ -0,0 +1,12 @@
+self.description = "Remove a package, with a modified file marked for backup (--nosave)"
+
+p1 = pmpkg("dummy")
+p1.files = ["etc/dummy.conf*"]
+p1.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", p1)
+
+self.args = "-Rn dummy"
+
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_EXIST=etc/dummy.conf")
+self.addrule("!FILE_PACSAVE=etc/dummy.conf")
diff --git a/pactest/tests/remove030.py b/pactest/tests/remove030.py
new file mode 100644
index 00000000..ff81a263
--- /dev/null
+++ b/pactest/tests/remove030.py
@@ -0,0 +1,12 @@
+self.description = "Remove a package in HoldPkg"
+
+p1 = pmpkg("dummy")
+self.addpkg2db("local", p1)
+
+self.option["holdpkg"] = ["dummy"]
+
+self.args = "-R dummy"
+
+self.addrule("!PKG_EXIST=dummy")
+self.addrule("!FILE_EXIST=etc/dummy.conf")
+self.addrule("!FILE_PACSAVE=etc/dummy.conf")
diff --git a/pactest/tests/smoke001.py b/pactest/tests/smoke001.py
new file mode 100644
index 00000000..85ee782b
--- /dev/null
+++ b/pactest/tests/smoke001.py
@@ -0,0 +1,19 @@
+self.description = "Install a thousand packages in a single transaction"
+
+p = pmpkg("pkg1000")
+
+self.addpkg2db("local", p)
+
+for i in range(1000):
+ p = pmpkg("pkg%03d" % i)
+ p.depends = ["pkg%03d" % (i+1)]
+ p.files = ["usr/share/pkg%03d" % i]
+ self.addpkg(p)
+
+_list = []
+[_list.append(p.filename()) for p in self.localpkgs]
+self.args = "-A %s" % " ".join(_list)
+
+self.addrule("PACMAN_RETCODE=0")
+#for i in range(1000):
+# self.addrule("PKG_EXIST=pkg%03d" %i)
diff --git a/pactest/tests/sync001.py b/pactest/tests/sync001.py
new file mode 100644
index 00000000..fc2015d4
--- /dev/null
+++ b/pactest/tests/sync001.py
@@ -0,0 +1,12 @@
+self.description = "Install a package from a sync db"
+
+sp = pmpkg("dummy")
+sp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("sync", sp)
+
+self.args = "-S dummy"
+
+self.addrule("PKG_EXIST=dummy")
+for f in sp.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/sync002.py b/pactest/tests/sync002.py
new file mode 100644
index 00000000..43c99fbd
--- /dev/null
+++ b/pactest/tests/sync002.py
@@ -0,0 +1,17 @@
+self.description = "Upgrade a package from a sync db"
+
+sp = pmpkg("dummy", "1.0-2")
+sp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("sync", sp)
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+self.args = "-S dummy"
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+for f in lp.files:
+ self.addrule("FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/sync003.py b/pactest/tests/sync003.py
new file mode 100644
index 00000000..3a480adf
--- /dev/null
+++ b/pactest/tests/sync003.py
@@ -0,0 +1,12 @@
+self.description = "Install a package from a sync db, with a filesystem conflict"
+
+sp = pmpkg("dummy")
+sp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("sync", sp)
+
+self.filesystem = ["bin/dummy"]
+
+self.args = "-S dummy"
+
+self.addrule("!PKG_EXIST=dummy")
diff --git a/pactest/tests/sync009.py b/pactest/tests/sync009.py
new file mode 100644
index 00000000..fc2015d4
--- /dev/null
+++ b/pactest/tests/sync009.py
@@ -0,0 +1,12 @@
+self.description = "Install a package from a sync db"
+
+sp = pmpkg("dummy")
+sp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("sync", sp)
+
+self.args = "-S dummy"
+
+self.addrule("PKG_EXIST=dummy")
+for f in sp.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/sync010.py b/pactest/tests/sync010.py
new file mode 100644
index 00000000..9e54eb62
--- /dev/null
+++ b/pactest/tests/sync010.py
@@ -0,0 +1,26 @@
+self.description = "Install a package from a sync db, with its dependencies"
+
+sp1 = pmpkg("dummy", "1.0-2")
+sp1.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+sp1.depends = ["dep1"]
+
+sp2 = pmpkg("dep1")
+sp2.files = ["bin/dep1"]
+sp2.depends = ["dep2"]
+
+sp3 = pmpkg("dep2")
+sp3.files = ["bin/dep2"]
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p);
+
+self.args = "-S dummy"
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+self.addrule("PKG_DEPENDS=dummy|dep1")
+for f in sp1.files:
+ self.addrule("FILE_EXIST=%s" % f)
+self.addrule("PKG_DEPENDS=dep1|dep2")
+self.addrule("PKG_REQUIREDBY=dep1|dummy")
+self.addrule("PKG_REQUIREDBY=dep2|dep1")
diff --git a/pactest/tests/sync020.py b/pactest/tests/sync020.py
new file mode 100644
index 00000000..eae699fe
--- /dev/null
+++ b/pactest/tests/sync020.py
@@ -0,0 +1,19 @@
+self.description = "Install a group from a sync db"
+
+sp1 = pmpkg("pkg1")
+sp1.groups = ["grp"]
+
+sp2 = pmpkg("pkg2")
+sp2.groups = ["grp"]
+
+sp3 = pmpkg("pkg3")
+sp3.groups = ["grp"]
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p);
+
+self.args = "-S grp"
+
+self.addrule("PACMAN_RETCODE=0")
+for p in sp1, sp2, sp3:
+ self.addrule("PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync021.py b/pactest/tests/sync021.py
new file mode 100644
index 00000000..d8a5475b
--- /dev/null
+++ b/pactest/tests/sync021.py
@@ -0,0 +1,21 @@
+self.description = "Install a group from a sync db, with a package in IgnorePkg"
+
+sp1 = pmpkg("pkg1")
+sp1.groups = ["grp"]
+
+sp2 = pmpkg("pkg2")
+sp2.groups = ["grp"]
+
+sp3 = pmpkg("pkg3")
+sp3.groups = ["grp"]
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p);
+
+self.option["ignorepkg"] = ["pkg2"]
+
+self.args = "-S grp"
+
+self.addrule("PACMAN_RETCODE=0")
+for p in sp1, sp2, sp3:
+ self.addrule("PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync040.py b/pactest/tests/sync040.py
new file mode 100644
index 00000000..73f6ee63
--- /dev/null
+++ b/pactest/tests/sync040.py
@@ -0,0 +1,15 @@
+self.description = "Install two targets with a conflict"
+
+sp1 = pmpkg("pkg1")
+sp1.conflicts = ["pkg2"]
+
+sp2 = pmpkg("pkg2")
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p);
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=1")
+for p in sp1, sp2:
+ self.addrule("!PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync041.py b/pactest/tests/sync041.py
new file mode 100644
index 00000000..a612e71f
--- /dev/null
+++ b/pactest/tests/sync041.py
@@ -0,0 +1,16 @@
+self.description = "Install two conflicting targets"
+
+sp1 = pmpkg("pkg1")
+sp1.conflicts = ["pkg2"]
+
+sp2 = pmpkg("pkg2")
+sp2.conflicts = ["pkg1"]
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p);
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync042.py b/pactest/tests/sync042.py
new file mode 100644
index 00000000..200626d3
--- /dev/null
+++ b/pactest/tests/sync042.py
@@ -0,0 +1,14 @@
+self.description = "Install a sync package conflicting with a local one"
+
+sp = pmpkg("pkg1")
+sp.conflicts = ["pkg2"]
+self.addpkg2db("sync", sp);
+
+lp = pmpkg("pkg2")
+self.addpkg2db("local", lp);
+
+self.args = "-S pkg1"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync043.py b/pactest/tests/sync043.py
new file mode 100644
index 00000000..200626d3
--- /dev/null
+++ b/pactest/tests/sync043.py
@@ -0,0 +1,14 @@
+self.description = "Install a sync package conflicting with a local one"
+
+sp = pmpkg("pkg1")
+sp.conflicts = ["pkg2"]
+self.addpkg2db("sync", sp);
+
+lp = pmpkg("pkg2")
+self.addpkg2db("local", lp);
+
+self.args = "-S pkg1"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync050.py b/pactest/tests/sync050.py
new file mode 100644
index 00000000..6c7619be
--- /dev/null
+++ b/pactest/tests/sync050.py
@@ -0,0 +1,10 @@
+self.description = "Install a virtual target (provided by a sync package)"
+
+sp1 = pmpkg("pkg1")
+sp1.provides = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+self.args = "-S pkg2"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
diff --git a/pactest/tests/sync100.py b/pactest/tests/sync100.py
new file mode 100644
index 00000000..a4997fb1
--- /dev/null
+++ b/pactest/tests/sync100.py
@@ -0,0 +1,12 @@
+self.description = "Sysupgrade with a newer sync package"
+
+sp = pmpkg("dummy", "1.0-2")
+lp = pmpkg("dummy")
+
+self.addpkg2db("sync", sp)
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=dummy|1.0-2")
diff --git a/pactest/tests/sync101.py b/pactest/tests/sync101.py
new file mode 100644
index 00000000..5d39ecb9
--- /dev/null
+++ b/pactest/tests/sync101.py
@@ -0,0 +1,12 @@
+self.description = "Sysupgrade with same version for local and sync packages"
+
+sp = pmpkg("dummy")
+lp = pmpkg("dummy")
+
+self.addpkg2db("sync", sp)
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_MODIFIED=dummy")
diff --git a/pactest/tests/sync102.py b/pactest/tests/sync102.py
new file mode 100644
index 00000000..40a7ec99
--- /dev/null
+++ b/pactest/tests/sync102.py
@@ -0,0 +1,12 @@
+self.description = "Sysupgrade with a newer local package"
+
+sp = pmpkg("dummy", "0.9-1")
+lp = pmpkg("dummy")
+
+self.addpkg2db("sync", sp)
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_MODIFIED=dummy")
diff --git a/pactest/tests/sync103.py b/pactest/tests/sync103.py
new file mode 100644
index 00000000..5d17790b
--- /dev/null
+++ b/pactest/tests/sync103.py
@@ -0,0 +1,14 @@
+self.description = "Sysupgrade with a local package not existing in sync db"
+
+sp = pmpkg("spkg")
+
+self.addpkg2db("sync", sp)
+
+lp = pmpkg("lpkg")
+
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_MODIFIED=lpkg")
diff --git a/pactest/tests/sync110.py b/pactest/tests/sync110.py
new file mode 100644
index 00000000..08a1a890
--- /dev/null
+++ b/pactest/tests/sync110.py
@@ -0,0 +1,22 @@
+self.description = "Sysupgrade of a package pulling new dependencies"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.depends = ["pkg2"]
+
+sp2 = pmpkg("pkg2")
+sp2.depends = ["pkg3"]
+
+sp3 = pmpkg("pkg3")
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg1")
+self.addpkg2db("local", lp1)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=pkg1|1.0-2")
+for p in sp2, sp3:
+ self.addrule("PKG_REASON=%s|1" % p.name)
diff --git a/pactest/tests/sync120.py b/pactest/tests/sync120.py
new file mode 100644
index 00000000..b8fc6747
--- /dev/null
+++ b/pactest/tests/sync120.py
@@ -0,0 +1,21 @@
+self.description = "Sysupgrade of packages in 'IgnorePkg'"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp2 = pmpkg("pkg2", "1.0-2")
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg1")
+lp2 = pmpkg("pkg2")
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.option["ignorepkg"] = ["pkg2"]
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_VERSION=pkg1|1.0-2")
+self.addrule("!PKG_MODIFIED=pkg2")
diff --git a/pactest/tests/sync130.py b/pactest/tests/sync130.py
new file mode 100644
index 00000000..afd196d1
--- /dev/null
+++ b/pactest/tests/sync130.py
@@ -0,0 +1,16 @@
+self.description = "Sysupgrade with a sync package replacing a local one"
+
+sp = pmpkg("pkg2")
+sp.replaces = ["pkg1"]
+
+self.addpkg2db("sync", sp)
+
+lp = pmpkg("pkg1")
+
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_EXIST=pkg1")
+self.addrule("PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync131.py b/pactest/tests/sync131.py
new file mode 100644
index 00000000..d0ed1a9f
--- /dev/null
+++ b/pactest/tests/sync131.py
@@ -0,0 +1,19 @@
+self.description = "Sysupgrade with a sync package replacing a set of local ones"
+
+sp = pmpkg("pkg4")
+sp.replaces = ["pkg1", "pkg2", "pkg3"]
+
+self.addpkg2db("sync", sp)
+
+lp1 = pmpkg("pkg1")
+lp2 = pmpkg("pkg2")
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg4")
+for p in lp1, lp2:
+ self.addrule("!PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync132.py b/pactest/tests/sync132.py
new file mode 100644
index 00000000..5e85727d
--- /dev/null
+++ b/pactest/tests/sync132.py
@@ -0,0 +1,18 @@
+self.description = "Sysupgrade with a replacement for a local package out of date"
+
+sp1 = pmpkg("pkg1")
+sp1.replaces = ["pkg2"]
+sp2 = pmpkg("pkg2", "2.0-1")
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p)
+
+lp = pmpkg("pkg2")
+
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync133.py b/pactest/tests/sync133.py
new file mode 100644
index 00000000..b852a7fc
--- /dev/null
+++ b/pactest/tests/sync133.py
@@ -0,0 +1,18 @@
+self.description = "Sysupgrade with a sync package replacing a local one in 'IgnorePkg'"
+
+sp = pmpkg("pkg2")
+sp.replaces = ["pkg1"]
+
+self.addpkg2db("sync", sp)
+
+lp = pmpkg("pkg1")
+
+self.addpkg2db("local", lp)
+
+self.option["ignorepkg"] = ["pkg1"]
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync134.py b/pactest/tests/sync134.py
new file mode 100644
index 00000000..572ab326
--- /dev/null
+++ b/pactest/tests/sync134.py
@@ -0,0 +1,21 @@
+self.description = "Sysupgrade with a set of sync packages replacing a set local one"
+
+sp1 = pmpkg("pkg2")
+sp1.replaces = ["pkg1"]
+
+sp2 = pmpkg("pkg3")
+sp2.replaces = ["pkg1"]
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p)
+
+lp = pmpkg("pkg1")
+
+self.addpkg2db("local", lp)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_EXIST=pkg1")
+for p in sp1, sp2:
+ self.addrule("PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync135.py b/pactest/tests/sync135.py
new file mode 100644
index 00000000..18c412aa
--- /dev/null
+++ b/pactest/tests/sync135.py
@@ -0,0 +1,31 @@
+self.description = "Sysupgrade with a set of sync packages replacing a set of local ones"
+
+sp1 = pmpkg("pkg2")
+sp1.replaces = ["pkg1"]
+
+sp2 = pmpkg("pkg3")
+sp2.replaces = ["pkg1"]
+
+sp3 = pmpkg("pkg4")
+sp3.replaces = ["pkg1", "pkg0"]
+
+sp4 = pmpkg("pkg5")
+sp4.replaces = ["pkg0"]
+
+for p in sp1, sp2, sp3, sp4:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg1")
+
+lp2 = pmpkg("pkg0")
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+for p in lp1, lp2:
+ self.addrule("!PKG_EXIST=%s" % p.name)
+for p in sp1, sp2, sp3, sp4:
+ self.addrule("PKG_EXIST=%s" % p.name)
diff --git a/pactest/tests/sync200.py b/pactest/tests/sync200.py
new file mode 100644
index 00000000..82f30da1
--- /dev/null
+++ b/pactest/tests/sync200.py
@@ -0,0 +1,15 @@
+self.description = "Synchronize database"
+
+sp1 = pmpkg("spkg1", "1.0-1")
+sp1.depends = ["spkg2"]
+sp2 = pmpkg("spkg2", "2.0-1")
+sp2.depends = ["spkg3"]
+sp3 = pmpkg("spkg3", "3.0-1")
+sp3.depends = ["spkg1"]
+
+for sp in sp1, sp2, sp3:
+ self.addpkg2db("sync", sp)
+
+self.args = "-Sy"
+
+self.addrule("PACMAN_RETCODE=0")
diff --git a/pactest/tests/sync890.py b/pactest/tests/sync890.py
new file mode 100644
index 00000000..0613128e
--- /dev/null
+++ b/pactest/tests/sync890.py
@@ -0,0 +1,20 @@
+self.description = "conflict 'db vs targ'"
+
+sp = pmpkg("pkg3")
+
+self.addpkg2db("sync", sp)
+
+lp1 = pmpkg("pkg1")
+
+lp2 = pmpkg("pkg2")
+lp2.conflicts = ["pkg3"]
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.args = "-S pkg3"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
+self.addrule("PKG_EXIST=pkg3")
diff --git a/pactest/tests/sync891.py b/pactest/tests/sync891.py
new file mode 100644
index 00000000..7810ac93
--- /dev/null
+++ b/pactest/tests/sync891.py
@@ -0,0 +1,22 @@
+self.description = "conflict 'db vs targ'"
+
+sp1 = pmpkg("pkg2")
+sp2 = pmpkg("pkg3")
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg1")
+
+lp2 = pmpkg("pkg2")
+lp2.conflicts = ["pkg3"]
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.args = "-S pkg2 pkg3"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("PKG_EXIST=pkg2")
+self.addrule("PKG_EXIST=pkg3")
diff --git a/pactest/tests/sync892.py b/pactest/tests/sync892.py
new file mode 100644
index 00000000..9d37d682
--- /dev/null
+++ b/pactest/tests/sync892.py
@@ -0,0 +1,24 @@
+self.description = "conflict 'targ vs targ' and 'db vs targ'"
+
+sp1 = pmpkg("pkg2")
+sp1.conflicts = ["pkg1"]
+
+sp2 = pmpkg("pkg3")
+
+for p in sp1, sp2:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg1")
+
+lp2 = pmpkg("pkg2")
+lp2.conflicts = ["pkg3"]
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+self.args = "-S pkg2 pkg3"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("!PKG_EXIST=pkg1")
+self.addrule("PKG_EXIST=pkg2")
+self.addrule("PKG_EXIST=pkg3")
diff --git a/pactest/tests/sync893.py b/pactest/tests/sync893.py
new file mode 100644
index 00000000..7ab55383
--- /dev/null
+++ b/pactest/tests/sync893.py
@@ -0,0 +1,20 @@
+self.description = "conflict (bug)"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.conflicts = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+sp2 = pmpkg("pkg2", "1.0-2")
+self.addpkg2db("sync", sp2)
+
+lp1 = pmpkg("pkg1")
+self.addpkg2db("local", lp1)
+
+lp2 = pmpkg("pkg2")
+self.addpkg2db("local", lp2)
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync897.py b/pactest/tests/sync897.py
new file mode 100644
index 00000000..adaaa1c2
--- /dev/null
+++ b/pactest/tests/sync897.py
@@ -0,0 +1,26 @@
+self.description = "System upgrade"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.conflicts = ["pkg2"]
+sp1.provides = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+sp2 = pmpkg("pkg2", "1.0-2")
+self.addpkg2db("sync", sp2)
+
+lp1 = pmpkg("pkg1")
+lp1.conflicts = ["pkg2"]
+self.addpkg2db("local", lp1)
+
+lp2 = pmpkg("pkg2")
+self.addpkg2db("local", lp2)
+
+lp3 = pmpkg("pkg3")
+lp3.conflicts = ["pkg1"]
+self.addpkg2db("local", lp3)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync898.py b/pactest/tests/sync898.py
new file mode 100644
index 00000000..06583217
--- /dev/null
+++ b/pactest/tests/sync898.py
@@ -0,0 +1,18 @@
+self.description = "System upgrade"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.conflicts = ["pkg2"]
+sp1.provides = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+sp2 = pmpkg("pkg2", "1.0-2")
+self.addpkg2db("sync", sp2)
+
+lp1 = pmpkg("pkg1")
+self.addpkg2db("local", lp1)
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=1")
+self.addrule("!PKG_MODIFIED=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync899.py b/pactest/tests/sync899.py
new file mode 100644
index 00000000..698c93ea
--- /dev/null
+++ b/pactest/tests/sync899.py
@@ -0,0 +1,18 @@
+self.description = "System upgrade"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.conflicts = ["pkg2"]
+sp1.provides = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+lp1 = pmpkg("pkg1")
+self.addpkg2db("local", lp1)
+
+lp2 = pmpkg("pkg2")
+self.addpkg2db("local", lp2)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/sync990.py b/pactest/tests/sync990.py
new file mode 100644
index 00000000..1bf532c5
--- /dev/null
+++ b/pactest/tests/sync990.py
@@ -0,0 +1,20 @@
+self.description = "Sync a package pulling a dependency conflicting with a target"
+
+sp1 = pmpkg("pkg1")
+sp1.depends = ["pkg3"]
+
+sp2 = pmpkg("pkg2")
+
+sp3 = pmpkg("pkg3")
+sp3.conflicts = ["pkg2"]
+sp3.provides = ["pkg2"]
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p)
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("PKG_EXIST=pkg2")
+self.addrule("!PKG_EXIST=pkg3")
diff --git a/pactest/tests/sync992.py b/pactest/tests/sync992.py
new file mode 100644
index 00000000..fc07f0cc
--- /dev/null
+++ b/pactest/tests/sync992.py
@@ -0,0 +1,23 @@
+self.description = "Sync a package pulling a conflicting dependency"
+
+sp1 = pmpkg("pkg1")
+sp1.depends = ["pkg3"]
+
+sp2 = pmpkg("pkg2")
+
+sp3 = pmpkg("pkg3")
+sp3.conflicts = ["pkg2"]
+sp3.provides = ["pkg2"]
+
+for p in sp1, sp2, sp3:
+ self.addpkg2db("sync", p)
+
+lp1 = pmpkg("pkg2", "0.1-1")
+self.addpkg2db("local", lp1)
+
+self.args = "-S pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
+self.addrule("PKG_EXIST=pkg3")
diff --git a/pactest/tests/sync999.py b/pactest/tests/sync999.py
new file mode 100644
index 00000000..69089fa1
--- /dev/null
+++ b/pactest/tests/sync999.py
@@ -0,0 +1,21 @@
+self.description = "System upgrade"
+
+sp1 = pmpkg("pkg1", "1.0-2")
+sp1.conflicts = ["pkg2"]
+sp1.provides = ["pkg2"]
+self.addpkg2db("sync", sp1);
+
+sp2 = pmpkg("pkg2", "1.0-2")
+self.addpkg2db("sync", sp2)
+
+lp1 = pmpkg("pkg1")
+self.addpkg2db("local", lp1)
+
+lp2 = pmpkg("pkg2")
+self.addpkg2db("local", lp2)
+
+self.args = "-Su"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
diff --git a/pactest/tests/upgrade001.py b/pactest/tests/upgrade001.py
new file mode 100644
index 00000000..2a9538e0
--- /dev/null
+++ b/pactest/tests/upgrade001.py
@@ -0,0 +1,18 @@
+self.description = "Upgrade a package (newer version)"
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_MODIFIED=dummy")
+self.addrule("PKG_VERSION=dummy|1.0-2")
+for f in lp.files:
+ self.addrule("FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/upgrade002.py b/pactest/tests/upgrade002.py
new file mode 100644
index 00000000..d561d894
--- /dev/null
+++ b/pactest/tests/upgrade002.py
@@ -0,0 +1,18 @@
+self.description = "Upgrade a package (same version)"
+
+lp = pmpkg("dummy")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_MODIFIED=dummy")
+self.addrule("PKG_VERSION=dummy|1.0-1")
+for f in lp.files:
+ self.addrule("FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/upgrade003.py b/pactest/tests/upgrade003.py
new file mode 100644
index 00000000..dac21e59
--- /dev/null
+++ b/pactest/tests/upgrade003.py
@@ -0,0 +1,18 @@
+self.description = "Upgrade a package (lesser version)"
+
+lp = pmpkg("dummy", "1.0-2")
+lp.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_MODIFIED=dummy")
+self.addrule("PKG_VERSION=dummy|1.0-1")
+for f in lp.files:
+ self.addrule("FILE_MODIFIED=%s" % f)
diff --git a/pactest/tests/upgrade004.py b/pactest/tests/upgrade004.py
new file mode 100644
index 00000000..31daf915
--- /dev/null
+++ b/pactest/tests/upgrade004.py
@@ -0,0 +1,12 @@
+self.description = "Upgrade a package (not installed)"
+
+p = pmpkg("dummy")
+p.files = ["bin/dummy",
+ "usr/man/man1/dummy.1"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_EXIST=dummy")
+for f in p.files:
+ self.addrule("FILE_EXIST=%s" % f)
diff --git a/pactest/tests/upgrade010.py b/pactest/tests/upgrade010.py
new file mode 100644
index 00000000..04f3d816
--- /dev/null
+++ b/pactest/tests/upgrade010.py
@@ -0,0 +1,17 @@
+self.description = "Upgrade a package, with a file in NoUpgrade"
+
+lp = pmpkg("dummy")
+lp.files = ["etc/dummy.conf"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.option["noupgrade"] = ["etc/dummy.conf"]
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+self.addrule("!FILE_MODIFIED=etc/dummy.conf")
+self.addrule("FILE_PACNEW=etc/dummy.conf")
diff --git a/pactest/tests/upgrade020.py b/pactest/tests/upgrade020.py
new file mode 100644
index 00000000..bab00381
--- /dev/null
+++ b/pactest/tests/upgrade020.py
@@ -0,0 +1,17 @@
+self.description = "Upgrade a package, with a file in 'backup' (new modified)"
+
+lp = pmpkg("dummy")
+lp.files = ["etc/dummy.conf"]
+lp.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["etc/dummy.conf*"]
+p.backup = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+self.addrule("FILE_MODIFIED=etc/dummy.conf")
+self.addrule("!FILE_PACNEW=etc/dummy.conf")
diff --git a/pactest/tests/upgrade021.py b/pactest/tests/upgrade021.py
new file mode 100644
index 00000000..1ac0ceb5
--- /dev/null
+++ b/pactest/tests/upgrade021.py
@@ -0,0 +1,17 @@
+self.description = "Upgrade a package, with a file in 'backup' (local modified, new unchanged)"
+
+lp = pmpkg("dummy")
+lp.files = ["etc/dummy.conf*"]
+lp.backup = ["etc/dummy.conf"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["etc/dummy.conf"]
+p.backup = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+self.addrule("!FILE_MODIFIED=etc/dummy.conf")
+self.addrule("!FILE_PACNEW=etc/dummy.conf")
diff --git a/pactest/tests/upgrade022.py b/pactest/tests/upgrade022.py
new file mode 100644
index 00000000..6bf12c15
--- /dev/null
+++ b/pactest/tests/upgrade022.py
@@ -0,0 +1,17 @@
+self.description = "Upgrade a package, with a file in 'backup' (local and new modified)"
+
+lp = pmpkg("dummy")
+lp.files = ["etc/dummy.conf"]
+lp.backup = ["etc/dummy.conf*"]
+self.addpkg2db("local", lp)
+
+p = pmpkg("dummy", "1.0-2")
+p.files = ["etc/dummy.conf**"]
+p.backup = ["etc/dummy.conf"]
+self.addpkg(p)
+
+self.args = "-U %s" % p.filename()
+
+self.addrule("PKG_VERSION=dummy|1.0-2")
+self.addrule("!FILE_MODIFIED=etc/dummy.conf")
+self.addrule("FILE_PACNEW=etc/dummy.conf")
diff --git a/pactest/tests/upgrade030.py b/pactest/tests/upgrade030.py
new file mode 100644
index 00000000..1082c32b
--- /dev/null
+++ b/pactest/tests/upgrade030.py
@@ -0,0 +1,22 @@
+self.description = "Upgrade packages with various reasons"
+
+lp1 = pmpkg("pkg1")
+lp1.reason = 0
+lp2 = pmpkg("pkg2")
+lp2.reason = 1
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+p1 = pmpkg("pkg1", "1.0-2")
+p2 = pmpkg("pkg2", "1.0-2")
+
+for p in p1, p2:
+ self.addpkg(p)
+
+self.args = "-U %s" % " ".join([p.filename() for p in p1, p2])
+#self.args = "-Qi %s" % " ".join([p.name for p in lp1, lp2])
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_REASON=pkg1|0")
+self.addrule("PKG_REASON=pkg2|1")
diff --git a/pactest/tests/upgrade040.py b/pactest/tests/upgrade040.py
new file mode 100644
index 00000000..6946882e
--- /dev/null
+++ b/pactest/tests/upgrade040.py
@@ -0,0 +1,25 @@
+self.description = "file relocation 1"
+
+lp1 = pmpkg("dummy")
+lp1.files = ["bin/dummy",
+ "usr/share/file"]
+
+lp2 = pmpkg("foobar")
+lp2.files = ["bin/foobar"]
+
+for p in lp1, lp2:
+ self.addpkg2db("local", p)
+
+p1 = pmpkg("dummy")
+p1.files = ["bin/dummy"]
+
+p2 = pmpkg("foobar")
+p2.files = ["bin/foobar",
+ "usr/share/file"]
+
+for p in p1, p2:
+ self.addpkg(p)
+
+self.args = "-U %s" % " ".join([p.filename() for p in p1, p2])
+
+self.addrule("PACMAN_RETCODE=0")
diff --git a/pactest/util.py b/pactest/util.py
new file mode 100755
index 00000000..d7bb8c8e
--- /dev/null
+++ b/pactest/util.py
@@ -0,0 +1,259 @@
+#! /usr/bin/python
+#
+# Copyright (c) 2006 by Aurelien Foret <orelien@chez.com>
+#
+# 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.
+
+
+import sys
+import os
+import md5
+import stat
+
+
+# ALPM
+PM_ROOT = "/"
+PM_DBPATH = "var/lib/pacman"
+PM_CACHEDIR = "var/cache/pacman/pkg"
+PM_LOCK = "/tmp/pacman.lck"
+PM_EXT_PKG = ".pkg.tar.gz"
+PM_EXT_DB = ".db.tar.gz"
+PM_PACNEW = ".pacnew"
+PM_PACORIG = ".pacorig"
+PM_PACSAVE = ".pacsave"
+
+# Pacman
+PACCONF = "etc/pacman.conf"
+
+# Pactest
+TMPDIR = "tmp"
+SYNCREPO = "var/pub"
+LOGFILE = "var/log/pactest.log"
+
+
+verbose = 0
+debug = 1
+
+
+def err(msg):
+ print "error: " + msg
+ sys.exit(1)
+
+def vprint(msg):
+ if verbose:
+ print msg
+
+def dbg(msg):
+ if debug:
+ print msg
+
+
+#
+# Methods to generate files
+#
+
+def getfilename(name):
+ """
+ """
+ filename = ""
+ link = ""
+ if not name.find(" -> ") == -1:
+ filename, link = name.split(" -> ")
+ elif name[-1] == "*":
+ filename = name.rstrip("*")
+ else:
+ filename = name
+ return filename
+
+def mkfile(name, data = ""):
+ """
+ """
+
+ isaltered = 0
+ isdir = 0
+ islink = 0
+ link = ""
+ filename = ""
+
+ if not name.find(" -> ") == -1:
+ islink = 1
+ filename, link = name.split(" -> ")
+ elif name[-1] == "*":
+ isaltered = 1
+ filename = name.rstrip("*")
+ else:
+ filename = name
+ if name[-1] == "/":
+ isdir = 1
+
+ if isdir:
+ path = filename
+ else:
+ path = os.path.dirname(filename)
+ try:
+ if path and not os.path.isdir(path):
+ os.makedirs(path, 0755)
+ except:
+ error("mkfile: could not create directory hierarchy '%s'" % path)
+
+ if isdir:
+ return
+ if islink:
+ curdir = os.getcwd()
+ os.chdir(path)
+ os.symlink(link, os.path.basename(filename))
+ os.chdir(curdir)
+ else:
+ fd = file(filename, "w")
+ if data:
+ fd.write(data)
+ if data[-1] != "\n":
+ fd.write("\n")
+ fd.close()
+
+def mkdescfile(filename, pkg):
+ """
+ """
+
+ data = []
+
+ # desc
+ #data.append("pkgname = %s" % pkg.name)
+ #data.append("pkgver = %s" % pkg.version)
+ if pkg.desc:
+ data.append("pkgdesc = %s" % pkg.desc)
+ if pkg.url:
+ data.append("url = %s" % pkg.url)
+ if pkg.builddate:
+ data.append("builddate = %s" % pkg.builddate)
+ if pkg.packager:
+ data.append("packager = %s" % pkg.packager)
+ if pkg.size:
+ data.append("size = %s" % pkg.size)
+ if pkg.arch:
+ data.append("arch = %s" % pkg.arch)
+ for i in pkg.groups:
+ data.append("group = %s" % i)
+ for i in pkg.license:
+ data.append("license = %s" % i)
+ if pkg.md5sum:
+ data.append("md5sum = %s" % pkg.md5sum)
+
+ # depends
+ for i in pkg.replaces:
+ data.append("replaces = %s" % i)
+ for i in pkg.depends:
+ data.append("depend = %s" % i)
+ for i in pkg.conflicts:
+ data.append("conflict = %s" % i)
+ for i in pkg.provides:
+ data.append("provides = %s" % i)
+ for i in pkg.backup:
+ data.append("backup = %s" % i)
+ if pkg.force:
+ data.append("force = 1")
+
+ mkfile(filename, "\n".join(data))
+
+def mkinstallfile(filename, install):
+ """
+ """
+ data = []
+ for key, value in install.iteritems():
+ if value:
+ data.append("%s() {\n%s\n}" % (key, value))
+
+ mkfile(filename, "\n".join(data))
+
+def mkcfgfile(filename, root, option, db):
+ """
+ """
+ # Options
+ data = ["[options]"]
+ for key, value in option.iteritems():
+ data.extend(["%s = %s" % (key, j) for j in value])
+
+ # Repositories
+ data.extend(["[%s]\n" \
+ "server = file://%s\n" \
+ % (value.treename, os.path.join(root, SYNCREPO, value.treename)) \
+ for key, value in db.iteritems() if not key == "local"])
+
+ mkfile(os.path.join(root, filename), "\n".join(data))
+
+
+#
+# MD5 helpers
+#
+
+def getmd5sum(filename):
+ """
+ """
+ fd = open(filename, "rb")
+ checksum = md5.new()
+ while 1:
+ block = fd.read(1048576)
+ if not block:
+ break
+ checksum.update(block)
+ fd.close()
+ digest = checksum.digest()
+ return "%02x"*len(digest) % tuple(map(ord, digest))
+
+def mkmd5sum(data):
+ """
+ """
+ checksum = md5.new()
+ checksum.update("%s\n" % data)
+ digest = checksum.digest()
+ return "%02x"*len(digest) % tuple(map(ord, digest))
+
+
+#
+# Mtime helpers
+#
+
+def getmtime(filename):
+ """
+ """
+ st = os.stat(filename)
+ return st[stat.ST_ATIME], st[stat.ST_MTIME], st[stat.ST_CTIME]
+
+def diffmtime(mt1, mt2):
+ """ORE: TBD
+ """
+ return not mt1 == mt2
+
+
+#
+# Miscellaneous
+#
+
+def grep(filename, pattern):
+ found = 0
+ fd = file(filename, "r")
+ while 1 and not found:
+ line = fd.readline()
+ if not line:
+ break
+ if line.find(pattern) != -1:
+ found = 1
+ fd.close()
+ return found
+
+
+if __name__ == "__main__":
+ pass